Package gui :: Module relax_gui
[hide private]
[frames] | no frames]

Source Code for Module gui.relax_gui

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2009 Michael Bieri                                            # 
  4  # Copyright (C) 2010-2016 Edward d'Auvergne                                   # 
  5  # Copyright (C) 2016 Troels Schwartz-Linnet                                   # 
  6  #                                                                             # 
  7  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  8  #                                                                             # 
  9  # This program is free software: you can redistribute it and/or modify        # 
 10  # it under the terms of the GNU General Public License as published by        # 
 11  # the Free Software Foundation, either version 3 of the License, or           # 
 12  # (at your option) any later version.                                         # 
 13  #                                                                             # 
 14  # This program is distributed in the hope that it will be useful,             # 
 15  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 16  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 17  # GNU General Public License for more details.                                # 
 18  #                                                                             # 
 19  # You should have received a copy of the GNU General Public License           # 
 20  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 21  #                                                                             # 
 22  ############################################################################### 
 23   
 24  # Module docstring. 
 25  """Main module for the relax graphical user interface.""" 
 26   
 27  # Python module imports. 
 28  from os import F_OK, access, getcwd, sep 
 29  import platform 
 30  from re import search 
 31  import sys 
 32  from time import sleep 
 33  import webbrowser 
 34  import wx 
 35   
 36  # relax module imports. 
 37  from data_store import Relax_data_store; ds = Relax_data_store() 
 38  from data_store.gui import Gui 
 39  from graphics import IMAGE_PATH, fetch_icon 
 40  from gui.about import About_relax 
 41  from gui.analyses import Analysis_controller 
 42  from gui.spin_viewer.frame import Spin_view_window 
 43  from gui.controller import Controller 
 44  from gui.export_bmrb import Export_bmrb_window 
 45  from gui.filedialog import RelaxDirDialog, RelaxFileDialog 
 46  from gui.fonts import font 
 47  from gui.icons import relax_icons 
 48  from gui.interpreter import Interpreter 
 49  from gui.menu import Menu 
 50  from gui.message import error_message, Question 
 51  from gui.misc import bitmap_setup, gui_raise, open_file, protected_exec 
 52  from gui.pipe_editor import Pipe_editor 
 53  from gui.references import References 
 54  from gui.relax_prompt import Prompt 
 55  from gui.results_viewer import Results_viewer 
 56  from gui.components.free_file_format import Free_file_format_window 
 57  from gui.string_conv import gui_to_str 
 58  from gui.uf_objects import Uf_storage; uf_store = Uf_storage() 
 59  from info import Info_box 
 60  from lib.errors import RelaxNoPipeError 
 61  from lib.io import io_streams_restore 
 62  from pipe_control import state 
 63  from pipe_control.pipes import cdp_name 
 64  from pipe_control.reset import reset 
 65  from pipe_control.system import pwd 
 66  from status import Status; status = Status() 
 67  from version import version 
 68   
 69   
 70  # wx IDs for the toolbar. 
 71  TB_FILE_NEW = wx.NewId() 
 72  TB_FILE_CLOSE = wx.NewId() 
 73  TB_FILE_CLOSE_ALL = wx.NewId() 
 74  TB_FILE_CWD = wx.NewId() 
 75  TB_FILE_OPEN = wx.NewId() 
 76  TB_FILE_SAVE = wx.NewId() 
 77  TB_FILE_SAVE_AS = wx.NewId() 
 78  TB_VIEW_CONTROLLER = wx.NewId() 
 79  TB_VIEW_SPIN_VIEW = wx.NewId() 
 80  TB_VIEW_RESULTS = wx.NewId() 
 81  TB_VIEW_PIPE_EDIT = wx.NewId() 
 82  TB_VIEW_PROMPT = wx.NewId() 
 83   
 84   
 85   
