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