"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/input_elements/sequence.py" (2 Dec 2019, 30879 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 "sequence.py": 4.1.3_vs_5.0.0.

    1 ###############################################################################
    2 #                                                                             #
    3 # Copyright (C) 2012,2014,2019 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 """Module containing a set of special GUI elements to be used in the relax wizards."""
   24 
   25 # Python module imports.
   26 import wx
   27 import wx.lib.mixins.listctrl
   28 
   29 # relax module imports.
   30 import dep_check
   31 from graphics import fetch_icon
   32 from gui.input_elements.combo_list import Combo_list
   33 from gui.fonts import font
   34 from gui.misc import add_border
   35 from gui.string_conv import float_to_gui, gui_to_float, gui_to_int, gui_to_list, gui_to_py, gui_to_str, gui_to_tuple, int_to_gui, list_to_gui, py_to_gui, str_to_gui, tuple_to_gui
   36 from lib.check_types import is_list
   37 from lib.errors import RelaxError
   38 from status import Status; status = Status()
   39 
   40 
   41 class Sequence:
   42     """Wizard GUI element for the input of all types of Python sequence objects.
   43 
   44     The supported Python types include:
   45         - list of floats
   46         - list of integers
   47         - list of strings
   48         - tuple of floats
   49         - tuple of integers
   50         - tuple of strings
   51     """
   52 
   53     def __init__(self, name=None, default=None, parent=None, element_type='default', seq_type=None, value_type=None, dim=None, min=0, max=1000, sizer=None, titles=None, desc=None, combo_choices=None, combo_data=None, combo_list_min=None, tooltip=None, divider=None, padding=0, spacer=None, height_element=27, single_value=False, read_only=False, can_be_none=False):
   54         """Set up the element.
   55 
   56         @keyword name:              The name of the element to use in titles, etc.
   57         @type name:                 str
   58         @keyword default:           The default value of the element.
   59         @type default:              sequence object
   60         @keyword parent:            The wizard GUI element.
   61         @type parent:               wx.Panel instance
   62         @keyword element_type:      The type of GUI element to create.  If set to 'default', the wx.TextCtrl element with a button to bring up a dialog with ListCtrl will be used.  If set to 'combo_list', the special gui.components.combo_list.Combo_list element will be used.
   63         @type element_type:         str
   64         @keyword seq_type:          The type of Python sequence.  This should be one of 'list' or 'tuple'.
   65         @type seq_type:             str
   66         @keyword value_type:        The type of Python object that the value should be.  This can be one of 'float', 'int', or 'str'.
   67         @type value_type:           str
   68         @keyword dim:               The dimensions that a list or tuple must conform to.  For a 1D sequence, this can be a single value or a tuple of possible sizes.  For a 2D sequence (a numpy matrix or list of lists), this must be a tuple of the fixed dimension sizes, e.g. a 3x5 matrix should be specified as (3, 5).
   69         @type dim:                  int, tuple of int or None
   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 sizer:             The sizer to put the input field widget into.
   75         @type sizer:                wx.Sizer instance
   76         @keyword titles:            The titles of each of the elements of the fixed dimension elements.
   77         @type titles:               list of str
   78         @keyword desc:              The text description.
   79         @type desc:                 str
   80         @keyword combo_choices:     The list of choices to present to the user.  This is only used if the element_type is set to 'combo'.
   81         @type combo_choices:        list of str
   82         @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.
   83         @type combo_data:           list
   84         @keyword combo_list_min:    The minimum length for the Combo_list object.
   85         @type combo_list_min:       int or None
   86         @keyword tooltip:           The tooltip which appears on hovering over the text or input field.
   87         @type tooltip:              str
   88         @keyword divider:           The position of the divider.
   89         @type divider:              int
   90         @keyword padding:           Spacing to the left and right of the widgets.
   91         @type padding:              int
   92         @keyword spacer:            The amount of spacing to add below the field in pixels.  If None, a stretchable spacer will be used.
   93         @type spacer:               None or int
   94         @keyword height_element:    The height in pixels of the GUI element.
   95         @type height_element:       int
   96         @keyword single_value:      A flag which if True will cause single input values to be treated as single values rather than a list or tuple.
   97         @type single_value:         bool
   98         @keyword read_only:         A flag which if True means that the text of the element cannot be edited.
   99         @type read_only:            bool
  100         @keyword can_be_none:       A flag which specifies if the element is allowed to have the None value.
  101         @type can_be_none:          bool
  102         """
  103 
  104         # Store the args.
  105         self.parent = parent
  106         self.name = name
  107         self.default = default
  108         self.element_type = element_type
  109         self.seq_type = seq_type
  110         self.value_type = value_type
  111         self.dim = dim
  112         self.min = min
  113         self.max = max
  114         self.titles = titles
  115         self.single_value = single_value
  116         self.can_be_none = can_be_none
  117 
  118         # The base types.
  119         if value_type in ['float', 'num']:
  120             self.convert_from_gui = gui_to_float
  121             self.convert_to_gui =   float_to_gui
  122         elif value_type == 'int':
  123             self.convert_from_gui = gui_to_int
  124             self.convert_to_gui =   int_to_gui
  125         elif value_type == 'str':
  126             self.convert_from_gui = gui_to_str
  127             self.convert_to_gui =   str_to_gui
  128         else:
  129             self.convert_from_gui = gui_to_py
  130             self.convert_to_gui =   py_to_gui
  131 
  132         # The sequence types.
  133         if seq_type == 'list':
  134             self.convert_from_gui_seq = gui_to_list
  135             self.convert_to_gui_seq =   list_to_gui
  136         elif seq_type == 'tuple':
  137             self.convert_from_gui_seq = gui_to_tuple
  138             self.convert_to_gui_seq =   tuple_to_gui
  139         else:
  140             raise RelaxError("Unknown sequence type '%s'." % seq_type)
  141 
  142         # Initialise the default element.
  143         if self.element_type == 'default':
  144             # Translate the read_only flag if None.
  145             if read_only == None:
  146                 read_only = False
  147 
  148             # Init.
  149             sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
  150 
  151             # Left padding.
  152             sub_sizer.AddSpacer(padding)
  153 
  154             # The description.
  155             text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT)
  156             text.SetFont(font.normal)
  157             sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0)
  158 
  159             # The divider.
  160             if not divider:
  161                 raise RelaxError("The divider position has not been supplied.")
  162 
  163             # Spacing.
  164             x, y = text.GetSize()
  165             if dep_check.wx_classic:
  166                 sub_sizer.AddSpacer((divider - x, 0))
  167             else:
  168                 sub_sizer.AddSpacer(divider - x)
  169 
  170             # The input field.
  171             self._field = wx.TextCtrl(parent, -1, '')
  172             self._field.SetMinSize((50, height_element))
  173             self._field.SetFont(font.normal)
  174             sub_sizer.Add(self._field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
  175 
  176             # Read-only.
  177             if read_only:
  178                 self._field.SetEditable(False)
  179                 colour = parent.GetBackgroundColour()
  180                 self._field.SetOwnBackgroundColour(colour)
  181 
  182             # A little spacing.
  183             sub_sizer.AddSpacer(5)
  184 
  185             # The edit button.
  186             button = wx.BitmapButton(parent, -1, wx.Bitmap(fetch_icon('oxygen.actions.edit-rename', "16x16"), wx.BITMAP_TYPE_ANY))
  187             button.SetMinSize((height_element, height_element))
  188             button.SetToolTip(wx.ToolTip("Edit the values."))
  189             sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
  190             parent.Bind(wx.EVT_BUTTON, self.open_dialog, button)
  191 
  192             # Right padding.
  193             sub_sizer.AddSpacer(padding)
  194 
  195             # Add to the main sizer.
  196             sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0)
  197 
  198             # Spacing below the widget.
  199             if spacer == None:
  200                 sizer.AddStretchSpacer()
  201             else:
  202                 sizer.AddSpacer(spacer)
  203 
  204             # Tooltip.
  205             if tooltip:
  206                 text.SetToolTip(wx.ToolTip(tooltip))
  207                 self._field.SetToolTip(wx.ToolTip(tooltip))
  208 
  209             # Set the default value.
  210             if self.default is not None:
  211                 self._field.SetValue(self.convert_to_gui_seq(self.default))
  212 
  213         # Initialise the combo list input field.
  214         elif self.element_type == 'combo_list':
  215             # Translate the read_only flag if None.
  216             if read_only == None:
  217                 read_only = False
  218 
  219             # Correct the min_length argument.
  220             if combo_list_min == None:
  221                 combo_list_min = 1
  222 
  223             # Set up the Combo_list object.
  224             self._field = Combo_list(parent, sizer, desc, value_type=value_type, min_length=combo_list_min, choices=combo_choices, data=combo_data, default=default, tooltip=tooltip, read_only=read_only, can_be_none=can_be_none)
  225 
  226         # Unknown field.
  227         else:
  228             raise RelaxError("Unknown element type '%s'." % self.element_type)
  229 
  230 
  231     def Clear(self):
  232         """Special method for clearing or resetting the GUI element."""
  233 
  234         # Clear the value from a TextCtrl or ComboBox.
  235         if self.element_type in ['default', 'combo_list']:
  236             self._field.Clear()
  237 
  238 
  239     def GetValue(self):
  240         """Special method for returning the sequence values of the GUI element.
  241 
  242         @return:    The sequence of values.
  243         @rtype:     sequence type
  244         """
  245 
  246         # The value.
  247         value = self._field.GetValue()
  248 
  249         # Handle Combo_list elements.
  250         if self.element_type == 'combo_list':
  251             # Empty lists.
  252             if value == [] or value == None:
  253                 return None
  254 
  255         # Non Combo_list elements.
  256         else:
  257             # Handle single values.
  258             value_set = False
  259             if self.single_value:
  260                 try:
  261                     # Convert.
  262                     value = self.convert_from_gui(value)
  263 
  264                     # Check that the conversion was successful.
  265                     if value == None and self.can_be_none:
  266                         value_set = True
  267                     elif self.value_type == None:
  268                         value_set = True
  269                     elif self.value_type in ['float', 'num']:
  270                         if isinstance(value, int) or isinstance(value, float):
  271                             value_set = True
  272                     elif self.value_type == 'int':
  273                         if isinstance(value, int):
  274                             value_set = True
  275                     elif self.value_type == 'str':
  276                         if self.seq_type == 'list' and value[0] != '[':
  277                             value_set = True
  278                         elif self.seq_type == 'tuple' and value[0] != '(':
  279                             value_set = True
  280                 except:
  281                     pass
  282 
  283             # Convert to a sequence, handling bad user behaviour.
  284             if not value_set:
  285                 try:
  286                     value = self.convert_from_gui_seq(value)
  287 
  288                 # Set the value to None or an empty sequence.
  289                 except RelaxError:
  290                     if self.can_be_none:
  291                         value = None
  292                     elif self.seq_type == 'list':
  293                         value = []
  294                     else:
  295                         value = ()
  296 
  297                 # Handle all other errors.
  298                 except:
  299                     value = None
  300 
  301         # Nothing to do.
  302         if value == None:
  303             return None
  304 
  305         # Convert sequences to single values as needed.
  306         if self.single_value:
  307             if (isinstance(value, list) or isinstance(value, tuple)) and len(value) == 1:
  308                 value = value[0]
  309 
  310         # Convert single values to sequences as needed.
  311         elif value != None:
  312             if self.seq_type == 'list' and not isinstance(value, list):
  313                 value = [value]
  314             elif self.seq_type == 'tuple' and not isinstance(value, tuple):
  315                 value = (value,)
  316 
  317         # Handle empty list and tuple values.
  318         if not self.single_value and len(value) == 0:
  319             return None
  320 
  321         # Return the value.
  322         return value
  323 
  324 
  325     def SetValue(self, value=None, index=None):
  326         """Special method for setting the value of the GUI element.
  327 
  328         @keyword value: The value to set.
  329         @type value:    value or list of values
  330         @keyword index: The index of the value to set, if the full list is not given.
  331         @type index:    int or None
  332         """
  333 
  334         # The ComboBox list.
  335         if self.element_type == 'combo_list':
  336             self._field.SetValue(value=value, index=index)
  337 
  338         # The other elements.
  339         else:
  340             # Handle single values.
  341             if self.single_value and isinstance(value, list) and len(value) == 1:
  342                 value = value[0]
  343 
  344             # Convert and set the value.
  345             self._field.SetValue(self.convert_to_gui_seq(value))
  346 
  347 
  348     def UpdateChoices(self, combo_choices=None, combo_data=None, combo_default=None):
  349         """Special wizard method for updating the list of choices in a ComboBox type element.
  350 
  351         @keyword combo_choices: The list of choices to present to the user.  This is only used if the element_type is set to 'combo_list'.
  352         @type combo_choices:    list of str
  353         @keyword combo_data:    The data returned by a call to GetValue().  This is only used if the element_type is set to 'combo_list'.  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.
  354         @type combo_data:       list
  355         @keyword combo_default: The default value of the ComboBox.  This is only used if the element_type is set to 'combo_list'.
  356         @type combo_default:    str or None
  357         """
  358 
  359         # The ComboBox list.
  360         if self.element_type == 'combo_list':
  361             self._field.UpdateChoices(combo_choices=combo_choices, combo_data=combo_data, combo_default=combo_default)
  362 
  363 
  364     def open_dialog(self, event):
  365         """Open a special dialog for inputting a list of text values.
  366 
  367         @param event:   The wx event.
  368         @type event:    wx event
  369         """
  370 
  371         # Show the window.
  372         self.selection_win_show()
  373 
  374         # Extract the data from the selection window once closed.
  375         self.selection_win_data()
  376 
  377         # Destroy the window.
  378         self.sel_win.Destroy()
  379         del self.sel_win
  380 
  381 
  382     def selection_win_data(self):
  383         """Extract the data from the selection window."""
  384 
  385         # Get the value.
  386         value = self.sel_win.GetValue()
  387 
  388         # No sequence data.
  389         if value == None or not len(value):
  390             self.Clear()
  391 
  392         # Set the values.
  393         else:
  394             self.SetValue(value)
  395 
  396 
  397     def selection_win_show(self):
  398         """Show the selection window."""
  399 
  400         # Destroy any pre-existing sequence window.
  401         if hasattr(self, 'sel_win'):
  402             self.sel_win.Destroy()
  403             del self.sel_win
  404 
  405         # Initialise the model selection window.
  406         self.sel_win = Sequence_window(parent=self.parent, name=self.name, seq_type=self.seq_type, value_type=self.value_type, titles=self.titles, dim=self.dim)
  407 
  408         # Set the model selector window selections.
  409         self.sel_win.SetValue(self.GetValue())
  410 
  411         # Show the model selector window.
  412         if status.show_gui:
  413             self.sel_win.ShowModal()
  414             self.sel_win.Close()
  415 
  416 
  417 
  418 class Sequence_list_ctrl(wx.ListCtrl, wx.lib.mixins.listctrl.TextEditMixin, wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin):
  419     """The string list ListCtrl object."""
  420 
  421     def __init__(self, parent):
  422         """Initialise the control.
  423 
  424         @param parent:  The parent window.
  425         @type parent:   wx.Frame instance
  426         """
  427 
  428         # Execute the parent __init__() methods.
  429         wx.ListCtrl.__init__(self, parent, -1, style=wx.BORDER_SUNKEN|wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES)
  430         wx.lib.mixins.listctrl.TextEditMixin.__init__(self)
  431         wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.__init__(self)
  432 
  433         # Catch edits.
  434         self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.begin_label_edit)
  435 
  436 
  437     def begin_label_edit(self, event):
  438         """Catch edits to make the first column read only.
  439 
  440         @param event:   The wx event.
  441         @type event:    wx event
  442         """
  443 
  444         # Prevent edits in the first column.
  445         if event.m_col == 0:
  446             event.Veto()
  447 
  448         # Otherwise the user is free to edit.
  449         else:
  450             event.Skip()
  451 
  452 
  453 
  454 class Sequence_window(wx.Dialog):
  455     """The Python sequence object editor window."""
  456 
  457     # The window size.
  458     SIZE = (800, 600)
  459 
  460     # A border.
  461     BORDER = 10
  462 
  463     # Sizes.
  464     SIZE_BUTTON = (150, 33)
  465 
  466     def __init__(self, parent=None, name='', seq_type='list', value_type='str', dim=None, titles=None):
  467         """Set up the string list editor window.
  468 
  469         @keyword parent:        The parent GUI element.
  470         @type parent:           wx.Window instance or None
  471         @keyword name:          The name of the window.
  472         @type name:             str
  473         @keyword seq_type:      The type of Python sequence.  This should be one of 'list' or 'tuple'.
  474         @type seq_type:         str
  475         @keyword value_type:    The type of Python data expected in the sequence.  This should be one of 'float', 'int', or 'str'.
  476         @type value_type:       str
  477         @keyword dim:           The fixed dimension that the sequence must conform to.
  478         @type dim:              int or None
  479         @keyword titles:        The titles of each of the elements of the fixed dimension elements.
  480         @type titles:           list of str
  481         """
  482 
  483         # Store the args.
  484         self.name = name
  485         self.seq_type = seq_type
  486         self.value_type = value_type
  487         self.dim = dim
  488         self.titles = titles
  489 
  490         # The base types.
  491         if value_type in ['float', 'num']:
  492             self.convert_from_gui = gui_to_float
  493             self.convert_to_gui =   float_to_gui
  494         elif value_type == 'int':
  495             self.convert_from_gui = gui_to_int
  496             self.convert_to_gui =   int_to_gui
  497         elif value_type == 'str':
  498             self.convert_from_gui = gui_to_str
  499             self.convert_to_gui =   str_to_gui
  500         else:
  501             raise RelaxError("Unknown base data type '%s'." % value_type)
  502 
  503         # Variable length.
  504         if not hasattr(self, 'variable_length'):
  505             self.variable_length = False
  506             self.offset = 0
  507             if dim == None:
  508                 self.variable_length = True
  509                 self.offset = 1
  510 
  511         # The title of the dialog.
  512         title = "Edit the %s values." % name
  513 
  514         # Set up the dialog.
  515         wx.Dialog.__init__(self, parent, id=-1, title=title)
  516 
  517         # Initialise some values
  518         self.width = self.SIZE[0] - 2*self.BORDER
  519 
  520         # Set the frame properties.
  521         self.SetSize(self.SIZE)
  522         if not dep_check.wx_classic and status.show_gui:
  523             self.Centre()
  524         self.SetFont(font.normal)
  525 
  526         # The main box sizer.
  527         main_sizer = wx.BoxSizer(wx.VERTICAL)
  528 
  529         # Pack the sizer into the frame.
  530         self.SetSizer(main_sizer)
  531 
  532         # Build the central sizer, with borders.
  533         sizer = add_border(main_sizer, border=self.BORDER, packing=wx.VERTICAL)
  534 
  535         # Add the list control.
  536         self.add_list(sizer)
  537 
  538         # Some spacing.
  539         sizer.AddSpacer(self.BORDER)
  540 
  541         # Add the bottom buttons.
  542         self.add_buttons(sizer)
  543 
  544         # Initialise the list of elements to a single element.
  545         if not self.sequence.GetItemCount():
  546             self.add_element()
  547 
  548 
  549     def GetValue(self):
  550         """Return the values as a sequence of values.
  551 
  552         @return:    The sequence of values.
  553         @rtype:     sequence type
  554         """
  555 
  556         # Init.
  557         values = []
  558 
  559         # Loop over the entries.
  560         for i in range(self.sequence.GetItemCount()):
  561             # Get the text.
  562             item = self.sequence.GetItem(i, col=1)
  563             text = item.GetText()
  564 
  565             # Store the text.
  566             try:
  567                 value = self.convert_from_gui(text)
  568             except:
  569                 value = None
  570             values.append(value)
  571 
  572         # Sequence conversion.
  573         if self.seq_type == 'tuple':
  574             values = tuple(values)
  575 
  576         # Check that something is set.
  577         empty = True
  578         for i in range(len(values)):
  579             if values[i] != None:
  580                 empty = False
  581                 break
  582 
  583         # Return nothing.
  584         if empty:
  585             return None
  586 
  587         # Return the sequence.
  588         return values
  589 
  590 
  591     def SetValue(self, values):
  592         """Set up the list values.
  593 
  594         @param values:  The list of values to add to the list.
  595         @type values:   list of str or None
  596         """
  597 
  598         # No value.
  599         if values == None:
  600             return
  601 
  602         # Invalid list, so do nothing.
  603         if not self.variable_length and is_list(values) and len(values) != self.dim:
  604             return
  605 
  606         # Single values.
  607         try:
  608             len(values)
  609         except TypeError:
  610             if self.seq_type == 'list':
  611                 values = [values]
  612             elif self.seq_type == 'tuple':
  613                 values = (values,)
  614 
  615         # Loop over the entries.
  616         for i in range(len(values)):
  617             # Fixed dimension sequences - set the values of the pre-created list.
  618             if not self.variable_length:
  619                 if dep_check.wx_classic:
  620                     self.sequence.SetStringItem(i, 1, self.convert_to_gui(values[i]))
  621                 else:
  622                     self.sequence.SetItem(i, 1, self.convert_to_gui(values[i]))
  623 
  624             # Variable dimension sequences - append the item to the end of the blank list.
  625             else:
  626                 # The first element already exists.
  627                 if i != 0:
  628                     # First add the index+1.
  629                     if dep_check.wx_classic:
  630                         self.sequence.InsertStringItem(i, int_to_gui(i+1))
  631                     else:
  632                         self.sequence.InsertItem(i, int_to_gui(i+1))
  633 
  634                 # Then set the value.
  635                 if dep_check.wx_classic:
  636                     self.sequence.SetStringItem(i, 1, self.convert_to_gui(values[i]))
  637                 else:
  638                     self.sequence.SetItem(i, 1, self.convert_to_gui(values[i]))
  639 
  640 
  641     def add_buttons(self, sizer):
  642         """Add the buttons to the sizer.
  643 
  644         @param sizer:   A sizer object.
  645         @type sizer:    wx.Sizer instance
  646         """
  647 
  648         # Create a horizontal layout for the buttons.
  649         button_sizer = wx.BoxSizer(wx.HORIZONTAL)
  650         sizer.Add(button_sizer, 0, wx.ALIGN_CENTER|wx.ALL, 0)
  651 
  652         # The non-fixed sequence buttons.
  653         if self.variable_length:
  654             # The add button.
  655             button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, "  Add")
  656             button.SetBitmapLabel(wx.Bitmap(fetch_icon('oxygen.actions.list-add-relax-blue', "22x22"), wx.BITMAP_TYPE_ANY))
  657             button.SetFont(font.normal)
  658             button.SetToolTip(wx.ToolTip("Add an item to the list."))
  659             button.SetMinSize(self.SIZE_BUTTON)
  660             button_sizer.Add(button, 0, wx.ADJUST_MINSIZE, 0)
  661             self.Bind(wx.EVT_BUTTON, self.add_element, button)
  662 
  663             # Spacer.
  664             button_sizer.AddSpacer(20)
  665 
  666             # The delete button.
  667             button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, "  Delete")
  668             button.SetBitmapLabel(wx.Bitmap(fetch_icon('oxygen.actions.list-remove', "22x22"), wx.BITMAP_TYPE_ANY))
  669             button.SetFont(font.normal)
  670             button.SetToolTip(wx.ToolTip("Delete the last item."))
  671             button.SetMinSize(self.SIZE_BUTTON)
  672             button_sizer.Add(button, 0, wx.ADJUST_MINSIZE, 0)
  673             self.Bind(wx.EVT_BUTTON, self.delete, button)
  674 
  675             # Spacer.
  676             button_sizer.AddSpacer(20)
  677 
  678             # The delete all button.
  679             button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, "  Delete all")
  680             button.SetBitmapLabel(wx.Bitmap(fetch_icon('oxygen.actions.edit-delete', "22x22"), wx.BITMAP_TYPE_ANY))
  681             button.SetFont(font.normal)
  682             button.SetToolTip(wx.ToolTip("Delete all items."))
  683             button.SetMinSize(self.SIZE_BUTTON)
  684             button_sizer.Add(button, 0, wx.ADJUST_MINSIZE, 0)
  685             self.Bind(wx.EVT_BUTTON, self.delete_all, button)
  686 
  687             # Spacer.
  688             button_sizer.AddSpacer(20)
  689 
  690         # The Ok button.
  691         button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, "  Ok")
  692         button.SetBitmapLabel(wx.Bitmap(fetch_icon('oxygen.actions.dialog-ok', "22x22"), wx.BITMAP_TYPE_ANY))
  693         button.SetFont(font.normal)
  694         button.SetMinSize(self.SIZE_BUTTON)
  695         button_sizer.Add(button, 0, wx.ADJUST_MINSIZE, 0)
  696         self.Bind(wx.EVT_BUTTON, self.close, button)
  697 
  698 
  699     def add_element(self, event=None):
  700         """Append a new row to the list.
  701 
  702         @keyword event: The wx event.
  703         @type event:    wx event
  704         """
  705 
  706         # The next index.
  707         next = self.sequence.GetItemCount()
  708 
  709         # Add a new row with the index at the start.
  710         if self.variable_length:
  711             if dep_check.wx_classic:
  712                 self.sequence.InsertStringItem(next, int_to_gui(next+1))
  713             else:
  714                 self.sequence.InsertItem(next, int_to_gui(next+1))
  715 
  716         # Add a new empty row.
  717         else:
  718             if dep_check.wx_classic:
  719                 self.sequence.InsertStringItem(next, str_to_gui(''))
  720             else:
  721                 self.sequence.InsertItem(next, str_to_gui(''))
  722 
  723 
  724     def add_list(self, sizer):
  725         """Set up the list control.
  726 
  727         @param sizer:   A sizer object.
  728         @type sizer:    wx.Sizer instance
  729         """
  730 
  731         # The control.
  732         self.sequence = Sequence_list_ctrl(self)
  733 
  734         # Set the column title.
  735         title = "%s%s" % (self.name[0].upper(), self.name[1:])
  736 
  737         # Add the index column.
  738         if self.titles:
  739             self.sequence.InsertColumn(0, "Title")
  740             self.sequence.SetColumnWidth(0, 200)
  741         else:
  742             self.sequence.InsertColumn(0, "Number")
  743             self.sequence.SetColumnWidth(0, 70)
  744 
  745         # Add a single column, full width.
  746         self.sequence.InsertColumn(1, title)
  747         self.sequence.SetColumnWidth(1, wx.LIST_AUTOSIZE)
  748 
  749         # Add the table to the sizer.
  750         sizer.Add(self.sequence, 1, wx.ALL|wx.EXPAND, 0)
  751 
  752         # The fixed dimension sequence - add all the rows needed.
  753         if not self.variable_length:
  754             for i in range(self.dim):
  755                 # Add a new row.
  756                 self.add_element()
  757 
  758                 # Add a title to the first column.
  759                 if self.titles:
  760                     if dep_check.wx_classic:
  761                         self.sequence.SetStringItem(i, 0, str_to_gui(self.titles[i]))
  762                     else:
  763                         self.sequence.SetItem(i, 0, str_to_gui(self.titles[i]))
  764 
  765                 # Otherwise add numbers starting from 1.
  766                 else:
  767                     if dep_check.wx_classic:
  768                         self.sequence.SetStringItem(i, 0, int_to_gui(i+1))
  769                     else:
  770                         self.sequence.SetItem(i, 0, int_to_gui(i+1))
  771 
  772 
  773     def close(self, event):
  774         """Close the window.
  775 
  776         @param event:   The wx event.
  777         @type event:    wx event
  778         """
  779 
  780         # Close the window.
  781         self.Close()
  782 
  783 
  784     def delete(self, event):
  785         """Remove the last item from the list.
  786 
  787         @param event:   The wx event.
  788         @type event:    wx event
  789         """
  790 
  791         # Delete the last item.
  792         item = self.sequence.GetItemCount()
  793         self.sequence.DeleteItem(item-1)
  794 
  795         # If the list is empty, start again with a single blank element.
  796         if not self.sequence.GetItemCount():
  797             self.add_element()
  798 
  799 
  800     def delete_all(self, event):
  801         """Remove all items from the list.
  802 
  803         @param event:   The wx event.
  804         @type event:    wx event
  805         """
  806 
  807         # Delete.
  808         self.sequence.DeleteAllItems()
  809 
  810         # Start again with a single blank element.
  811         self.add_element()