"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/input_elements/value.py" (2 Dec 2019, 16372 Bytes) of package /linux/privat/relax-5.0.0.src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "value.py": 4.1.3_vs_5.0.0.

    1 ###############################################################################
    2 #                                                                             #
    3 # Copyright (C) 2012 Edward d'Auvergne                                        #
    4 #                                                                             #
    5 # This file is part of the program relax (http://www.nmr-relax.com).          #
    6 #                                                                             #
    7 # This program is free software: you can redistribute it and/or modify        #
    8 # it under the terms of the GNU General Public License as published by        #
    9 # the Free Software Foundation, either version 3 of the License, or           #
   10 # (at your option) any later version.                                         #
   11 #                                                                             #
   12 # This program is distributed in the hope that it will be useful,             #
   13 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
   14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
   15 # GNU General Public License for more details.                                #
   16 #                                                                             #
   17 # You should have received a copy of the GNU General Public License           #
   18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
   19 #                                                                             #
   20 ###############################################################################
   21 
   22 # Module docstring.
   23 """GUI element for the user input of values."""
   24 
   25 # Python module imports.
   26 from copy import deepcopy
   27 import wx
   28 
   29 # relax module imports.
   30 import dep_check
   31 from gui.errors import gui_raise
   32 from gui.fonts import font
   33 from gui.string_conv import float_to_gui, gui_to_float, gui_to_int, gui_to_str, int_to_gui, str_to_gui
   34 from lib.errors import RelaxError
   35 
   36 
   37 class Value:
   38     """GUI element for the input of all types of simple Python objects.
   39 
   40     The supported Python types include:
   41         - floats
   42         - integers
   43         - strings
   44     """
   45 
   46     def __init__(self, name=None, default=None, parent=None, element_type='default', value_type=None, sizer=None, desc=None, combo_choices=None, combo_data=None, min=0, max=1000, tooltip=None, divider=None, padding=0, spacer=None, height_element=27, read_only=False, can_be_none=False):
   47         """Set up the base value element.
   48 
   49         @keyword name:              The name of the element to use in titles, etc.
   50         @type name:                 str
   51         @keyword default:           The default value of the element.
   52         @type default:              float or int or str
   53         @keyword parent:            The parent GUI element.
   54         @type parent:               wx.Panel instance
   55         @keyword element_type:      The type of GUI element to create.  This can be set to:
   56                                         - 'text', a wx.TextCtrl element will be used.
   57                                         - 'combo', a wx.ComboBox element will be used.
   58                                         - 'spin', a wx.SpinCtrl element will be used.  This is only valid for integer types!
   59         @type element_type:         str
   60         @keyword value_type:        The type of Python object that the value should be.  This can be one of 'float', 'int', or 'str'.
   61         @type value_type:           str
   62         @keyword sizer:             The sizer to put the input field widget into.
   63         @type sizer:                wx.Sizer instance
   64         @keyword desc:              The text description.
   65         @type desc:                 str
   66         @keyword combo_choices:     The list of choices to present to the user.  This is only used if the element_type is set to 'combo'.
   67         @type combo_choices:        list of str
   68         @keyword combo_data:        The data returned by a call to GetValue().  This is only used if the element_type is set to 'combo'.  If supplied, it should be the same length at the combo_choices list.  If not supplied, the combo_choices list will be used for the returned data.
   69         @type combo_data:           list
   70         @keyword min:               For a SpinCtrl, the minimum value allowed.
   71         @type min:                  int
   72         @keyword max:               For a SpinCtrl, the maximum value allowed.
   73         @type max:                  int
   74         @keyword tooltip:           The tooltip which appears on hovering over the text or input field.
   75         @type tooltip:              str
   76         @keyword divider:           The position of the divider.
   77         @type divider:              int
   78         @keyword padding:           Spacing to the left and right of the widgets.
   79         @type padding:              int
   80         @keyword spacer:            The amount of spacing to add below the field in pixels.  If None, a stretchable spacer will be used.
   81         @type spacer:               None or int
   82         @keyword height_element:    The height in pixels of the GUI element.
   83         @type height_element:       int
   84         @keyword read_only:         A flag which if True means that the text of the element cannot be edited.
   85         @type read_only:            bool
   86         @keyword can_be_none:       A flag which specifies if the element is allowed to have the None value.
   87         @type can_be_none:          bool
   88         """
   89 
   90         # Set the default.
   91         if element_type == 'default':
   92             # Set the default to a SpinCtrl for integers.
   93             if value_type == 'int' and not can_be_none:
   94                 element_type = 'spin'
   95 
   96             # Set the default to a TextCtrl for all other types.
   97             else:
   98                 element_type = 'text'
   99 
  100         # Check the spinner.
  101         if element_type == "spin" and value_type != 'int':
  102             raise RelaxError("A wx.SpinCtrl element can only be used together with integers.")
  103 
  104         # Store the args.
  105         self.name = name
  106         self.default = default
  107         self.element_type = element_type
  108         self.can_be_none = can_be_none
  109         self.read_only = read_only
  110 
  111         # The value types.
  112         if value_type in ['float', 'num']:
  113             self.convert_from_gui = gui_to_float
  114             self.convert_to_gui =   float_to_gui
  115             self.type_string = 'float'
  116         elif value_type == 'int':
  117             self.convert_from_gui = gui_to_int
  118             self.convert_to_gui =   int_to_gui
  119             self.type_string = 'integer'
  120         elif value_type == 'str':
  121             self.convert_from_gui = gui_to_str
  122             self.convert_to_gui =   str_to_gui
  123             self.type_string = 'string'
  124         else:
  125             raise RelaxError("Unknown value type '%s'." % value_type)
  126 
  127         # Init.
  128         sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
  129 
  130         # Left padding.
  131         sub_sizer.AddSpacer(padding)
  132 
  133         # The description.
  134         text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT)
  135         text.SetFont(font.normal)
  136         sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0)
  137 
  138         # The divider.
  139         if not divider:
  140             raise RelaxError("The divider position has not been supplied.")
  141 
  142         # Spacing.
  143         x, y = text.GetSize()
  144         if dep_check.wx_classic:
  145             sub_sizer.AddSpacer((divider - x, 0))
  146         else:
  147             sub_sizer.AddSpacer(divider - x)
  148 
  149         # Initialise the text input field.
  150         if self.element_type == 'text':
  151             # Set up the text control.
  152             self._field = wx.TextCtrl(parent, -1, '')
  153 
  154             # Read only field.
  155             if read_only:
  156                 # Cannot edit.
  157                 self._field.SetEditable(False)
  158 
  159                 # Change the colour to the background.
  160                 colour = parent.GetBackgroundColour()
  161                 self._field.SetOwnBackgroundColour(colour)
  162 
  163             # Set the default value.
  164             if self.default != None:
  165                 self._field.SetValue(self.convert_to_gui(self.default))
  166 
  167         # Initialise the spinner input field.
  168         elif self.element_type == 'spin':
  169             # Catch limits of None, and set to the wxSpinCtrl defaults.
  170             if min == None:
  171                 min = 0
  172             if max == None:
  173                 max = 100
  174 
  175             # Set up the text control.
  176             self._field = wx.SpinCtrl(parent, -1, min=min, max=max)
  177 
  178             # Read only field (really no such thing for a spin control).
  179             if read_only:
  180                 # Change the colour to the background.
  181                 colour = parent.GetBackgroundColour()
  182                 self._field.SetOwnBackgroundColour(colour)
  183 
  184             # Set the default value.
  185             if self.default != None:
  186                 self._field.SetValue(self.default)
  187 
  188         # Initialise the combo box input field.
  189         elif self.element_type == 'combo':
  190             # The style.
  191             style = wx.CB_DROPDOWN
  192             if read_only:
  193                 style = style | wx.CB_READONLY
  194 
  195             # Set up the combo box.
  196             self._field = wx.ComboBox(parent, -1, '', style=style)
  197 
  198             # Update the choices.
  199             self.UpdateChoices(combo_choices=combo_choices, combo_data=combo_data, combo_default=default)
  200 
  201         # Unknown field.
  202         else:
  203             raise RelaxError("Unknown element type '%s'." % self.element_type)
  204 
  205         # Set up the input field.
  206         self._field.SetMinSize((50, height_element))
  207         self._field.SetFont(font.normal)
  208         sub_sizer.Add(self._field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
  209 
  210         # Right padding.
  211         sub_sizer.AddSpacer(padding)
  212 
  213         # Add to the main sizer.
  214         sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0)
  215 
  216         # Spacing below the widget.
  217         if spacer == None:
  218             sizer.AddStretchSpacer()
  219         else:
  220             sizer.AddSpacer(spacer)
  221 
  222         # Tooltip.
  223         if tooltip:
  224             text.SetToolTip(wx.ToolTip(tooltip))
  225             self._field.SetToolTip(wx.ToolTip(tooltip))
  226 
  227 
  228     def Clear(self):
  229         """Special method for clearing or resetting the GUI element."""
  230 
  231         # Clear the value from a TextCtrl.
  232         if self.element_type == 'text':
  233             self._field.Clear()
  234 
  235         # Clear the value from a ComboBox.
  236         if self.element_type == 'combo':
  237             self._field.Clear()
  238             self._field.SetValue('')
  239 
  240 
  241     def GetValue(self):
  242         """Special method for returning the value of the GUI element.
  243 
  244         @return:    The string list value.
  245         @rtype:     list of str
  246         """
  247 
  248         # Convert and return the value from a TextCtrl.
  249         if self.element_type == 'text':
  250             # The value.
  251             value = self._field.GetValue()
  252 
  253             # Convert.
  254             try:
  255                 value = self.convert_from_gui(value)
  256 
  257             # Raise a clear error for user feedback.
  258             except:
  259                 gui_raise(RelaxError("The value '%s' is not of the Python %s type." % (value, self.type_string)))
  260                 return None
  261 
  262             return value
  263 
  264         # Return the integer value from a SpinCtrl.
  265         if self.element_type == 'spin':
  266             # The value.
  267             return self._field.GetValue()
  268 
  269         # Convert and return the value from a ComboBox.
  270         if self.element_type == 'combo':
  271             # An element selected from the list.
  272             sel_index = self._field.GetSelection()
  273             if sel_index == wx.NOT_FOUND:
  274                 value = None
  275             else:
  276                 value = self.convert_from_gui(self._field.GetClientData(sel_index))
  277 
  278             # A non-list value.
  279             if value == None:
  280                 value = self.convert_from_gui(self._field.GetValue())
  281 
  282             # Return the value.
  283             return value
  284 
  285 
  286     def SetValue(self, value):
  287         """Special method for setting the value of the GUI element.
  288 
  289         @param value:   The value to set.
  290         @type value:    list of str or None
  291         """
  292 
  293         # Convert and set the value for a TextCtrl.
  294         if self.element_type == 'text':
  295             self._field.SetValue(self.convert_to_gui(value))
  296 
  297         # Set the value for a SpinCtrl.
  298         elif self.element_type == 'spin':
  299             self._field.SetValue(value)
  300 
  301         # Convert and set the value for a ComboBox.
  302         elif self.element_type == 'combo':
  303             # Loop until the proper client data is found.
  304             found = False
  305             for i in range(self._field.GetCount()):
  306                 if self._field.GetClientData(i) == value:
  307                     self._field.SetSelection(i)
  308                     found = True
  309                     break
  310 
  311             # No value found.
  312             if not found:
  313                 # Invalid value.
  314                 if self.read_only:
  315                     if value != None:
  316                         raise RelaxError("The Value element is read only, cannot set the value '%s'." % value)
  317 
  318                 # Set the unknown value, and remove the selection.
  319                 else:
  320                     self._field.SetSelection(wx.NOT_FOUND)
  321                     self._field.SetValue(self.convert_to_gui(value))
  322 
  323 
  324     def UpdateChoices(self, combo_choices=None, combo_data=None, combo_default=None):
  325         """Special wizard method for updating the list of choices in a ComboBox type element.
  326 
  327         @keyword combo_choices: The list of choices to present to the user.  This is only used if the element_type is set to 'combo'.
  328         @type combo_choices:    list of str
  329         @keyword combo_data:    The data returned by a call to GetValue().  This is only used if the element_type is set to 'combo'.  If supplied, it should be the same length at the combo_choices list.  If not supplied, the combo_choices list will be used for the returned data.
  330         @type combo_data:       list
  331         @keyword combo_default: The default value of the ComboBox.  This is only used if the element_type is set to 'combo'.
  332         @type combo_default:    str or None
  333         """
  334 
  335         # A TextCtrl?!
  336         if self.element_type == 'text':
  337             raise RelaxError("Cannot update the list of ComboBox choices as this is a TextCtrl!")
  338 
  339         # A SpinCtrl?!
  340         if self.element_type == 'spin':
  341             raise RelaxError("Cannot update the list of ComboBox choices as this is a SpinCtrl!")
  342 
  343         # Update the choices for a ComboBox.
  344         if self.element_type == 'combo':
  345             # Store the current selection's client data to restore at the end.
  346             sel_index = self._field.GetSelection()
  347             if sel_index == wx.NOT_FOUND:
  348                 sel = None
  349             else:
  350                 sel = self._field.GetClientData(sel_index)
  351 
  352             # First clear all data.
  353             self.Clear()
  354 
  355             # Set the data if needed.
  356             if combo_data == None:
  357                 combo_data = deepcopy(combo_choices)
  358 
  359             # Handle None in combo boxes by prepending a None element to the lists.
  360             if self.can_be_none:
  361                 combo_choices.insert(0, '')
  362                 combo_data.insert(0, None)
  363 
  364             # Loop over the choices and data, adding both to the end.
  365             for i in range(len(combo_choices)):
  366                 self._field.Insert(self.convert_to_gui(combo_choices[i]), i, combo_data[i])
  367 
  368             # Set the default selection.
  369             if sel == None and combo_default != None:
  370                 # Translate if needed.
  371                 if combo_default in combo_choices:
  372                     string = str_to_gui(str(combo_default))
  373                     set_sel = True
  374                 elif combo_default not in combo_data:
  375                     string = str_to_gui(str(combo_default))
  376                     set_sel = False
  377                 else:
  378                     string = combo_choices[combo_data.index(combo_default)]
  379                     set_sel = True
  380 
  381                 # Set the selection.
  382                 if set_sel:
  383                     self._field.SetStringSelection(string)
  384 
  385                 # Set the value.
  386                 else:
  387                     self._field.SetValue(string)
  388 
  389             # Restore the selection.
  390             else:
  391                 for j in range(self._field.GetCount()):
  392                     if self._field.GetClientData(j) == sel:
  393                         self._field.SetSelection(j)