"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/components/grid.py" (2 Dec 2019, 26765 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 "grid.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) 2009-2010 Michael Bieri                                       #
    4 # Copyright (C) 2009-2011 Edward d'Auvergne                                   #
    5 #                                                                             #
    6 # This file is part of the program relax (http://www.nmr-relax.com).          #
    7 #                                                                             #
    8 # This program is free software: you can redistribute it and/or modify        #
    9 # it under the terms of the GNU General Public License as published by        #
   10 # the Free Software Foundation, either version 3 of the License, or           #
   11 # (at your option) any later version.                                         #
   12 #                                                                             #
   13 # This program is distributed in the hope that it will be useful,             #
   14 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
   15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
   16 # GNU General Public License for more details.                                #
   17 #                                                                             #
   18 # You should have received a copy of the GNU General Public License           #
   19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
   20 #                                                                             #
   21 ###############################################################################
   22 
   23 # Module docstring.
   24 """Module containing the classes for GUI components involving spectral data."""
   25 
   26 # Python module imports.
   27 from re import search
   28 import wx
   29 import wx.lib.buttons
   30 
   31 # relax module imports.
   32 from data_store import Relax_data_store; ds = Relax_data_store()
   33 from graphics import fetch_icon
   34 from gui.filedialog import RelaxFileDialog
   35 from gui.fonts import font
   36 from gui.message import error_message
   37 from gui.misc import add_border
   38 from gui.string_conv import str_to_gui
   39 from status import Status; status = Status()
   40 
   41 
   42 class Delay_num_cell_editor(wx.grid.PyGridCellEditor):
   43     """Custom GridCellEditor for the number of delays grid cells.
   44 
   45     Changing these cells will update the relaxation delay times.
   46     """
   47 
   48     def __init__(self, min=None, max=None, parent=None):
   49         """Initialise the class.
   50         
   51         @keyword min:       The minimum value for wx.SpinCtrl.
   52         @type min:          None or int
   53         @keyword max:       The maximum value for wx.SpinCtrl.
   54         @type max:          None or int
   55         @keyword parent:    The parent wx object.
   56         @type parent:       wx object
   57         """
   58 
   59         # Store the args.
   60         self.min = min
   61         self.max = max
   62         self.parent = parent
   63 
   64         # Initialise the base class.
   65         super(Delay_num_cell_editor, self).__init__()
   66 
   67         # A flag for a resetting event.
   68         self.reset = False
   69 
   70 
   71     def BeginEdit(self, row, col, grid):
   72         """Start the editing.
   73 
   74         @param row:     The row index.
   75         @type row:      int
   76         @param col:     The column index.
   77         @type col:      int
   78         @param grid:    The grid GUI element.
   79         @type grid:     wx.grid.Grid instance.
   80         """
   81 
   82         # The previous value.
   83         self.prev_val = grid.GetTable().GetValue(row, col)
   84 
   85         # Set the starting value.
   86         self.cell.SetValueString(str_to_gui(self.prev_val))
   87 
   88         # Set the focus to the cell.
   89         self.cell.SetFocus()
   90 
   91 
   92     def Clone(self):
   93         """Create and return a new class instance."""
   94 
   95         # Initialise and return the class.
   96         return Delay_num_cell_editor(self.min, self.max, self.parent)
   97 
   98 
   99     def Create(self, parent, id, evtHandler):
  100         """Create the control for the cell.
  101 
  102         @param parent:      The parent wx object.
  103         @type parent:       wx object
  104         @param id:          The ID number.
  105         @type id:           int
  106         @param evtHandler:  The event handler function.
  107         @type evtHandler:   func
  108         """
  109 
  110         # Set the cell to be a spin control.
  111         self.cell = wx.SpinCtrl(parent, id, "", min=self.min, max=self.max)
  112         self.SetControl(self.cell)
  113 
  114         # Handle the event handler.
  115         if evtHandler:
  116             self.cell.PushEventHandler(evtHandler)
  117 
  118 
  119     def EndEdit(self, row, col, grid):
  120         """End the editing.
  121 
  122         @param row:     The row index.
  123         @type row:      int
  124         @param col:     The column index.
  125         @type col:      int
  126         @param grid:    The grid GUI element.
  127         @type grid:     wx.grid.Grid instance.
  128         """
  129 
  130         # A reset.
  131         if self.reset:
  132             # Reset the reset flag.
  133             self.reset = False
  134 
  135             # No starting value, so do nothing.
  136             if self.prev_val == '':
  137                 return False
  138 
  139         # The new value.
  140         value = self.cell.GetValue()
  141 
  142         # No change.
  143         if value == self.prev_val:
  144             return False
  145 
  146         # Set the value in the table (the value of zero shows nothing).
  147         if value == 0:
  148             text = ''
  149         else:
  150             text = str(value)
  151         grid.GetTable().SetValue(row, col, str_to_gui(text))
  152 
  153         # The delay cycle time.
  154         time = self.parent.delay_time.GetValue()
  155 
  156         # No times to update.
  157         if time == '':
  158             # A change occurred.
  159             return True
  160 
  161         # Update the relaxation delay time.
  162         delay_time = float(time) * float(value)
  163         grid.GetTable().SetValue(row, col-1, str_to_gui(delay_time))
  164 
  165         # A change occurred.
  166         return True
  167 
  168 
  169     def Reset(self):
  170         """Reset the cell to the previous value."""
  171 
  172         # Set the previous value.
  173         self.cell.SetValueString(str_to_gui(self.prev_val))
  174 
  175         # Set a flag for EndEdit to catch a reset.
  176         self.reset = True
  177 
  178 
  179     def StartingKey(self, event):
  180         """Catch the starting key stroke to add the value to the cell.
  181 
  182         @param event:   The wx event.
  183         @type event:    wx event
  184         """
  185 
  186         # The value.
  187         key = event.GetKeyCode()
  188 
  189         # Acceptable integers.
  190         if key >= 49 and key <= 57:
  191             # The number.
  192             num = int(chr(key))
  193 
  194             # Set the value.
  195             self.cell.SetValue(str_to_gui(num))
  196 
  197             # Set the insertion point to the end.
  198             self.cell.SetSelection(1, 1)
  199 
  200         # Skip everything else.
  201         else:
  202             event.Skip()
  203 
  204 
  205 
  206 class Grid_base:
  207     """The peak list selection class."""
  208 
  209     # Class variables.
  210     col_label_width = 40
  211     col1_width = 160
  212     col2_width = 140
  213 
  214     def __init__(self, gui=None, parent=None, subparent=None, data=None, label=None, width=688, height=300, box=None):
  215         """Build the peak list reading GUI element.
  216 
  217         @keyword gui:       The main GUI object.
  218         @type gui:          wx.Frame instance
  219         @keyword parent:    The parent GUI element that this is to be attached to (the panel object).
  220         @type parent:       wx object
  221         @keyword subparent: The subparent GUI element that this is to be attached to (the analysis object).
  222         @type subparent:    wx object
  223         @keyword data:      The data storage container.
  224         @type data:         class instance
  225         @keyword label:     The type of analysis.
  226         @type label:        str
  227         @keyword width:     The initial width of the GUI element.
  228         @type width:        int
  229         @keyword height:    The initial height of the GUI element.
  230         @type height:       int
  231         @keyword box:       The vertical box sizer to pack this GUI component into.
  232         @type box:          wx.BoxSizer instance
  233         """
  234 
  235         # Store the arguments.
  236         self.gui = gui
  237         self.parent = parent
  238         self.subparent = subparent
  239         self.data = data
  240         self.label = label
  241 
  242         # GUI variables.
  243         self.spacing = 5
  244         self.border = 5
  245 
  246         # The number of rows.
  247         self.num_rows = 50
  248 
  249         # A static box to hold all the widgets, and its sizer.
  250         stat_box = wx.StaticBox(self.parent, -1, "Peak lists")
  251         stat_box.SetFont(font.subtitle)
  252         sub_sizer = wx.StaticBoxSizer(stat_box, wx.VERTICAL)
  253 
  254         # Add the sizer to the static box and the static box to the main box.
  255         box.Add(sub_sizer, 1, wx.ALL|wx.EXPAND, 0)
  256 
  257         # Add a border.
  258         box_centre = add_border(sub_sizer, border=self.border)
  259 
  260         # Add the cycle delay time element.
  261         box_centre.AddSpacer(self.spacing)
  262         self.delay_time = self.subparent.add_text_sel_element(box_centre, self.parent, text="Single delay cycle time [s]")
  263 
  264         # Add the grid.
  265         box_centre.AddSpacer(self.spacing)
  266         self.add_grid(box_centre)
  267         box_centre.AddSpacer(self.spacing)
  268 
  269         # Bind some events.
  270         self.delay_time.Bind(wx.EVT_KEY_DOWN, self.change_delay_down)
  271         self.delay_time.Bind(wx.EVT_KEY_UP, self.change_delay_up)
  272 
  273 
  274     def resize(self, event):
  275         """Catch the resize to allow the grid to be resized.
  276 
  277         @param event:   The wx event.
  278         @type event:    wx event
  279         """
  280 
  281         # The new grid size.
  282         x, y = event.GetSize()
  283 
  284         # The expandable column width.
  285         width = x - self.col_label_width - self.col1_width - self.col2_width - 20
  286 
  287         # Set the column sizes.
  288         self.grid.SetRowLabelSize(self.col_label_width)
  289         self.grid.SetColSize(0, width)
  290         self.grid.SetColSize(1, self.col1_width)
  291         self.grid.SetColSize(2, self.col2_width)
  292 
  293         # Continue with the normal resizing.
  294         event.Skip()
  295 
  296 
  297     def add_buttons(self, sizer):
  298         """Add the buttons for peak list manipulation.
  299 
  300         @param sizer:   The sizer element to pack the buttons into.
  301         @type sizer:    wx.BoxSizer instance
  302         """
  303 
  304         # Button Sizer
  305         button_sizer = wx.BoxSizer(wx.VERTICAL)
  306 
  307         # Add peaklist button
  308         add_pkl = wx.BitmapButton(self.parent, -1, bitmap=wx.Bitmap(fetch_icon('oxygen.actions.list-add-relax-blue', "16x16"), wx.BITMAP_TYPE_ANY))
  309         add_pkl.SetMinSize((50, 50))
  310         self.gui.Bind(wx.EVT_BUTTON, self.load_peaklist, add_pkl)
  311         button_sizer.Add(add_pkl, 0, wx.ADJUST_MINSIZE, 0)
  312 
  313         # Add VD list import
  314         if self.label =='R1':
  315             add_vd = wx.Button(self.parent, -1, "+VD")
  316             add_vd.SetToolTip(wx.ToolTip("Add VD (variable delay) list to automatically fill in R1 relaxation times."))
  317             add_vd.SetMinSize((50, 50))
  318             self.gui.Bind(wx.EVT_BUTTON, self.load_delay, add_vd)
  319             button_sizer.Add(add_vd, 0, wx.ADJUST_MINSIZE, 0)
  320 
  321         # Add Vc list import
  322         if self.label =='R2':
  323             add_vc = wx.Button(self.parent, -1, "+VC")
  324             add_vc.SetToolTip(wx.ToolTip("Add VC (variable counter) list to automatically fill in R2 relaxation times."))
  325             add_vc.SetMinSize((50, 50))
  326             button_sizer.Add(add_vc, 0, wx.ADJUST_MINSIZE, 0)
  327 
  328             # Time of counter
  329             self.vc_time = wx.TextCtrl(self.parent, -1, "0")
  330             self.vc_time.SetToolTip(wx.ToolTip("Time of counter loop in seconds."))
  331             self.vc_time.SetMinSize((50, 20))
  332             self.vc_time.SetFont(wx.Font(7, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, ""))
  333             button_sizer.Add(self.vc_time, 0, 0, 0)
  334 
  335             # Action of Button
  336             self.gui.Bind(wx.EVT_BUTTON, lambda event, vc=True: self.load_delay(event, vc), add_vc)
  337 
  338         # Pack buttons
  339         sizer.Add(button_sizer, 0, 0, 0)
  340 
  341 
  342     def add_grid(self, sizer):
  343         """Add the grid for the peak list files and delay times.
  344 
  345         @param sizer:   The sizer element to pack the grid into.
  346         @type sizer:    wx.BoxSizer instance
  347         """
  348 
  349         # Grid of peak list file names and relaxation time.
  350         self.grid = wx.grid.Grid(self.parent, -1)
  351 
  352         # Create entries.
  353         self.grid.CreateGrid(self.num_rows, 3)
  354 
  355         # Create headers.
  356         self.grid.SetColLabelValue(0, "%s peak list" % self.label)
  357         self.grid.SetColLabelValue(1, "Relaxation delay [s]")
  358         self.grid.SetColLabelValue(2, "No. of cycles")
  359 
  360         # Properties.
  361         self.grid.SetDefaultCellFont(font.normal)
  362         self.grid.SetLabelFont(font.normal_bold)
  363 
  364         # Text height.
  365         height = self.delay_time.GetSize()[1]
  366 
  367         # Column properties.
  368         for i in range(self.grid.GetNumberRows()):
  369             # Set the editor for the number of cycles column.
  370             self.grid.SetCellEditor(i, 2, Delay_num_cell_editor(0, 200, self))
  371 
  372             # Row properties.
  373             self.grid.SetRowSize(i, height)
  374 
  375         # No cell resizing allowed.
  376         self.grid.EnableDragColSize(False)
  377         self.grid.EnableDragRowSize(False)
  378 
  379         # Bind some events.
  380         self.grid.GetGridWindow().Bind(wx.EVT_LEFT_DCLICK, self.event_left_dclick)
  381         self.grid.Bind(wx.EVT_KEY_DOWN, self.event_key_down)
  382         self.grid.Bind(wx.EVT_KEY_UP, self.event_key_up)
  383         self.grid.Bind(wx.EVT_SIZE, self.resize)
  384 
  385         # Add grid to sizer, with spacing.
  386         sizer.Add(self.grid, 1, wx.ALL|wx.EXPAND, 0)
  387 
  388 
  389     def change_delay_down(self, event):
  390         """Handle changes to the delay time.
  391 
  392         @param event:   The wx event.
  393         @type event:    wx event
  394         """
  395 
  396         # The key.
  397         key = event.GetKeyCode()
  398 
  399         # Get the text.
  400         text = str(self.delay_time.GetString(0, self.delay_time.GetLastPosition()))
  401 
  402         # Allowed keys.
  403         allowed = []
  404         allowed += [8]    # Backspace.
  405         if not search('\.', text):
  406             allowed += [46]    # Only one full stop.
  407         allowed += [48, 49, 50, 51, 52, 53, 54, 55, 56, 57]    # Numbers.
  408         allowed += [127]    # Delete.
  409         allowed += [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_HOME, wx.WXK_END]    # Navigation keys.
  410 
  411         # Disallowed values, so do nothing.
  412         if key not in allowed:
  413             return
  414 
  415         # Normal event handling.
  416         event.Skip()
  417 
  418 
  419     def change_delay_up(self, event):
  420         """Handle updates to the delay time.
  421 
  422         @param event:   The wx event.
  423         @type event:    wx event
  424         """
  425 
  426         # Normal event handling.
  427         event.Skip()
  428 
  429         # Update the grid.
  430         self.update_grid()
  431 
  432 
  433     def event_left_dclick(self, event):
  434         """Handle the left mouse double click.
  435 
  436         @param event:   The wx event.
  437         @type event:    wx event
  438         """
  439 
  440         # The row and column.
  441         col = self.grid.GetGridCursorCol()
  442         row = self.grid.GetGridCursorRow()
  443 
  444         # File selection.
  445         if col == 0:
  446             # The dialog.
  447             dialog = RelaxFileDialog(parent=self, style=wx.FD_OPEN)
  448 
  449             # Show the dialog and catch if no file has been selected.
  450             if status.show_gui and dialog.ShowModal() != wx.ID_OK:
  451                 # Don't do anything.
  452                 return
  453 
  454             # The files.
  455             filename = dialog.get_file()
  456 
  457             # Set the file name.
  458             self.grid.SetCellValue(row, col, str(filename))
  459 
  460         # Skip the event to allow for normal operation.
  461         event.Skip()
  462 
  463 
  464     def event_key_down(self, event):
  465         """Control what happens when a key is pressed.
  466 
  467         @param event:   The wx event.
  468         @type event:    wx event
  469         """
  470 
  471         # Clear cell contents (delete key).
  472         if event.GetKeyCode() == wx.WXK_DELETE:
  473             # Get the cell selection.
  474             cells = self.get_selection()
  475 
  476             # Debugging printout.
  477             if status.debug:
  478                 print(cells)
  479 
  480             # Loop over the cells.
  481             for cell in cells:
  482                 # Set to the empty string.
  483                 self.grid.SetCellValue(cell[0], cell[1], '')
  484 
  485             # Update the grid.
  486             self.update_grid()
  487 
  488             # Do nothing else.
  489             return
  490 
  491         # Skip the event to allow for normal operation.
  492         event.Skip()
  493 
  494 
  495     def event_key_up(self, event):
  496         """Control what happens when a key is released.
  497 
  498         @param event:   The wx event.
  499         @type event:    wx event
  500         """
  501 
  502         # Update the grid.
  503         self.update_grid()
  504 
  505         # Skip the event to allow for normal operation.
  506         event.Skip()
  507 
  508 
  509     def get_all_coordinates(self, top_left, bottom_right):
  510         """Convert the cell range into a coordinate list.
  511 
  512         @param top_left:        The top left hand coordinate.
  513         @type top_left:         list or tuple
  514         @param bottom_right:    The bottom right hand coordinate.
  515         @type bottom_right:     list or tuple
  516         @return:                The list of tuples of coordinates of all cells.
  517         @rtype:                 list of tuples
  518         """
  519 
  520         # Init.
  521         cells = []
  522 
  523         # Loop over the x-range.
  524         for x in range(top_left[0], bottom_right[0]+1):
  525             # Loop over the y-range.
  526             for y in range(top_left[1], bottom_right[1]+1):
  527                 # Append the coordinate.
  528                 cells.append((x, y))
  529 
  530         # Return the coordinates.
  531         return cells
  532 
  533 
  534     def get_selection(self):
  535         """Determine which cells are selected.
  536 
  537         There are three possibilities for cell selections in a wx.grid.  These are:
  538 
  539             - Single cell selection (this is not highlighted).
  540             - Multiple cells are selected.
  541             - Column selection.
  542             - Row selection.
  543 
  544         @return:    An array of the cell selection coordinates.
  545         @rtype:     list of tuples of int
  546         """
  547 
  548         # First try to get the coordinates.
  549         top_left = self.grid.GetSelectionBlockTopLeft()
  550         bottom_right = self.grid.GetSelectionBlockBottomRight()
  551 
  552         # Or the selection.
  553         selection = self.grid.GetSelectedCells()
  554         col = self.grid.GetSelectedCols()
  555         row = self.grid.GetSelectedRows()
  556 
  557         # Debugging printout.
  558         if status.debug:
  559             print("\nTop left: %s" % top_left)
  560             print("Bottom right: %s" % bottom_right)
  561             print("selection: %s" % selection)
  562             print("col: %s" % col)
  563             print("row: %s" % row)
  564 
  565         # Column selection.
  566         if col:
  567             # Debugging printout.
  568             if status.debug:
  569                 print("Column selection")
  570 
  571             # Return the coordinates of the selected columns.
  572             return self.get_all_coordinates([0, col[0]], [self.num_rows-1, col[-1]])
  573 
  574         # Row selection.
  575         elif row:
  576             # Debugging printout.
  577             if status.debug:
  578                 print("Row selection")
  579 
  580             # Return the coordinates of the selected rows.
  581             return self.get_all_coordinates([row[0], 0], [row[-1], 1])
  582 
  583         # Multiple block selection.
  584         elif top_left and not selection:
  585             # Debugging printout.
  586             if status.debug:
  587                 print("Multiple block selection.")
  588 
  589             # The cell list.
  590             cells = []
  591 
  592             # Loop over the n blocks.
  593             for n in range(len(top_left)):
  594                 # Append the cells.
  595                 cells = cells + self.get_all_coordinates(top_left[n], bottom_right[n])
  596 
  597             # Return the selected cells.
  598             return cells
  599 
  600         # Single cell.
  601         elif not selection and not top_left:
  602             # Debugging printout.
  603             if status.debug:
  604                 print("Single cell.")
  605 
  606             # The position.
  607             pos = self.grid.GetGridCursorRow(), self.grid.GetGridCursorCol()
  608 
  609             # Return the coordinate as a list.
  610             return [pos]
  611 
  612         # Complex selection.
  613         elif selection:
  614             # Debugging printout.
  615             if status.debug:
  616                 print("Complex selection.")
  617 
  618             # The cell list.
  619             cells = []
  620 
  621             # Loop over the n blocks.
  622             for n in range(len(top_left)):
  623                 # Append the cells.
  624                 cells = cells + self.get_all_coordinates(top_left[n], bottom_right[n])
  625 
  626             # Return the selection.
  627             return cells + selection
  628 
  629         # Unknown.
  630         else:
  631             # Debugging printout.
  632             if status.debug:
  633                 print("Should not be here.")
  634 
  635 
  636     def load_delay(self, event, vc=False):
  637         """The variable delay list loading GUI element.
  638 
  639         @param event:   The wx event.
  640         @type event:    wx event
  641         """
  642 
  643         # VD
  644 
  645         # VC time is not a number
  646         if vc:
  647             try:
  648                 vc_factor = float(self.vc_time.GetValue())
  649             except:
  650                 error_message('VC time is not a number.')
  651                 return
  652 
  653         # VD
  654         else:
  655             vc_factor = 1
  656 
  657         # The dialog.
  658         dialog = RelaxFileDialog(parent=self, style=wx.FD_OPEN)
  659 
  660         # Show the dialog and catch if no file has been selected.
  661         if status.show_gui and dialog.ShowModal() != wx.ID_OK:
  662             # Don't do anything.
  663             return
  664 
  665         # The files.
  666         filename = dialog.get_file()
  667 
  668         # Open the file
  669         file = open(filename, 'r')
  670 
  671         # Read entries
  672         index = 0
  673         for line in file:
  674             # Evaluate if line is a number
  675             try:
  676                 t = float(line.replace('/n', ''))
  677             except:
  678                 continue
  679 
  680             # Write delay to peak list grid
  681             self.grid.SetCellValue(index, 1, str(t*vc_factor))
  682 
  683             # Next peak list
  684             index = index + 1
  685 
  686             # Too many entries in VD list
  687             if index == self.num_rows:
  688                 error_message('Too many entries in list.')
  689                 return
  690 
  691 
  692     def load_peaklist(self, event):
  693         """Function to load peak lists to data grid.
  694 
  695         @param event:   The wx event.
  696         @type event:    wx event
  697         """
  698 
  699         # The dialog.
  700         dialog = RelaxFileDialog(parent=self, message='Select the %s peak list file'%self.label, style=wx.FD_OPEN|wx.FD_MULTIPLE)
  701 
  702         # Show the dialog and catch if no file has been selected.
  703         if status.show_gui and dialog.ShowModal() != wx.ID_OK:
  704             # Don't do anything.
  705             return
  706 
  707         # The files.
  708         files = dialog.get_file()
  709 
  710         # Fill values in data grid
  711         index = 0
  712         for i in range(self.num_rows):
  713             # Add entry if nothing is filled in already
  714             if str(self.grid.GetCellValue(i, 0)) == '':
  715                 # Write peak file
  716                 self.grid.SetCellValue(i, 0, str(files[index]))
  717 
  718                 # Next file
  719                 index = index + 1
  720 
  721                 # Stop if no files left
  722                 if index == len(files):
  723                     break
  724 
  725         # Error message if not all files were loaded
  726         if index < (len(files)-1):
  727                 error_message('Not all files could be loaded.')
  728 
  729 
  730     def sync_ds(self, upload=False):
  731         """Synchronise the rx analysis frame and the relax data store, both ways.
  732 
  733         This method allows the frame information to be uploaded into the relax data store, or for the information in the relax data store to be downloaded by the frame.
  734 
  735         @keyword upload:    A flag which if True will cause the frame to send data to the relax data store.  If False, data will be downloaded from the relax data store to update the frame.
  736         @type upload:       bool
  737         """
  738 
  739         # The peak lists and relaxation times.
  740         if upload:
  741             # The delay time.
  742             self.data.delay_time = str(self.delay_time.GetString(0, self.delay_time.GetLastPosition()))
  743 
  744             # Loop over the rows.
  745             for i in range(self.num_rows):
  746                 # Old save file support.
  747                 if not hasattr(self.data, 'file_list'):
  748                     self.data.file_list = []
  749                 if not hasattr(self.data, 'ncyc'):
  750                     self.data.ncyc = []
  751                 if not hasattr(self.data, 'relax_times'):
  752                     self.data.relax_times = []
  753 
  754                 # The cell data.
  755                 file_name = str(self.grid.GetCellValue(i, 0))
  756                 relax_time = str(self.grid.GetCellValue(i, 1))
  757                 ncyc = str(self.grid.GetCellValue(i, 2))
  758 
  759                 # No data, so stop.
  760                 if file_name == '' and ncyc == '':
  761                     break
  762 
  763                 # New row needed.
  764                 if i >= len(self.data.file_list):
  765                     self.data.file_list.append('')
  766                 if i >= len(self.data.ncyc):
  767                     self.data.ncyc.append('')
  768                 if i >= len(self.data.relax_times):
  769                     self.data.relax_times.append('')
  770 
  771                 # Set the file name and relaxation time.
  772                 self.data.file_list[i] = file_name
  773                 self.data.ncyc[i] = ncyc
  774                 self.data.relax_times[i] = relax_time
  775 
  776         else:
  777             # The delay time.
  778             if hasattr(self.data, 'delay_time'):
  779                 self.delay_time.SetValue(str_to_gui(self.data.delay_time))
  780 
  781             # Loop over the rows.
  782             for i in range(len(self.data.file_list)):
  783                 # The file name.
  784                 if hasattr(self.data, 'file_list'):
  785                     self.grid.SetCellValue(i, 0, str_to_gui(self.data.file_list[i]))
  786 
  787                 # The relaxation time.
  788                 if hasattr(self.data, 'relax_times'):
  789                     self.grid.SetCellValue(i, 1, str_to_gui(self.data.relax_times[i]))
  790 
  791                 # The number of cycles.
  792                 if hasattr(self.data, 'ncyc'):
  793                     self.grid.SetCellValue(i, 2, str_to_gui(self.data.ncyc[i]))
  794 
  795             # Update the grid.
  796             self.update_grid()
  797 
  798 
  799     def update_grid(self):
  800         """Update the grid, changing the relaxation delay times as needed."""
  801 
  802         # The time value.
  803         time = self.delay_time.GetString(0, self.delay_time.GetLastPosition())
  804         try:
  805             time = float(time)
  806         except ValueError:
  807             time = ''
  808 
  809         # Loop over the rows.
  810         for i in range(self.grid.GetNumberRows()):
  811             # The number of cycles.
  812             ncyc = str(self.grid.GetCellValue(i, 2))
  813 
  814             # Update the relaxation time.
  815             if time != '' and ncyc not in ['', '0']:
  816                 self.grid.SetCellValue(i, 1, str(int(ncyc) * time))
  817 
  818             # The relaxation time and number of cycles.
  819             relax_time = str(self.grid.GetCellValue(i, 1))
  820 
  821             # Clear the relaxation time if set to zero.
  822             if relax_time == '0.0':
  823                 self.grid.SetCellValue(i, 1, '')