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