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-2013 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.SetToolTipString("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.SetToolTipString("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.SetToolTipString("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, '')
824