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

Source Code for Module gui.relax_gui

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