Package gui :: Package spin_viewer :: Module frame
[hide private]
[frames] | no frames]

Source Code for Module gui.spin_viewer.frame

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2010-2015 Edward d'Auvergne                                   # 
  4  # Copyright (C) 2013-2014 Troels E. Linnet                                    # 
  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  """The spin viewer frame.""" 
 25   
 26  # Python module imports. 
 27  import wx 
 28   
 29  # relax module imports. 
 30  from graphics import WIZARD_IMAGE_PATH, fetch_icon 
 31  from gui.icons import relax_icons 
 32  from gui.misc import gui_raise 
 33  from gui.spin_viewer.splitter import Tree_splitter 
 34  from gui.string_conv import gui_to_str, str_to_gui 
 35  from gui.wizards.wiz_objects import Wiz_page, Wiz_window 
 36  from gui.uf_objects import build_uf_menus, Uf_storage; uf_store = Uf_storage() 
 37  from lib.errors import RelaxNoPipeError 
 38  from pipe_control.pipes import cdp_name, pipe_names 
 39  from status import Status; status = Status() 
 40   
 41   
 42  # wx IDs for the toolbar. 
 43  TB_SPIN_LOADER_ID = wx.NewId() 
 44  TB_REFRESH = wx.NewId() 
 45   
 46   
 47   
