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