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