"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/input_elements/combo_list.py" (2 Dec 2019, 18064 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. For more information about "combo_list.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.3_vs_5.0.0.

    1 ###############################################################################
    2 #                                                                             #
    3 # Copyright (C) 2011-2012,2016 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 """The combo list GUI element."""
   24 
   25 # Python module imports.
   26 from copy import deepcopy
   27 import wx
   28 
   29 # relax module imports.
   30 import dep_check
   31 from graphics import fetch_icon
   32 from gui.string_conv import float_to_gui, gui_to_float, gui_to_int, gui_to_str, int_to_gui, str_to_gui
   33 from lib.errors import RelaxError
   34 
   35 
   36 class Combo_list:
   37     """The combo list GUI element."""
   38 
   39     def __init__(self, parent, sizer, desc, value_type=None, n=1, min_length=1, choices=None, data=None, default=None, evt_fn=None, tooltip=None, divider=None, padding=0, spacer=None, read_only=True, can_be_none=False):
   40         """Build the combo box list widget for a list of list selections.
   41 
   42         @param parent:          The parent GUI element.
   43         @type parent:           wx object instance
   44         @param sizer:           The sizer to put the combo box widget into.
   45         @type sizer:            wx.Sizer instance
   46         @param desc:            The text description.
   47         @type desc:             str
   48         @keyword value_type:    The type of Python object that the value should be.  This can be one of 'float', 'int', or 'str'.
   49         @type value_type:       str
   50         @keyword n:             The number of initial entries.
   51         @type n:                int
   52         @keyword min_length:    The minimum length for the Combo_list object.
   53         @type min_length:       int
   54         @keyword choices:       The list of choices (all combo boxes will have the same list).
   55         @type choices:          list of str
   56         @keyword 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 choices list.  If not supplied, the choices list will be used for the returned data.
   57         @type data:             list
   58         @keyword default:       The default value of the ComboBox.  This is only used if the element_type is set to 'combo'.
   59         @type default:          str or None
   60         @keyword evt_fn:        The event handling function.
   61         @type evt_fn:           func
   62         @keyword tooltip:       The tooltip which appears on hovering over the text or input field.
   63         @type tooltip:          str
   64         @keyword divider:       The optional position of the divider.  If None, the parent class variable _div_left will be used if present.
   65         @type divider:          None or int
   66         @keyword padding:       Spacing to the left and right of the widgets.
   67         @type padding:          int
   68         @keyword spacer:        The amount of spacing to add below the field in pixels.  If None, a stretchable spacer will be used.
   69         @type spacer:           None or int
   70         @keyword read_only:     A flag which if True means that text cannot be typed into the combo box widget.
   71         @type read_only:        bool
   72         @keyword can_be_none:   A flag which specifies if the element is allowed to have the None value.
   73         @type can_be_none:      bool
   74         """
   75 
   76         # Store some args.
   77         self._parent = parent
   78         self._sizer = sizer
   79         self._desc = desc
   80         self._choices = choices
   81         self._data = data
   82         self._default = default
   83         self._evt_fn = evt_fn
   84         self._tooltip = tooltip
   85         self._padding = padding
   86         self._read_only = read_only
   87         self._can_be_none = can_be_none
   88         self._min_length = min_length
   89 
   90         # Set the data if needed.
   91         if self._data == None:
   92             self._data = deepcopy(self._choices)
   93 
   94         # The value types.
   95         if value_type in ['float', 'num']:
   96             self.convert_from_gui = gui_to_float
   97             self.convert_to_gui =   float_to_gui
   98             self.type_string = 'float'
   99         elif value_type == 'int':
  100             self.convert_from_gui = gui_to_int
  101             self.convert_to_gui =   int_to_gui
  102             self.type_string = 'integer'
  103         elif value_type == 'str':
  104             self.convert_from_gui = gui_to_str
  105             self.convert_to_gui =   str_to_gui
  106             self.type_string = 'string'
  107         else:
  108             raise RelaxError("Unknown value type '%s'." % value_type)
  109 
  110         # Init.
  111         self._main_sizer = wx.BoxSizer(wx.VERTICAL)
  112         self._combo_boxes = []
  113         self._sub_sizers = []
  114 
  115         # Set the initial size, if needed.
  116         if n == None:
  117             n = 1
  118 
  119         # The divider.
  120         if not divider:
  121             self._divider = self._parent._div_left
  122         else:
  123             self._divider = divider
  124 
  125         # Build the first rows.
  126         if n < min_length:
  127             n = min_length
  128         for i in range(n):
  129             self._build_row()
  130 
  131         # Add the main sizer.
  132         self._sizer.Add(self._main_sizer, 0, wx.EXPAND|wx.ALL, 0)
  133 
  134         # Spacing below the widget.
  135         if spacer == None:
  136             self._sizer.AddStretchSpacer()
  137         else:
  138             self._sizer.AddSpacer(spacer)
  139 
  140 
  141     def _add(self, event):
  142         """Add a new combo box.
  143 
  144         @param event:   The wx event.
  145         @type event:    wx event
  146         """
  147 
  148         # Add another row.
  149         self._build_row()
  150 
  151         # Re-perform the window layout.
  152         self._parent.Layout()
  153 
  154 
  155     def _build_row(self, text=None):
  156         """Construct a row of the GUI element.
  157 
  158         @param text:    The text description of the 
  159         """
  160 
  161         # Init.
  162         sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
  163         index = len(self._combo_boxes)
  164 
  165         # Left padding.
  166         sub_sizer.AddSpacer(self._padding)
  167 
  168         # The description.
  169         if index == 0:
  170             text = wx.StaticText(self._parent, -1, self._desc, style=wx.ALIGN_LEFT)
  171             sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0)
  172 
  173             # Spacing.
  174             x, y = text.GetSize()
  175             if dep_check.wx_classic:
  176                 sub_sizer.AddSpacer((self._divider - x, 0))
  177             else:
  178                 sub_sizer.AddSpacer(self._divider - x)
  179 
  180         # No description for other rows, so add a blank space.
  181         else:
  182             if dep_check.wx_classic:
  183                 sub_sizer.AddSpacer((self._divider, 0))
  184             else:
  185                 sub_sizer.AddSpacer(self._divider)
  186 
  187         # The combo box element.
  188         style = wx.CB_DROPDOWN
  189         if self._read_only:
  190             style = style | wx.CB_READONLY
  191         combo = wx.ComboBox(self._parent, -1, value='', style=style)
  192         combo.SetMinSize((50, 27))
  193         sub_sizer.Add(combo, 1, wx.ALIGN_CENTER_VERTICAL, 0)
  194         self._combo_boxes.append(combo)
  195 
  196         # Choices.
  197         if self._choices != None:
  198             # Loop over the choices and data, adding both to the end.
  199             for j in range(len(self._choices)):
  200                 self._combo_boxes[-1].Insert(self.convert_to_gui(self._choices[j]), j, self._data[j])
  201 
  202             # Set the default selection.
  203             if self._default:
  204                 # A list.
  205                 if isinstance(self._default, list):
  206                     if index < len(self._default):
  207                         self._combo_boxes[-1].SetStringSelection(self._default[index-1])
  208 
  209                 # Single value.
  210                 else:
  211                     self._combo_boxes[-1].SetStringSelection(self._default)
  212 
  213         # The add button.
  214         button = None
  215         if index == 0:
  216             button = wx.BitmapButton(self._parent, -1, wx.Bitmap(fetch_icon('oxygen.actions.list-add-relax-blue', "16x16"), wx.BITMAP_TYPE_ANY))
  217             button.SetMinSize((27, 27))
  218             sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
  219             self._parent.Bind(wx.EVT_BUTTON, self._add, button)
  220 
  221         # The delete button.
  222         elif index == self._min_length:
  223             button = wx.BitmapButton(self._parent, -1, wx.Bitmap(fetch_icon('oxygen.actions.list-remove', "16x16"), wx.BITMAP_TYPE_ANY))
  224             button.SetMinSize((27, 27))
  225             sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
  226             self._parent.Bind(wx.EVT_BUTTON, self._delete, button)
  227 
  228         # Otherwise empty spacing.
  229         else:
  230             if dep_check.wx_classic:
  231                 sub_sizer.AddSpacer((27, 0))
  232             else:
  233                 sub_sizer.AddSpacer(27)
  234 
  235         # Right padding.
  236         sub_sizer.AddSpacer(self._padding)
  237 
  238         # Add to the main sizer.
  239         self._sub_sizers.append(sub_sizer)
  240         self._main_sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0)
  241 
  242         # Bind events.
  243         if self._evt_fn:
  244             self._parent.Bind(wx.EVT_COMBOBOX, self._evt_fn, combo)
  245 
  246         # Tooltip.
  247         if self._tooltip:
  248             if index == 0:
  249                 text.SetToolTip(wx.ToolTip(self._tooltip))
  250             combo.SetToolTip(wx.ToolTip(self._tooltip))
  251             if index <= 1 and button != None:
  252                 button.SetToolTip(wx.ToolTip(self._tooltip))
  253 
  254 
  255     def _delete(self, event):
  256         """Add a new combo box.
  257 
  258         @param event:   The wx event.
  259         @type event:    wx event
  260         """
  261 
  262         # Remove the combo box element from the list.
  263         self._combo_boxes.pop()
  264 
  265         # Destroy the subsizer.
  266         sub_sizer = self._sub_sizers.pop()
  267         sub_sizer.Clear(True)
  268         self._main_sizer.Remove(sub_sizer)
  269 
  270         # Re-perform the window layout.
  271         self._parent.Layout()
  272 
  273 
  274     def GetValue(self):
  275         """Return the value represented by this GUI element.
  276 
  277         @return:    The list of choices.
  278         @rtype:     list
  279         """
  280 
  281         # Loop over the combo boxes.
  282         data = []
  283         n = 0
  284         for i in range(len(self._combo_boxes)):
  285             # Get the value.
  286             sel_index = self._combo_boxes[i].GetSelection()
  287             if sel_index == wx.NOT_FOUND:
  288                 val = None
  289             else:
  290                 val = self.convert_from_gui(self._combo_boxes[i].GetClientData(sel_index))
  291 
  292             # Manually added value by the user.
  293             if val == None:
  294                 val = self.convert_from_gui(self._combo_boxes[i].GetValue())
  295             # Nothing, so skip.
  296             if val == None:
  297                 continue
  298 
  299             # Add the value.
  300             data.append(val)
  301 
  302             # Increment the number.
  303             n += 1
  304 
  305         # Return the list.
  306         if self._min_length != None and n < self._min_length:
  307             return None
  308         else:
  309             return data
  310 
  311 
  312     def SetValue(self, value=None, index=None):
  313         """Special method for setting the value of the GUI element.
  314 
  315         @keyword value: The value to set.
  316         @type value:    value or list of values
  317         @keyword index: The index of the value to set.
  318         @type index:    int
  319         """
  320 
  321         # Single element.
  322         if not isinstance(value, list):
  323             # The index default.
  324             if index == None:
  325                 index = 0
  326 
  327             # Add elements as needed.
  328             if len(self._combo_boxes) <= index:
  329                 for i in range(len(self._combo_boxes) - index + 1):
  330                     self._add(None)
  331 
  332             # Loop until the proper client data is found.
  333             found = False
  334             for j in range(self._combo_boxes[index].GetCount()):
  335                 if self._combo_boxes[index].GetClientData(j) == value:
  336                     self._combo_boxes[index].SetSelection(j)
  337                     found = True
  338                     break
  339 
  340             # No value found.
  341             if not found:
  342                 # Invalid value.
  343                 if self._read_only:
  344                     if value != None:
  345                         raise RelaxError("The Value element is read only, cannot set the value '%s'." % value)
  346 
  347                 # Set the unknown value, and remove the selection.
  348                 else:
  349                     self._combo_boxes[index].SetSelection(wx.NOT_FOUND)
  350                     self._combo_boxes[index].SetValue(self.convert_to_gui(value))
  351 
  352         # A list of values.
  353         else:
  354             # Add elements as needed.
  355             if len(self._combo_boxes) <= len(value):
  356                 for i in range(len(value) - len(self._combo_boxes)):
  357                     self._add(None)
  358 
  359             # Loop over the list.
  360             for i in range(len(value)):
  361                 # Loop until the proper client data is found.
  362                 found = False
  363                 for j in range(self._combo_boxes[i].GetCount()):
  364                     if self._combo_boxes[i].GetClientData(j) == value[i]:
  365                         self._combo_boxes[i].SetSelection(j)
  366                         found = True
  367                         break
  368 
  369                 # Otherwise set the value.
  370                 if not found:
  371                     self._combo_boxes[i].SetValue(value[i])
  372 
  373 
  374     def UpdateChoices(self, combo_choices=None, combo_data=None, combo_default=None):
  375         """Special wizard method for updating the list of choices in a ComboBox type element.
  376 
  377         @keyword combo_choices: The list of choices to present to the user.  This is only used if the element_type is set to 'combo'.
  378         @type combo_choices:    list of str
  379         @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.
  380         @type combo_data:       list
  381         @keyword combo_default: The default value of the ComboBox.  This is only used if the element_type is set to 'combo'.
  382         @type combo_default:    str or None
  383         """
  384 
  385         # Store the values.
  386         self._choices = combo_choices
  387         self._data = combo_data
  388         self._default = combo_default
  389 
  390         # Set the data if needed.
  391         if self._data == None:
  392             self._data = deepcopy(self._choices)
  393 
  394         # Handle None in combo boxes by prepending a None element to the lists.
  395         if self._can_be_none:
  396             self._choices.insert(0, '')
  397             self._data.insert(0, None)
  398 
  399         # Loop over the combo boxes.
  400         for i in range(len(self._combo_boxes)):
  401             # Store the current selection's client data to restore at the end.
  402             sel_index = self._combo_boxes[i].GetSelection()
  403             if sel_index == wx.NOT_FOUND:
  404                 sel = None
  405             else:
  406                 sel = self._combo_boxes[i].GetClientData(sel_index)
  407 
  408             # First clear all data.
  409             self._combo_boxes[i].Clear()
  410 
  411             # Loop over the choices and data, adding both to the end.
  412             for j in range(len(self._choices)):
  413                 self._combo_boxes[i].Insert(self.convert_to_gui(self._choices[j]), j, self._data[j])
  414 
  415             # Set the default selection.
  416             if sel == None and self._default != None:
  417                 # A list.
  418                 if isinstance(self._default, list):
  419                     # Add rows as needed.
  420                     if len(self._default) > len(self._combo_boxes):
  421                         for k in range(len(self._default) - len(self._combo_boxes)):
  422                             self._add(None)
  423 
  424                     # Loop over the defaults.
  425                     for k in range(len(self._default)):
  426                         # Translate if needed.
  427                         if self._default[k] in self._choices:
  428                             string = self._default[k]
  429                         elif self._default[k] not in self._data:
  430                             string = self._default[k]
  431                         else:
  432                             string = self._choices[self._data.index(self._default[k])]
  433 
  434                         # Set the selection.
  435                         self._combo_boxes[i].SetStringSelection(string)
  436 
  437                 # Single value.
  438                 else:
  439                     # Translate if needed.
  440                     if self._default in self._choices:
  441                         string = self._default
  442                     elif self._default not in self._data:
  443                         string = self._default
  444                     else:
  445                         string = self._choices[self._data.index(self._default)]
  446 
  447                     # Set the selection.
  448                     self._combo_boxes[i].SetStringSelection(string)
  449 
  450             # Restore the selection.
  451             else:
  452                 for j in range(self._combo_boxes[i].GetCount()):
  453                     if self._combo_boxes[i].GetClientData(j) == sel:
  454                         self._combo_boxes[i].SetSelection(j)