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