"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/spin_viewer/frame.py" (2 Dec 2019, 20958 Bytes) of package /linux/privat/relax-5.0.0.src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "frame.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.3_vs_5.0.0.

    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 
  414     def wizard_page_after_load_method(self):
  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 
  442     def wizard_page_after_sequence_read(self):
  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 
  453     def wizard_page_after_structure_read(self):
  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|wx.ALIGN_CENTER_VERTICAL, 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|wx.ALIGN_CENTER_VERTICAL, 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|wx.ALIGN_CENTER_VERTICAL, 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|wx.ALIGN_CENTER_VERTICAL, 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|wx.ALIGN_CENTER_VERTICAL, 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'