Package gui :: Package components :: Module grid
[hide private]
[frames] | no frames]

Source Code for Module gui.components.grid

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