1#===========================================================================
2#
3# StrConverter
4#
5#===========================================================================
6
7
8"""StrConverter module containing class StrConverter."""
9
10#===========================================================================
11# Place all imports after here.
12#
13from __future__ import (absolute_import, division, print_function,
14                        unicode_literals)
15
16import six
17from six.moves import xrange
18
19import matplotlib.units as units
20from matplotlib.cbook import iterable
21
22# Place all imports before here.
23#===========================================================================
24
25__all__ = [ 'StrConverter' ]
26
27#===========================================================================
28class StrConverter( units.ConversionInterface ):
29   """: A matplotlib converter class.  Provides matplotlib conversion
30        functionality for string data values.
31
32   Valid units for string are:
33   - 'indexed' : Values are indexed as they are specified for plotting.
34   - 'sorted'  : Values are sorted alphanumerically.
35   - 'inverted' : Values are inverted so that the first value is on top.
36   - 'sorted-inverted' :  A combination of 'sorted' and 'inverted'
37   """
38
39   #------------------------------------------------------------------------
40   @staticmethod
41   def axisinfo( unit, axis ):
42      """: Returns information on how to handle an axis that has string data.
43
44      = INPUT VARIABLES
45      - axis    The axis using this converter.
46      - unit    The units to use for a axis with string data.
47
48      = RETURN VALUE
49      - Returns a matplotlib AxisInfo data structure that contains
50        minor/major formatters, major/minor locators, and default
51        label information.
52      """
53
54      return None
55
56   #------------------------------------------------------------------------
57   @staticmethod
58   def convert( value, unit, axis ):
59      """: Convert value using unit to a float.  If value is a sequence, return
60      the converted sequence.
61
62      = INPUT VARIABLES
63      - axis    The axis using this converter.
64      - value   The value or list of values that need to be converted.
65      - unit    The units to use for a axis with Epoch data.
66
67      = RETURN VALUE
68      - Returns the value parameter converted to floats.
69      """
70
71      if ( units.ConversionInterface.is_numlike( value ) ):
72         return value
73
74      if ( value == [] ):
75         return []
76
77      # we delay loading to make matplotlib happy
78      ax = axis.axes
79      if axis is ax.get_xaxis():
80         isXAxis = True
81      else:
82         isXAxis = False
83
84      axis.get_major_ticks()
85      ticks = axis.get_ticklocs()
86      labels = axis.get_ticklabels()
87
88      labels = [ l.get_text() for l in labels if l.get_text() ]
89
90      if ( not labels ):
91         ticks = []
92         labels = []
93
94
95      if ( not iterable( value ) ):
96         value = [ value ]
97
98      newValues = []
99      for v in value:
100         if ( (v not in labels) and (v not in newValues) ):
101            newValues.append( v )
102
103      for v in newValues:
104         if ( labels ):
105            labels.append( v )
106         else:
107            labels = [ v ]
108
109      #DISABLED: This is disabled because matplotlib bar plots do not
110      #DISABLED: recalculate the unit conversion of the data values
111      #DISABLED: this is due to design and is not really a bug.
112      #DISABLED: If this gets changed, then we can activate the following
113      #DISABLED: block of code.  Note that this works for line plots.
114      #DISABLED if ( unit ):
115      #DISABLED    if ( unit.find( "sorted" ) > -1 ):
116      #DISABLED       labels.sort()
117      #DISABLED    if ( unit.find( "inverted" ) > -1 ):
118      #DISABLED       labels = labels[ ::-1 ]
119
120      # add padding (so they do not appear on the axes themselves)
121      labels = [ '' ] + labels + [ '' ]
122      ticks = list(xrange( len(labels) ))
123      ticks[0] = 0.5
124      ticks[-1] = ticks[-1] - 0.5
125
126      axis.set_ticks( ticks )
127      axis.set_ticklabels( labels )
128      # we have to do the following lines to make ax.autoscale_view work
129      loc = axis.get_major_locator()
130      loc.set_bounds( ticks[0], ticks[-1] )
131
132      if ( isXAxis ):
133         ax.set_xlim( ticks[0], ticks[-1] )
134      else:
135         ax.set_ylim( ticks[0], ticks[-1] )
136
137      result = []
138      for v in value:
139         # If v is not in labels then something went wrong with adding new
140         # labels to the list of old labels.
141         errmsg  = "This is due to a logic error in the StrConverter class.  "
142         errmsg += "Please report this error and its message in bugzilla."
143         assert ( v in labels ), errmsg
144         result.append( ticks[ labels.index(v) ] )
145
146      ax.viewLim.ignore(-1)
147      return result
148
149   #------------------------------------------------------------------------
150   @staticmethod
151   def default_units( value, axis ):
152      """: Return the default unit for value, or None.
153
154      = INPUT VARIABLES
155      - axis    The axis using this converter.
156      - value   The value or list of values that need units.
157
158      = RETURN VALUE
159      - Returns the default units to use for value.
160      Return the default unit for value, or None.
161      """
162
163      # The default behavior for string indexing.
164      return "indexed"
165