Package screenlets :: Package plugins :: Module Convert

[hide private]
[frames] | no frames]

# Source Code for Module screenlets.plugins.Convert

      1  # The base Converter class. Not to be used directly.
      2  # For a commented example on how to write general converter modules, see the
      3  # file BaseConverter.py. But first, please, read below if a RatioConverter isn't
      4  # well suitable for your desired task.
      5
      6 -class Converter:
      7          """The base class for the converters. The converters look after maintaining
      8          the list of currently shown values - initialising, accepting keyboard input,
      9          etc.."""
     10
     11          # the name of the class
     12          __name__ = 'Converter'
     13          # a short description to be used for selecting the converter
     14          __title__ = 'Base'
     15
     16          # the number of fields shown on the display between 2 and 4
     17          num_fields = 0
     18          # field captions up to 4 chars long
     19          field_names = []
     20          # zero-based index of field having input focus
     21          active_field = 0
     22          # the list of currently shown 'values'. They should always be strings.
     23          values = []
     24          # replace: True = replace all the input by next keypress
     25          replace = True
     26
     27          # initialize the list of values and fill it with defaults (strings!).
     28          # Override this if you don't want then be '0's.
     29 -        def __init__(self):
     30                  self.values = []
     31                  for i in range(self.num_fields):
     32                          self.values.append('0')
     33                  self.active_field = 0
     34
     35 -        def on_key_press(self, key):
     36                  """If accepted, appends the pressed key to the value in the currently
     37                  active field."""
     38                  translate_dict = {'comma': '.', 'period': '.', 'Add': '+', 'plus': '+',
     39                                  'Subtract': '-', 'minus': '-'}
     40                  if key[:3] == 'KP_':
     41                          key = key[3:]           # keys from numerical keypad come as digits
     42                  if translate_dict.has_key(key):
     43                          key = translate_dict[key]
     44                  if key == 'BackSpace':  # try to guess :-)
     45                          self.values[self.active_field] = self.values[self.active_field][:-1]
     46                  elif key == 'Escape':   # clean the field
     47                          self.values[self.active_field] = ''
     48                  elif key == 'Down':             # move field focus
     49                          if self.active_field < self.num_fields - 1:
     50                                  self.active_field += 1
     51                                  self.replace = True     # after a change, don't append next pressed
     52                                  return True                     # key but start over
     53                          return False
     54                  elif key == 'Up':
     55                          if self.active_field > 0:
     56                                  self.active_field -= 1
     57                                  self.replace = True
     58                                  return True
     59                          return False
     60                  elif key == 'Tab':
     61                          self.active_field += 1
     62                          self.active_field %= self.num_fields
     63                          self.replace = True
     64                          return True
     65                  else:           # leave other keys unchanged
     66                          if not self.filter_key(key):
     67                                  return False
     68                          if self.replace:
     69                                  self.values[self.active_field] = key
     70                          # limit input length to 10 characters
     71                          elif len(self.values[self.active_field]) < 10:
     72                                  self.values[self.active_field] = \
     73                                          self.values[self.active_field] + key
     74                          self.replace = False
     75                  self.neaten_input()
     76                  self.convert()
     77                  return True
     78
     79          # You can use this function instead of str or fixed-width % - it leaves
     80          # 3 digits after the decimal point for floats, and no decimal part if the
     81          # number is integer.
     82          # Serves good if the same converter can be used for both ints ant floats,
     83          # so that the user doesn't get '1.000' when not necessary, also for showing
     84          # '0' instead of '0.000' in all cases.
     85 -        def str(self, val):
     86                  if val == int(val):
     87                          return str(int(val))
     88                  else:
     89                          return '%.3f' % val
     90
     91          # Overridable functions:
     92
     93          # neaten_input: see the docstring
     94          # Return value: none
     95          # If you don't want such function, override it in your converter with pass.
     96          # If you override it by something more complicated, be careful not to modify
     97          # the current value so that it breaks the input.
     98 -        def neaten_input(self):
     99                  """Replaces an empty value with '0' and again removes leading '0' in
    100                  a non-empty string."""
    101                  # This version is aware of the possibility of negative input values,
    102                  # but you must allow + and - keys in filter_key and avoid an error when
    103                  # the input is '-'. See TemperatureConverter for an example.
    104                  if self.values[self.active_field] == '' or \
    105                                  self.values[self.active_field][-1] == '+':
    106                                          self.values[self.active_field] = '0'
    107                  elif self.values[self.active_field][-1] == '-':
    108                          self.values[self.active_field] = '-'
    109                  if len(self.values[self.active_field]) > 1 and \
    110                                  self.values[self.active_field][0] == '0':
    111                          self.values[self.active_field] = self.values[self.active_field][1:]
    112
    113          # filter_key: decides if pressed key can be appended to current value.
    114          # "key" is the GDK name of the key pressed by user
    115          # Return value: True - accept key, False - reject it.
    116          # There is no way to modify the value of key.
    117          # Be prepared for various "keys", e.g., 'apostrophe' must not match 'a'.
    118 -        def filter_key(self, key):
    119                  """Decides whether to accept pressed key."""
    120                  return False
    121
    122          # convert: actualizes field values to reflect input
    123          # Return value: none
    124          # Active value can be modified, but not so that it breaks the input.
    125 -        def convert(self):
    126                  """Recalculates field values after changes."""
    127                  pass
    128
    129
    130  # A subclass of Converter representing numerical converters whose field are
    131  # bound by a simple linear relation. It cares about float value input and output
    132  # and conversion, so the developer who wants to use it doesn't need to provide
    133  # any functions. Besides the __anything__, num_fields and field_names variables,
    134  # he must only provide a list describing desired ratios (see below).
    135  # Not to be used directly. See LengthConverter for a commented example.
    136
    137 -class RatioConverter(Converter):
    138          """A base class of converters having linear dependances between their
    139          fields."""
    140
    141          __name__ = 'RatioConverter'
    142          __title__ = 'Base / Ratio'
    143
    144          # The list of relative weights of the individual fields. The length of this
    145          # list must be equal to num_fields. The ratio between two fields will be
    146          # inverse to the ratio of corresponding entries in this list. For example,
    147          # see LengthConverter. One may ask whether not to better use relative
    148          # ratios, i.e., ratios between fields = ratio between entries, the answer is
    149          # that this approach is more intuitive. You would set 1 for some unit and
    150          # 1000 for the corresponding kilo-unit this way.
    151          # The above implies that the values in this list are relative to whichever
    152          # one you would choose, e.g., [10, 5, 1] (will be converted to float) is
    153          # equivalent to [2.0, 1.0, 0.2]. Actually, integer (exact) values are
    154          # preferred if they reflect the logic of the conversion.
    155          weights = []
    156
    157 -        def filter_key(self, key):
    158                  # Accept digits
    159                  if key.isdigit():
    160                          return True
    161                  # Also accept a decimal point if there is no one yet
    162                  elif key == '.':
    163                          return not ('.' in self.values[self.active_field])
    164                  else:
    165                          return False
    166
    167 -        def convert(self):
    168                  # A multiply-then-divide approach is used to help avoiding little
    169                  # inaccuracies arising in operations like (3/10)*100.
    170                  val = float(self.values[self.active_field]) \
    171                                  * self.weights[self.active_field]
    172                  for i in range(self.num_fields):
    173                          if i == self.active_field:
    174                                  continue
    175                          self.values[i] = self.str(val / float(self.weights[i]))
    176          # Note: if you don't like the Converter.str() function used above which
    177          # tries to always display integers as integers, you can provide your own,
    178          # e.g., def str(self, val): return '%.3f' % val
    179