48 -class Spin_view_window(wx.Frame):
49 """A window element for the tree view.""" 50
51 - def __init__(self, *args, **kwds):
52 """Set up the relax prompt.""" 53 54 # Store the parent object. 55 self.gui = kwds.pop('parent') 56 57 # Create GUI elements 58 kwds["style"] = wx.DEFAULT_FRAME_STYLE 59 if not status.debug and status.wx_info["os"] != 'darwin': 60 kwds["style"] = kwds["style"] | wx.MAXIMIZE 61 wx.Frame.__init__(self, *args, **kwds) 62 63 # Force the main window to start maximised (needed for MS Windows). 64 if not status.debug and status.wx_info["os"] != 'darwin': 65 self.Maximize() 66 67 # Set up the window icon. 68 self.SetIcons(relax_icons) 69 70 # Some default values. 71 self.size_x = 1000 72 self.size_y = 750 73 74 # Set up the window. 75 sizer = self.setup_window() 76 77 # Create a menu. 78 self._create_menu() 79 80 # Build the toolbar. 81 self.toolbar() 82 83 # The splitter window. 84 splitter = Tree_splitter(self.gui, self, -1) 85 sizer.Add(splitter, 1, wx.EXPAND|wx.ALL, 0) 86 87 # Initialise observer name. 88 self.name = 'spin viewer'
89 90
91 - def _activate(self):
92 """Activate or deactivate certain elements in response to the execution lock.""" 93 94 # Flag for enabling or disabling the elements. 95 enable = False 96 if not status.exec_lock.locked(): 97 enable = True 98 99 # Loop over the menus. 100 for menu, label in self.menubar.GetMenus(): 101 # Loop over the menu items. 102 for item in menu.GetMenuItems(): 103 wx.CallAfter(item.Enable, enable) 104 105 # The spin loader. 106 wx.CallAfter(self.bar.EnableTool, TB_SPIN_LOADER_ID, enable) 107 108 # The pipe selector. 109 wx.CallAfter(self.pipe_name.Enable, enable)
110 111
112 - def _create_menu(self):
113 """Build a menu for the window.""" 114 115 # Create the menu bar GUI item and add it to the main frame. 116 self.menubar = wx.MenuBar() 117 if status.show_gui: 118 self.SetMenuBar(self.menubar) 119 120 # The user function menus. 121 self.menu_uf_ids = build_uf_menus(parent=self, menubar=self.menubar)
122 123
124 - def Destroy(self, event=None):
125 """Cleanly destroy the spin viewer window. 126 127 @keyword event: The wx event. 128 @type event: wx event 129 """ 130 131 # First unregister the methods from the observers. 132 status.observers.gui_uf.unregister(self.name) 133 status.observers.pipe_alteration.unregister(self.name) 134 status.observers.exec_lock.unregister(self.name) 135 136 # Destroy the spin loading wizard, if it exists. 137 if hasattr(self, 'wizard'): 138 self.wizard.Destroy() 139 del self.wizard 140 141 # Destroy all children of the window. 142 super(Spin_view_window, self).DestroyChildren() 143 144 # Destroy the spin viewer window. 145 super(Spin_view_window, self).Destroy()
146 147
148 - def Show(self, show=True):
149 """Change the behaviour of showing the window to update the content. 150 151 @keyword show: A flag which is True shows the window. 152 @type show: bool 153 """ 154 155 # Register a few methods in the observer objects. 156 status.observers.gui_uf.register(self.name, self.refresh, method_name='ref') 157 status.observers.pipe_alteration.register(self.name, self.refresh, method_name='ref') 158 status.observers.exec_lock.register(self.name, self._activate, method_name='_activate') 159 160 # First update. 161 self.refresh() 162 163 # Activate or deactivate the frame. 164 self._activate() 165 166 # Then show the window using the base class method. 167 if status.show_gui: 168 super(Spin_view_window, self).Show(show)
169 170
171 - def refresh(self, event=None):
172 """Event handler for the refresh action (thread safe). 173 174 @keyword event: The wx event. 175 @type event: wx event 176 """ 177 178 # Thread safe. 179 wx.CallAfter(self.refresh_safe)
180 181
182 - def refresh_safe(self):
183 """Refresh the spin viewer window.""" 184 185 # Change the cursor to busy. 186 wx.BeginBusyCursor() 187 188 # Update the data pipe selector. 189 self.update_pipes() 190 191 # Update the tree. 192 self.tree_panel.update() 193 194 # Redisplay the container. 195 self.container.display(self.tree_panel.get_info()) 196 197 # Reset the cursor. 198 if wx.IsBusy(): 199 wx.EndBusyCursor()
200 201
202 - def handler_close(self, event=None):
203 """Event handler for the close window action. 204 205 @keyword event: The wx event. 206 @type event: wx event 207 """ 208 209 # Unregister the methods from the observers to avoid unnecessary updating. 210 status.observers.gui_uf.unregister(self.name) 211 status.observers.pipe_alteration.unregister(self.name) 212 status.observers.exec_lock.unregister(self.name) 213 214 # Close the window. 215 self.Hide()
216 217
218 - def load_spins_wizard(self, event=None):
219 """The spin loading wizard. 220 221 @keyword event: The wx event. 222 @type event: wx event 223 """ 224 225 # No current data pipe. 226 if not cdp_name(): 227 gui_raise(RelaxNoPipeError()) 228 return 229 230 # Change the cursor to busy. 231 wx.BeginBusyCursor() 232 233 # Destroy the spin loading wizard, if it exists. 234 if hasattr(self, 'wizard'): 235 self.wizard.Destroy() 236 237 # Initialise a wizard. 238 self.wizard = Wiz_window(parent=self, size_x=1000, size_y=750, title="Load spins") 239 self.page_indices = {} 240 241 # The loading method page. 242 self.page_method = Load_method_page(self.wizard) 243 self.page_indices['method'] = self.wizard.add_page(self.page_method, apply_button=True, skip_button=False) 244 self.wizard.set_seq_next_fn(self.page_indices['method'], self.wizard_page_after_load_method) 245 246 # The sequence.read page. 247 page = uf_store['sequence.read'].create_page(self.wizard) 248 self.page_indices['sequence.read'] = self.wizard.add_page(page, skip_button=True) 249 self.wizard.set_seq_next_fn(self.page_indices['sequence.read'], self.wizard_page_after_sequence_read) 250 251 # The structure.read_pdb page. 252 page = uf_store['structure.read_pdb'].create_page(self.wizard) 253 self.page_indices['structure.read_pdb'] = self.wizard.add_page(page, skip_button=True) 254 self.wizard.set_seq_next_fn(self.page_indices['structure.read_pdb'], self.wizard_page_after_structure_read) 255 256 # The structure.read_xyz page. 257 page = uf_store['structure.read_xyz'].create_page(self.wizard) 258 self.page_indices['structure.read_xyz'] = self.wizard.add_page(page, skip_button=True) 259 self.wizard.set_seq_next_fn(self.page_indices['structure.read_xyz'], self.wizard_page_after_structure_read) 260 261 # The spectrum.read_spins page. 262 page = uf_store['spectrum.read_spins'].create_page(self.wizard) 263 self.page_indices['spectrum.read_spins'] = self.wizard.add_page(page, skip_button=True) 264 self.wizard.set_seq_next_fn(self.page_indices['spectrum.read_spins'], self.wizard_page_after_sequence_read) 265 266 # The structure.load_spins page. 267 page = uf_store['structure.load_spins'].create_page(self.wizard) 268 self.page_indices['structure.load_spins'] = self.wizard.add_page(page) 269 270 # The termination page. 271 page = Finish_page(self.wizard) 272 self.page_indices['fin'] = self.wizard.add_page(page, apply_button=False, skip_button=False) 273 274 # Reset the cursor. 275 if wx.IsBusy(): 276 wx.EndBusyCursor() 277 278 # Run the wizard. 279 self.wizard.run()
280 281
282 - def setup_window(self):
283 """Set up the window. 284 285 @return: The sizer object. 286 @rtype: wx.Sizer instance 287 """ 288 289 # Set the frame title. 290 self.SetTitle("The spin viewer") 291 292 # Use a box sizer for packing the shell. 293 sizer = wx.BoxSizer(wx.VERTICAL) 294 self.SetSizer(sizer) 295 296 # Close the window cleanly (hide so it can be reopened). 297 self.Bind(wx.EVT_CLOSE, self.handler_close) 298 299 # Set the default size of the controller. 300 self.SetSize((self.size_x, self.size_y)) 301 302 # Return the sizer. 303 return sizer
304 305
306 - def toolbar(self):
307 """Create the toolbar.""" 308 309 # Init. 310 self.bar = self.CreateToolBar(wx.TB_HORIZONTAL|wx.TB_FLAT|wx.TB_TEXT) 311 312 # The spin loading button. 313 tooltip = "Load spins from either a sequence file or from a 3D structure file." 314 self.bar.AddLabelTool(TB_SPIN_LOADER_ID, "Load spins", wx.Bitmap(fetch_icon('relax.spin', '32x32'), wx.BITMAP_TYPE_ANY), bmpDisabled=wx.Bitmap(fetch_icon('relax.spin_grey', '32x32'), wx.BITMAP_TYPE_ANY), shortHelp=tooltip, longHelp=tooltip) 315 self.Bind(wx.EVT_TOOL, self.load_spins_wizard, id=TB_SPIN_LOADER_ID) 316 317 # A separator. 318 self.bar.AddSeparator() 319 320 # The refresh button. 321 tooltip = "Refresh the spin view." 322 self.bar.AddLabelTool(TB_REFRESH, "Refresh", wx.Bitmap(fetch_icon('oxygen.actions.view-refresh', '32x32'), wx.BITMAP_TYPE_ANY), shortHelp=tooltip, longHelp=tooltip) 323 self.Bind(wx.EVT_TOOL, self.refresh, id=TB_REFRESH) 324 325 # A separator. 326 self.bar.AddSeparator() 327 328 # The pipe text. 329 text = wx.StaticText(self.bar, -1, ' Current data pipe: ', style=wx.ALIGN_LEFT) 330 self.bar.AddControl(text) 331 332 # The pipe selection. 333 self.pipe_name = wx.ComboBox(self.bar, -1, "", style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=[]) 334 self.bar.AddControl(self.pipe_name) 335 self.Bind(wx.EVT_COMBOBOX, self.update_pipes, self.pipe_name) 336 337 # Build the toolbar. 338 self.bar.Realize()
339 340
341 - def uf_call(self, event=None):
342 """Catch the user function call to properly specify the parent window. 343 344 @keyword event: The wx event. 345 @type event: wx event 346 """ 347 348 # The user function ID. 349 uf_id = event.GetId() 350 351 # Get the user function name. 352 name = uf_store.get_uf(uf_id) 353 354 # Call the user function GUI object. 355 uf_store[name](event=event, wx_parent=self)
356 357
358 - def update_pipes(self, event=None):
359 """Update the spin view data pipe selector. 360 361 @keyword event: The wx event. 362 @type event: wx event 363 """ 364 365 # Change the cursor to busy. 366 wx.BeginBusyCursor() 367 368 # Init. 369 pipe_switch = False 370 371 # The selected pipe. 372 if event: 373 # The name of the selected pipe. 374 pipe = gui_to_str(self.pipe_name.GetString(event.GetSelection())) 375 376 # A pipe change. 377 if pipe != cdp_name(): 378 pipe_switch = True 379 else: 380 pipe = cdp_name() 381 if not pipe: 382 pipe = '' 383 384 # Clear the previous data. 385 self.pipe_name.Clear() 386 387 # The list of pipe names. 388 for name in pipe_names(): 389 self.pipe_name.Append(str_to_gui(name)) 390 391 # Switch. 392 if pipe_switch: 393 # Switch data pipes. 394 self.gui.interpreter.apply('pipe.switch', pipe) 395 396 # Update the tree view. 397 self.tree_panel.update() 398 399 # Set the pipe name to the cdp. 400 self.pipe_name.SetValue(str_to_gui(pipe)) 401 402 # Reset the cursor. 403 if wx.IsBusy(): 404 wx.EndBusyCursor()
405 406
408 """Set the page after the load method choice. 409 410 @return: The index of the next page. 411 @rtype: int 412 """ 413 414 # Go to the sequence.read page. 415 if self.page_method.selection == 'sequence': 416 return self.page_indices['sequence.read'] 417 418 # Go to the structure.read_pdb page. 419 elif self.page_method.selection == 'new pdb': 420 return self.page_indices['structure.read_pdb'] 421 422 # Go to the structure.read_xyz page. 423 elif self.page_method.selection == 'new xyz': 424 return self.page_indices['structure.read_xyz'] 425 426 # Go to the spectrum.read_spins page. 427 elif self.page_method.selection == 'new spectrum': 428 return self.page_indices['spectrum.read_spins'] 429 430 # Skip to the structure.load_spins page. 431 elif self.page_method.selection == 'preload': 432 return self.page_indices['structure.load_spins']
433 434
436 """Set the page after the sequence.read user function page. 437 438 @return: The index of the last page. 439 @rtype: int 440 """ 441 442 # Return the index of the terminal page. 443 return self.page_indices['fin']
444 445
447 """Set the page after the structure.read_* user function pages. 448 449 @return: The index of the structure.load_spins page. 450 @rtype: int 451 """ 452 453 # Return the index of the terminal page. 454 return self.page_indices['structure.load_spins']
455 456 457
458 -class Finish_page(Wiz_page):
459 """The terminating wizard page.""" 460 461 # Class variables. 462 image_path = WIZARD_IMAGE_PATH + 'spin.png' 463 main_text = 'The spin systems should now have been loaded into the relax data store.' 464 title = 'Spin loading complete' 465
466 - def add_contents(self, sizer):
467 """This blank method is needed so that the page shows and does nothing. 468 469 @param sizer: A sizer object. 470 @type sizer: wx.Sizer instance 471 """
472 473 474
475 -class Load_method_page(Wiz_page):
476 """The wizard page for specifying how to load spins.""" 477 478 # Class variables. 479 image_path = WIZARD_IMAGE_PATH + 'spin.png' 480 main_text = 'Select the method for loading spins into relax. Two options are possible: the first is to read sequence information out of a text file via the sequence.read user function; the second is to read in a 3D structure file via the structure.read_pdb user function and then to load the spins from this structure using the structure.load_spins user function.' 481 title = 'Spin loading' 482 483
484 - def add_contents(self, sizer):
485 """Add the specific GUI elements. 486 487 @param sizer: A sizer object. 488 @type sizer: wx.Sizer instance 489 """ 490 491 # Intro text. 492 msg = "Please specify by which method spins should be loaded into the relax data store:" 493 text = wx.StaticText(self, -1, msg) 494 text.Wrap(self._main_size) 495 sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 496 497 # Spacing. 498 sizer.AddStretchSpacer() 499 500 # A box sizer for placing the box sizer in. 501 sizer2 = wx.BoxSizer(wx.HORIZONTAL) 502 sizer.Add(sizer2, 1, wx.ALL|wx.EXPAND, 0) 503 504 # Bottom spacing. 505 sizer.AddStretchSpacer() 506 507 # A bit of indentation. 508 sizer2.AddStretchSpacer() 509 510 # A vertical sizer for the radio buttons. 511 sizer_radio = wx.BoxSizer(wx.VERTICAL) 512 sizer2.Add(sizer_radio, 1, wx.ALL|wx.EXPAND, 0) 513 514 # Pre-loaded structure exists. 515 self.preload_flag = False 516 if hasattr(cdp, 'structure') and not cdp.structure.empty(): 517 self.preload_flag = True 518 519 # The pre-load radio button. 520 if self.preload_flag: 521 # The button. 522 self.radio_preload = wx.RadioButton(self, -1, "From a pre-loaded structure.", style=wx.RB_GROUP) 523 sizer_radio.Add(self.radio_preload, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 524 525 # Spacing. 526 sizer_radio.AddSpacer(20) 527 528 # The sequence radio button. 529 if self.preload_flag: 530 style = 0 531 else: 532 style = wx.RB_GROUP 533 self.radio_seq = wx.RadioButton(self, -1, "From a file containing sequence data.", style=style) 534 sizer_radio.Add(self.radio_seq, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 535 536 # Spacing. 537 sizer_radio.AddSpacer(20) 538 539 # The PDB radio button. 540 self.radio_new_pdb = wx.RadioButton(self, -1, "From a new PDB structure file.") 541 sizer_radio.Add(self.radio_new_pdb, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 542 543 # Spacing. 544 sizer_radio.AddSpacer(20) 545 546 # The XYZ radio button. 547 self.radio_new_xyz = wx.RadioButton(self, -1, "From a new XYZ structure file.") 548 sizer_radio.Add(self.radio_new_xyz, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 549 550 # Spacing. 551 sizer_radio.AddSpacer(20) 552 553 # The spectrum.read_spins radio button. 554 self.radio_new_spectrum = wx.RadioButton(self, -1, "From a peak list or spectrum formatted file.") 555 sizer_radio.Add(self.radio_new_spectrum, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 556 557 # Bind the buttons. 558 self.Bind(wx.EVT_RADIOBUTTON, self._on_select, self.radio_seq) 559 self.Bind(wx.EVT_RADIOBUTTON, self._on_select, self.radio_new_pdb) 560 self.Bind(wx.EVT_RADIOBUTTON, self._on_select, self.radio_new_xyz) 561 self.Bind(wx.EVT_RADIOBUTTON, self._on_select, self.radio_new_spectrum) 562 if self.preload_flag: 563 self.Bind(wx.EVT_RADIOBUTTON, self._on_select, self.radio_preload) 564 565 # Right side spacing. 566 sizer2.AddStretchSpacer(3) 567 568 # Bottom spacing. 569 sizer.AddStretchSpacer() 570 571 # Set the default selection. 572 if self.preload_flag: 573 self.selection = 'preload' 574 else: 575 self.selection = 'sequence'
576 577
578 - def _on_select(self, event=None):
579 """Handle the radio button switching. 580 581 @keyword event: The wx event. 582 @type event: wx event 583 """ 584 585 # The button. 586 button = event.GetEventObject() 587 588 # RMSD. 589 if button == self.radio_seq: 590 self.selection = 'sequence' 591 elif button == self.radio_new_pdb: 592 self.selection = 'new pdb' 593 elif button == self.radio_new_xyz: 594 self.selection = 'new xyz' 595 elif button == self.radio_new_spectrum: 596 self.selection = 'new spectrum' 597 elif self.preload_flag and button == self.radio_preload: 598 self.selection = 'preload'
599