86 -class Main(wx.Frame):
87 """The main GUI class.""" 88 89 # Hard coded variables. 90 min_width = 1000 91 min_height = 600 92
93 - def __init__(self, parent=None, id=-1, title=""):
94 """Initialise the main relax GUI frame.""" 95 96 # Store the wxPython info for os/machine/version specific hacks. 97 status.wx_info = {} 98 status.wx_info["version"] = wx.__version__.split('.') 99 status.wx_info["minor"] = "%s.%s" % (status.wx_info["version"][0], status.wx_info["version"][1]) 100 status.wx_info["os"] = sys.platform 101 status.wx_info["build"] = None 102 if search('gtk2', wx.version()): 103 status.wx_info["build"] = 'gtk' 104 elif search('cocoa', wx.version()): 105 status.wx_info["build"] = 'cocoa' 106 elif search('mac-unicode', wx.version()): 107 status.wx_info["build"] = 'carbon' 108 status.wx_info["full"] = None 109 if status.wx_info["build"]: 110 status.wx_info["full"] = "%s-%s" % (status.wx_info["os"], status.wx_info["build"]) 111 112 # Some internal variables. 113 self.test_suite_flag = False 114 115 # The main window style. 116 style = wx.DEFAULT_FRAME_STYLE 117 if not status.debug and status.wx_info["os"] != 'darwin': 118 style = style | wx.MAXIMIZE 119 120 # Execute the base class __init__ method. 121 super(Main, self).__init__(parent=parent, id=id, title=title, style=style) 122 123 # Force the main window to start maximised (needed for MS Windows). 124 if not status.debug and status.wx_info["os"] != 'darwin': 125 self.Maximize() 126 127 # Set up some standard interface-wide fonts. 128 font.setup() 129 130 # Set up the relax icons. 131 relax_icons.setup() 132 self.SetIcons(relax_icons) 133 134 # Set up the Mac OS X task bar icon. 135 #if status.wx_info["os"] == 'darwin' and status.wx_info["build"] != 'gtk': 136 # self.taskbar_icon = Relax_task_bar_icon(self) 137 138 # Initialise some variables for the GUI. 139 self.launch_dir = getcwd() 140 141 # Set up the frame. 142 self.Layout() 143 self.SetSize((self.min_width, self.min_height)) 144 self.SetMinSize((self.min_width, self.min_height)) 145 self.Centre() 146 147 # The analysis window object storage. 148 self.analysis = Analysis_controller(self) 149 150 # The calculation threads list. 151 self.calc_threads = [] 152 153 # Initialise the GUI data. 154 self.init_data() 155 156 # Build the menu bar. 157 self.menu = Menu(self) 158 159 # Build the toolbar. 160 self.build_toolbar() 161 162 # Build the controller, but don't show it. 163 self.controller = Controller(self) 164 165 # Set the title. 166 self.SetTitle("relax " + version) 167 168 # Set up the status bar. 169 self.status_bar = self.CreateStatusBar(4, 0) 170 self.status_bar.SetStatusWidths([-4, -4, -1, -2]) 171 self.update_status_bar() 172 173 # Add the start screen. 174 self.add_start_screen() 175 176 # Close Box event. 177 self.Bind(wx.EVT_CLOSE, self.exit_gui) 178 179 # Initialise the special interpreter thread object. 180 self.interpreter = Interpreter() 181 182 # Register functions with the observer objects. 183 status.observers.pipe_alteration.register('status bar', self.update_status_bar, method_name='update_status_bar') 184 status.observers.system_cwd_path.register('status bar', self.update_status_bar, method_name='update_status_bar') 185 status.observers.result_file.register('gui', self.show_results_viewer_no_warn, method_name='show_results_viewer_no_warn') 186 status.observers.exec_lock.register('gui', self.enable, method_name='enab') 187 188 # Assume a script has been run and there is data in the store. 189 self.analysis.load_from_store()
190 191
192 - def about_relax(self, event=None):
193 """The about message for relax. 194 195 @keyword event: The wx event. 196 @type event: wx event 197 """ 198 199 # Build the dialog. 200 dialog = About_relax(None, -1) 201 202 # The dialog. 203 if status.show_gui: 204 dialog.Show()
205 206
207 - def action_export_bmrb(self, event=None):
208 """Export the contents of the current data pipe for BMRB deposition. 209 210 @keyword event: The wx event. 211 @type event: wx event 212 """ 213 214 # No current data pipe. 215 if not cdp_name(): 216 gui_raise(RelaxNoPipeError()) 217 return 218 219 # Open the export window. 220 Export_bmrb_window(self)
221 222
223 - def action_state_save(self, event=None):
224 """Save the program state. 225 226 @keyword event: The wx event. 227 @type event: wx event 228 """ 229 230 # Not saved yet, therefore pass execution to state_save_as(). 231 if not self.save_file: 232 self.action_state_save_as(event) 233 return 234 235 # Save. 236 self.state_save()
237 238
239 - def action_state_save_as(self, event=None):
240 """Save the program state with file name selection. 241 242 @keyword event: The wx event. 243 @type event: wx event 244 """ 245 246 # The dialog. 247 dialog = RelaxFileDialog(parent=self, message='Select the relax save state file', defaultFile='state.bz2', wildcard='relax save file (*.bz2)|*.bz2', style=wx.FD_SAVE) 248 249 # Show the dialog and catch if no file has been selected. 250 if status.show_gui and dialog.ShowModal() != wx.ID_OK: 251 # Don't do anything. 252 return 253 254 # The file. 255 file_name = dialog.get_file() 256 257 # Set the file name. 258 self.save_file = file_name 259 260 # Save. 261 self.state_save()
262 263
264 - def add_start_screen(self):
265 """Create a start screen for the main window when no analyses exist.""" 266 267 # The sizer for the main GUI window. 268 sizer = wx.BoxSizer(wx.VERTICAL) 269 self.SetSizer(sizer) 270 271 # The relax icon. 272 image = wx.StaticBitmap(self, -1, bitmap_setup(IMAGE_PATH+'ulysses_shadowless_400x168.png')) 273 274 # Add the icon to the main spacer with spacing. 275 sizer.AddStretchSpacer() 276 sizer.Add(image, 0, wx.ALIGN_CENTER_HORIZONTAL, 0) 277 sizer.AddStretchSpacer() 278 279 # Re-perform the layout of the GUI elements, and refresh. 280 self.Layout() 281 self.Refresh()
282 283
284 - def build_toolbar(self):
285 """Create the toolbar.""" 286 287 # Init. 288 self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL|wx.TB_FLAT) 289 290 # The new analysis button. 291 self.toolbar.AddLabelTool(TB_FILE_NEW, "New analysis", wx.Bitmap(fetch_icon('oxygen.actions.document-new', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="New analysis") 292 self.Bind(wx.EVT_TOOL, self.analysis.menu_new, id=TB_FILE_NEW) 293 294 # The close analysis button. 295 self.toolbar.AddLabelTool(TB_FILE_CLOSE, "Close analysis", wx.Bitmap(fetch_icon('oxygen.actions.document-close', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Close analysis") 296 self.Bind(wx.EVT_TOOL, self.analysis.menu_close, id=TB_FILE_CLOSE) 297 298 # The close all analyses button. 299 self.toolbar.AddLabelTool(TB_FILE_CLOSE_ALL, "Close all analyses", wx.Bitmap(fetch_icon('oxygen.actions.dialog-close', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Close all analyses") 300 self.Bind(wx.EVT_TOOL, self.analysis.menu_close_all, id=TB_FILE_CLOSE_ALL) 301 302 # A separator. 303 self.toolbar.AddSeparator() 304 305 # The change working directory button. 306 self.toolbar.AddLabelTool(TB_FILE_CWD, "Change working directory", wx.Bitmap(fetch_icon('oxygen.places.folder-favorites', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Change working directory") 307 self.Bind(wx.EVT_TOOL, self.system_cwd, id=TB_FILE_CWD) 308 309 # The open state button. 310 self.toolbar.AddLabelTool(TB_FILE_OPEN, "Open relax state", wx.Bitmap(fetch_icon('oxygen.actions.document-open', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Open relax state") 311 self.Bind(wx.EVT_TOOL, self.state_load, id=TB_FILE_OPEN) 312 313 # The save state button. 314 self.toolbar.AddLabelTool(TB_FILE_SAVE, "Save relax state", wx.Bitmap(fetch_icon('oxygen.actions.document-save', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Save relax state") 315 self.Bind(wx.EVT_TOOL, self.action_state_save, id=TB_FILE_SAVE) 316 317 # The save as button. 318 self.toolbar.AddLabelTool(TB_FILE_SAVE_AS, "Save as", wx.Bitmap(fetch_icon('oxygen.actions.document-save-as', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Save as") 319 self.Bind(wx.EVT_TOOL, self.action_state_save_as, id=TB_FILE_SAVE_AS) 320 321 # A separator. 322 self.toolbar.AddSeparator() 323 324 # The relax controller button. 325 self.toolbar.AddLabelTool(TB_VIEW_CONTROLLER, "Controller", wx.Bitmap(fetch_icon('oxygen.apps.preferences-system-performance', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="relax controller") 326 self.Bind(wx.EVT_TOOL, self.show_controller, id=TB_VIEW_CONTROLLER) 327 328 # The spin viewer button. 329 self.toolbar.AddLabelTool(TB_VIEW_SPIN_VIEW, "Spin viewer", wx.Bitmap(fetch_icon('relax.spin', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Spin viewer window") 330 self.Bind(wx.EVT_TOOL, self.show_tree, id=TB_VIEW_SPIN_VIEW) 331 332 # The results viewer button. 333 self.toolbar.AddLabelTool(TB_VIEW_RESULTS, "Results viewer", wx.Bitmap(fetch_icon('oxygen.actions.view-statistics', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Results viewer window") 334 self.Bind(wx.EVT_TOOL, self.show_results_viewer, id=TB_VIEW_RESULTS) 335 336 # The data pipe editor button. 337 self.toolbar.AddLabelTool(TB_VIEW_PIPE_EDIT, "Data pipe editor", wx.Bitmap(fetch_icon('relax.pipe', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Data pipe editor") 338 self.Bind(wx.EVT_TOOL, self.show_pipe_editor, id=TB_VIEW_PIPE_EDIT) 339 340 # The relax prompt button. 341 self.toolbar.AddLabelTool(TB_VIEW_PROMPT, "relax prompt", wx.Bitmap(fetch_icon('oxygen.mimetypes.application-x-executable-script', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="The relax prompt GUI window") 342 self.Bind(wx.EVT_TOOL, self.show_prompt, id=TB_VIEW_PROMPT) 343 344 # Build the toolbar. 345 self.toolbar.Realize()
346 347
348 - def close_windows(self):
349 """Throw a warning to close all of the non-essential windows when execution is locked. 350 351 This is to speed up the calculations by avoiding window updates. 352 """ 353 354 # Init the window list. 355 win_list = [] 356 357 # Is the spin viewer window open? 358 if hasattr(self, 'spin_viewer') and self.spin_viewer.IsShown(): 359 win_list.append('The spin viewer window') 360 361 # Is the pipe editor window open? 362 if hasattr(self, 'pipe_editor') and self.pipe_editor.IsShown(): 363 win_list.append('The data pipe editor window') 364 365 # Is the results viewer window open? 366 if hasattr(self, 'results_viewer') and self.results_viewer.IsShown(): 367 win_list.append('The results viewer window') 368 369 # The windows are not open, so quit. 370 if not len(win_list): 371 return 372 373 # The text. 374 text = "The following windows are currently open:\n\n" 375 for win in win_list: 376 text = "%s\t%s.\n" % (text, win) 377 text = text + "\nClosing these will significantly speed up the calculations." 378 379 # Display the error message dialog. 380 dlg = wx.MessageDialog(self, text, caption="Close windows", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP) 381 if status.show_gui: 382 dlg.ShowModal() 383 384 # Otherwise output to stderr. 385 else: 386 sys.stderr.write(text)
387 388
389 - def contact_relax(self, event=None):
390 """Write an email to the relax mailing-list using the standard mailing program. 391 392 @keyword event: The wx event. 393 @type event: wx event 394 """ 395 396 webbrowser.open_new('mailto:relax-users@gna.org')
397 398
399 - def enable(self):
400 """Enable and disable certain parts of the main window with the execution lock.""" 401 402 # Flag for enabling or disabling the elements. 403 enable = False 404 if not status.exec_lock.locked(): 405 enable = True 406 407 # The toolbar. 408 wx.CallAfter(self.toolbar.EnableTool, TB_FILE_NEW, enable) 409 wx.CallAfter(self.toolbar.EnableTool, TB_FILE_CLOSE, enable) 410 wx.CallAfter(self.toolbar.EnableTool, TB_FILE_CLOSE_ALL, enable) 411 wx.CallAfter(self.toolbar.EnableTool, TB_FILE_CWD, enable) 412 wx.CallAfter(self.toolbar.EnableTool, TB_FILE_OPEN, enable) 413 wx.CallAfter(self.toolbar.EnableTool, TB_FILE_SAVE, enable) 414 wx.CallAfter(self.toolbar.EnableTool, TB_FILE_SAVE_AS, enable)
415 416
417 - def exit_gui(self, event=None):
418 """Catch the main window closure and perform the exit procedure. 419 420 @keyword event: The wx event. 421 @type event: wx event 422 """ 423 424 # Ask if the user is sure they would like to exit. 425 doexit = wx.ID_YES 426 if status.show_gui and not ds.is_empty(): 427 doexit = Question('Are you sure you would like to quit relax? All unsaved data will be lost.', title='Exit relax', default=True).ShowModal() 428 429 # Exit. 430 if doexit == wx.ID_YES: 431 # Restore the IO streams. 432 io_streams_restore(verbosity=0) 433 434 # The relax information box. 435 info = Info_box() 436 437 # The width of the printout. 438 if platform.uname()[0] in ['Windows', 'Microsoft']: 439 width = 80 440 else: 441 width = 100 442 443 # Remove the Mac OS X task bar icon. 444 if hasattr(self, 'taskbar_icon'): 445 self.taskbar_icon.Destroy() 446 447 # Terminate the interpreter thread to allow for a cleaner exit. 448 self.interpreter.exit() 449 450 # End the GUI main loop. 451 app = wx.GetApp() 452 app.ExitMainLoop()
453 454
455 - def init_data(self):
456 """Initialise the data used by the GUI interface.""" 457 458 # Temporary data: the save file. 459 self.save_file = None 460 self.system_cwd_path = pwd(verbose=False) 461 462 # Add the GUI object to the data store, if not present. 463 if not hasattr(ds, 'relax_gui'): 464 ds.relax_gui = Gui()
465 466
467 - def free_file_format_settings(self, event=None):
468 """Open the free file format settings window. 469 470 @keyword event: The wx event. 471 @type event: wx event 472 """ 473 474 # Build the window. 475 win = Free_file_format_window() 476 477 # Show the window. 478 if status.show_gui: 479 win.Show()
480 481
482 - def references(self, event=None):
483 """Display the references relevant for relax. 484 485 @keyword event: The wx event. 486 @type event: wx event 487 """ 488 489 # Build and show the references window. 490 self.references = References(self) 491 if status.show_gui: 492 self.references.Show()
493 494
495 - def relax_manual(self, event=None):
496 """Display the relax manual. 497 498 @keyword event: The wx event. 499 @type event: wx event 500 """ 501 502 # The PDF manual. 503 file = status.install_path + sep+"docs"+sep+"relax.pdf" 504 505 # Test if it exists. 506 if not access(file, F_OK): 507 error_message("The relax manual '%s' cannot be found. Please compile using the scons program." % file) 508 return 509 510 # Open the relax PDF manual using the native PDF reader. 511 open_file(file)
512 513
514 - def reset(self):
515 """Reset the GUI.""" 516 517 # Close some GUI windows, if open. 518 windows = ['pipe_editor', 'relax_prompt', 'results_viewer', 'spin_viewer'] 519 for window in windows: 520 if hasattr(self, window): 521 # Get the object. 522 win_obj = getattr(self, window) 523 524 # Close the window. 525 win_obj.Close() 526 527 # Flush all wx events to make sure the GUI is ready for the next test. 528 wx.Yield() 529 530 # Reset the relax controller. 531 self.controller.reset()
532 533
534 - def run_test_suite(self, event=None, categories=['system', 'unit', 'gui', 'verification']):
535 """Execute the full test suite. 536 537 @keyword event: The wx event. 538 @type event: wx event 539 @keyword categories: The list of test categories to run, for example ['system', 'unit', 'gui', 'verification'] for all tests. 540 @type categories: list of str 541 """ 542 543 # Ask if this should be done. 544 msg = "In running the test suite, relax will be reset and all data lost. Are you sure you would like to run the test suite?" 545 if Question(msg, parent=self, size=(400, 150), default=False).ShowModal() == wx.ID_NO: 546 return 547 548 # Set the test suite flag. 549 self.test_suite_flag = True 550 551 # Change the cursor to waiting. 552 wx.BeginBusyCursor() 553 554 # Set a new style to stay on top, refreshing to update the style (needed for Mac OS X and MS Windows). 555 orig_style = self.controller.GetWindowStyle() 556 self.controller.SetWindowStyle(orig_style | wx.STAY_ON_TOP) 557 self.controller.Refresh() 558 559 # Make the relax controller modal so that all other windows are deactivated (to stop users from clicking on things). 560 self.controller.MakeModal(True) 561 562 # Close all open windows. 563 if hasattr(self, 'spin_viewer'): 564 self.spin_viewer.Close() 565 if hasattr(self, 'pipe_editor'): 566 self.pipe_editor.Close() 567 if hasattr(self, 'results_viewer'): 568 self.results_viewer.Close() 569 if hasattr(self, 'relax_prompt'): 570 self.relax_prompt.Close() 571 572 # Reset relax. 573 reset() 574 575 # Show the relax controller. 576 self.show_controller(event) 577 578 # Yield 579 wx.GetApp().Yield(True) 580 581 # Prevent all new GUI elements from being shown. 582 status.show_gui = False 583 584 # Run the tests (with the import here to break a nasty circular import). 585 import test_suite.test_suite_runner 586 runner = test_suite.test_suite_runner.Test_suite_runner([], from_gui=True, categories=categories) 587 runner.run_all_tests() 588 589 # Reactive the GUI. 590 status.show_gui = True 591 592 # Turn off the busy cursor. 593 if wx.IsBusy(): 594 wx.EndBusyCursor() 595 596 # Restore the controller. 597 self.controller.SetWindowStyle(orig_style) 598 self.controller.MakeModal(False) 599 self.controller.Refresh() 600 601 # Unset the test suite flag. 602 self.test_suite_flag = False 603 604 # Set the controller main gauge to 100%. 605 wx.CallAfter(self.controller.main_gauge.SetValue, 100)
606 607
608 - def run_test_suite_gui(self, event=None):
609 """Execute the GUI tests. 610 611 @keyword event: The wx event. 612 @type event: wx event 613 """ 614 615 # Forward the call. 616 self.run_test_suite(event, categories=['gui'])
617 618
619 - def run_test_suite_sys(self, event=None):
620 """Execute the system tests. 621 622 @keyword event: The wx event. 623 @type event: wx event 624 """ 625 626 # Forward the call. 627 self.run_test_suite(event, categories=['system'])
628 629
630 - def run_test_suite_unit(self, event=None):
631 """Execute the unit tests. 632 633 @keyword event: The wx event. 634 @type event: wx event 635 """ 636 637 # Forward the call. 638 self.run_test_suite(event, categories=['unit'])
639 640
641 - def run_test_suite_verification(self, event=None):
642 """Execute the verification tests. 643 644 @keyword event: The wx event. 645 @type event: wx event 646 """ 647 648 # Forward the call. 649 self.run_test_suite(event, categories=['verification'])
650 651
652 - def show_controller(self, event=None):
653 """Display the relax controller window. 654 655 @keyword event: The wx event. 656 @type event: wx event 657 """ 658 659 # Bring the window to the front. 660 if self.controller.IsShown(): 661 self.controller.Raise() 662 return 663 664 # Open the window. 665 if status.show_gui: 666 self.controller.Show()
667 668
669 - def show_pipe_editor(self, event=None):
670 """Display the data pipe editor window. 671 672 @keyword event: The wx event. 673 @type event: wx event 674 """ 675 676 # Throw a warning if the execution lock is on. 677 if status.exec_lock.locked(): 678 dlg = wx.MessageDialog(self, "Leaving the pipe editor window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP) 679 if status.show_gui: 680 dlg.ShowModal() 681 682 # Build the pipe editor if needed. 683 if not hasattr(self, 'pipe_editor'): 684 self.pipe_editor = Pipe_editor(gui=self) 685 686 # Bring the window to the front. 687 if self.pipe_editor.IsShown(): 688 self.pipe_editor.Raise() 689 return 690 691 # Open the window. 692 if status.show_gui and not self.pipe_editor.IsShown(): 693 self.pipe_editor.Show() 694 695 # Update the grid. 696 self.pipe_editor.update_grid() 697 self.pipe_editor.activate() 698 699 # Register the grid for updating when a user function completes or when the GUI analysis tabs change (needed here for the window hiding and associated unregistering). 700 self.pipe_editor.observer_setup(register=True)
701 702
703 - def show_prompt(self, event=None):
704 """Display the relax prompt window. 705 706 @keyword event: The wx event. 707 @type event: wx event 708 """ 709 710 # Build the relax prompt if needed. 711 if not hasattr(self, 'relax_prompt'): 712 self.relax_prompt = Prompt(None, -1, "", parent=self) 713 714 # Bring the window to the front. 715 if self.relax_prompt.IsShown(): 716 self.relax_prompt.Raise() 717 return 718 719 # Open the window. 720 if status.show_gui: 721 self.relax_prompt.Show()
722 723
724 - def show_results_viewer(self, event=None):
725 """Display the analysis results. 726 727 @keyword event: The wx event. 728 @type event: wx event 729 """ 730 731 # Show the results viewer in a thread safe way. 732 wx.CallAfter(self.show_results_viewer_safe, warn=True)
733 734
735 - def show_results_viewer_safe(self, warn=False):
736 """Display the analysis results in a thread safe wx.CallAfter call. 737 738 @keyword warn: A flag which if True will cause a message dialog to appear warning about keeping the window open with the execution lock. 739 @type warn: bool 740 """ 741 742 # Throw a warning if the execution lock is on. 743 if warn and status.exec_lock.locked(): 744 dlg = wx.MessageDialog(self, "Leaving the results viewer window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP) 745 if status.show_gui: 746 wx.CallAfter(dlg.ShowModal) 747 748 # Create the results viewer window if needed. 749 if not hasattr(self, 'results_viewer'): 750 self.results_viewer = Results_viewer(self) 751 752 # Bring the window to the front. 753 if self.results_viewer.IsShown(): 754 self.results_viewer.Raise() 755 return 756 757 # Open the window. 758 if status.show_gui and not self.results_viewer.IsShown(): 759 self.results_viewer.Show()
760 761
763 """Display the analysis results.""" 764 765 # Show the results viewer in a thread safe way with no warning dialog. 766 wx.CallAfter(self.show_results_viewer_safe, warn=False)
767 768
769 - def show_tree(self, event=None):
770 """Display the molecule, residue, and spin tree window. 771 772 @keyword event: The wx event. 773 @type event: wx event 774 """ 775 776 # Throw a warning if the execution lock is on. 777 if status.exec_lock.locked(): 778 dlg = wx.MessageDialog(self, "Leaving the spin viewer window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP) 779 if status.show_gui: 780 dlg.ShowModal() 781 782 # Build the spin view window. 783 if not hasattr(self, 'spin_viewer'): 784 self.spin_viewer = Spin_view_window(None, -1, "", parent=self) 785 786 # Bring the window to the front. 787 if self.spin_viewer.IsShown(): 788 self.spin_viewer.Raise() 789 return 790 791 # Open the window (the GUI flag check is inside the Show method). 792 if not self.spin_viewer.IsShown(): 793 self.spin_viewer.Show(show=status.show_gui)
794 795
796 - def state_load(self, event=None, file_name=None):
797 """Load the program state. 798 799 @keyword event: The wx event. 800 @type event: wx event 801 @keyword file_name: The name of the file to load (for dialogless operation). 802 @type file_name: str 803 """ 804 805 # Execution lock. 806 if status.exec_lock.locked(): 807 return 808 809 # Warning. 810 if not self.analysis.init_state or not ds.is_empty(): 811 # The message. 812 msg = "Loading a saved relax state file will cause all unsaved data to be lost. Are you sure you would to open a save file?" 813 814 # The dialog. 815 if status.show_gui and Question(msg, default=True, size=(400, 150)).ShowModal() == wx.ID_NO: 816 return 817 818 # Open the dialog. 819 if not file_name: 820 dialog = RelaxFileDialog(parent=self, message='Select the relax save state file', defaultFile='state.bz2', wildcard='relax save files (*.bz2;*.gz)|*.bz2;*.gz|All files (*)|*', style=wx.FD_OPEN) 821 822 # Show the dialog and catch if no file has been selected. 823 if status.show_gui and dialog.ShowModal() != wx.ID_OK: 824 # Don't do anything. 825 return 826 827 # The file. 828 file_name = gui_to_str(dialog.get_file()) 829 830 # Yield to allow the cursor to be changed. 831 wx.Yield() 832 833 # Change the cursor to waiting, and freeze the GUI. 834 wx.BeginBusyCursor() 835 self.Freeze() 836 837 # Make sure the GUI returns to normal if a failure occurs. 838 try: 839 # Delete the current tabs. 840 self.analysis.delete_all() 841 842 # Reset the relax data store. 843 reset() 844 845 # The new save file name. 846 self.save_file = file_name 847 848 # Load the relax state. 849 if protected_exec(state.load_state, file_name, verbosity=0): 850 # Update the core of the GUI to match the new data store. 851 self.sync_ds(upload=False) 852 853 # File loading failure. 854 else: 855 # Reset relax to clear any partially loaded data. 856 reset() 857 858 # Reinitialise the GUI data store structure. 859 self.init_data() 860 861 # Reset the cursor, and thaw the GUI. 862 finally: 863 self.Thaw() 864 865 # Turn off the busy cursor. 866 if wx.IsBusy(): 867 wx.EndBusyCursor()
868 869
870 - def state_save(self):
871 """Save the program state.""" 872 873 # Update the data store to match the GUI. 874 self.sync_ds(upload=True) 875 876 # Save the relax state (with save user feedback). 877 try: 878 wx.BeginBusyCursor() 879 state.save_state(self.save_file, verbosity=0, force=True) 880 881 # Sleep a little so the user sees the busy cursor and knows that a save has occurred! 882 sleep(1) 883 884 # Turn off the user feedback. 885 finally: 886 if wx.IsBusy(): 887 wx.EndBusyCursor()
888 889
890 - def sync_ds(self, upload=False):
891 """Synchronise the GUI and the relax data store, both ways. 892 893 This method allows the GUI information to be uploaded into the relax data store, or for the information in the relax data store to be downloaded by the GUI. 894 895 @keyword upload: A flag which if True will cause the GUI to send data to the relax data store. If False, data will be downloaded from the relax data store to update the GUI. 896 @type upload: bool 897 """ 898 899 # Loop over each analysis. 900 for page in self.analysis.analysis_loop(): 901 # Execute the analysis page specific update methods. 902 if hasattr(page, 'sync_ds'): 903 page.sync_ds(upload)
904 905
906 - def system_cwd(self, event=None):
907 """Change the system current working directory. 908 909 @keyword event: The wx event. 910 @type event: wx event 911 """ 912 913 # The dialog. 914 dialog = RelaxDirDialog(parent=self, message="Select working directory", defaultPath=wx.EmptyString, style=wx.DD_CHANGE_DIR) 915 916 # Show the dialog and catch if no directory has been selected. 917 if status.show_gui and dialog.ShowModal() != wx.ID_OK: 918 # Don't do anything. 919 return 920 921 # Call the get_path function to get the directory name and change path. 922 self.system_cwd_path = dialog.get_path() 923 924 # Update the status bar. 925 self.update_status_bar() 926 927 # Change the directory 928 try: 929 wx.BeginBusyCursor() 930 931 # Sleep a little so the user sees the busy cursor and knows that the directory changes has occurred. 932 sleep(1) 933 934 # Turn off the user feedback. 935 finally: 936 if wx.IsBusy(): 937 wx.EndBusyCursor()
938 939
940 - def uf_call(self, event=None):
941 """Catch the user function call to properly specify the parent window. 942 943 @keyword event: The wx event. 944 @type event: wx event 945 """ 946 947 # The user function ID. 948 uf_id = event.GetId() 949 950 # Get the user function name. 951 name = uf_store.get_uf(uf_id) 952 953 # Call the user function GUI object. 954 uf_store[name](event=event, wx_parent=self)
955 956
957 - def update_status_bar(self):
958 """Update the status bar info.""" 959 960 # Set the current data pipe info. 961 pipe = cdp_name() 962 963 # No data pipe. 964 if pipe == None: 965 pipe = '' 966 967 # Get the current working directory 968 self.system_cwd_path = pwd(verbose=False) 969 970 # The relax information box. 971 info = Info_box() 972 973 # Set the status. 974 wx.CallAfter(self.status_bar.SetStatusText, info.copyright_short, 0) 975 wx.CallAfter(self.status_bar.SetStatusText, self.system_cwd_path, 1) 976 wx.CallAfter(self.status_bar.SetStatusText, "Data pipe:", 2) 977 wx.CallAfter(self.status_bar.SetStatusText, pipe, 3)
978