"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/analyses/auto_model_free.py" (2 Dec 2019, 47076 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 "auto_model_free.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) 2009-2010 Michael Bieri                                       #
    4 # Copyright (C) 2009-2013,2015,2019 Edward d'Auvergne                         #
    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 """Module for the automatic model-free protocol frame."""
   25 
   26 # Python module imports.
   27 from math import ceil
   28 from os import sep
   29 import wx
   30 import wx.lib.buttons
   31 import wx.lib.mixins.listctrl
   32 
   33 # relax module imports.
   34 import dep_check
   35 from auto_analyses import dauvergne_protocol
   36 from data_store import Relax_data_store; ds = Relax_data_store()
   37 from graphics import ANALYSIS_IMAGE_PATH, IMAGE_PATH, fetch_icon
   38 from gui.about import About_base
   39 from gui.analyses.base import Base_analysis
   40 from gui.analyses.elements.spin_element import Spin_ctrl
   41 from gui.analyses.elements.text_element import Text_ctrl
   42 from gui.analyses.execute import Execute
   43 from gui.analyses.elements.model_list import Model_list
   44 from gui.base_classes import Container
   45 from gui.components.relax_data import Relax_data_list
   46 from gui.filedialog import RelaxDirDialog
   47 from gui.fonts import font
   48 from gui.message import error_message, Missing_data
   49 from gui.misc import add_border, bitmap_setup
   50 from gui.string_conv import gui_to_int, gui_to_str, str_to_gui
   51 from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
   52 from gui.wizards.wiz_objects import Wiz_window
   53 from lib.physical_constants import NH_BOND_LENGTH
   54 from lib.errors import RelaxError
   55 from lib.text.gui import local_tm, rex, s2, s2f, te, tf, tm, ts
   56 from lib.text.string import LIST, PARAGRAPH, SECTION, SUBSECTION, TITLE
   57 from pipe_control.interatomic import interatomic_loop
   58 from pipe_control.mol_res_spin import exists_mol_res_spin_data, return_spin, spin_loop
   59 from pipe_control.pipes import has_bundle, has_pipe
   60 from specific_analyses.api import return_api
   61 from status import Status; status = Status()
   62 
   63 
   64 class About_window(About_base):
   65     """The model-free about window."""
   66 
   67     # The relax background colour.
   68     colour1 = '#e5feff'
   69     colour2 = '#88cbff'
   70 
   71     # Dimensions.
   72     dim_x = 800
   73     dim_y = 800
   74     max_y = 2500
   75 
   76     # Spacer size (px).
   77     border = 10
   78 
   79     # Window style.
   80     style = wx.DEFAULT_DIALOG_STYLE
   81 
   82     # Destroy on clicking.
   83     DESTROY_ON_CLICK = False
   84 
   85     def __init__(self, parent):
   86         """Set up the user function class."""
   87 
   88         # Execute the base class method.
   89         super(About_window, self).__init__(parent, id=-1, title="Automatic model-free analysis about window")
   90 
   91 
   92     def build_widget(self):
   93         """Build the dialog using the dauvergne_protocol docstring."""
   94 
   95         # A global Y offset for packing the elements together (initialise to the border position).
   96         self.offset(self.border)
   97 
   98         # Loop over the lines.
   99         for i in range(len(dauvergne_protocol.doc)):
  100             # The level and text.
  101             level, text = dauvergne_protocol.doc[i]
  102 
  103             # The title.
  104             if level == TITLE:
  105                 self.draw_title(text, alt_font=font.roman_font_18)
  106 
  107             # The section.
  108             elif level == SECTION:
  109                 self.draw_title(text, alt_font=font.roman_font_14)
  110 
  111             # The section.
  112             elif level == SUBSECTION:
  113                 self.draw_title(text, alt_font=font.roman_font_12)
  114 
  115             # Paragraphs.
  116             elif level == PARAGRAPH:
  117                 self.draw_wrapped_text(text)
  118 
  119             # Lists.
  120             elif level == LIST:
  121                 # Start of list.
  122                 if i and dauvergne_protocol.doc[i-1][0] != LIST:
  123                     self.offset(10)
  124 
  125                 # The text.
  126                 self.draw_wrapped_text("    - %s" % text)
  127 
  128                 # End of list.
  129                 if i < len(dauvergne_protocol.doc) and dauvergne_protocol.doc[i+1][0] == PARAGRAPH:
  130                     self.offset(10)
  131 
  132         # Add space to the bottom.
  133         self.offset(self.border)
  134 
  135         # Round the offset up to the nearest factor of the scroll inc (needed for all scrolling).
  136         scroll_x, scroll_y = self.window.GetScrollPixelsPerUnit()
  137         y = self.offset()
  138         self.offset(-y)
  139         y = int(ceil(y/float(scroll_y)) * scroll_y)
  140         self.offset(y)
  141 
  142         # Resize the window.
  143         dim_x = self.dim_x
  144         virt_y = self.offset()
  145         self.SetSize((dim_x, self.dim_y))
  146         self.window.SetVirtualSize((dim_x, virt_y))
  147         self.window.EnableScrolling(False, True)
  148 
  149 
  150 
  151 class Auto_model_free(Base_analysis):
  152     """The model-free auto-analysis GUI element."""
  153 
  154     def __init__(self, parent, id=-1, pos=wx.Point(-1, -1), size=wx.Size(-1, -1), style=524288, name='scrolledpanel', gui=None, analysis_name=None, pipe_name=None, pipe_bundle=None, uf_exec=[], data_index=None):
  155         """Build the automatic model-free protocol GUI element.
  156 
  157         @param parent:          The parent wx element.
  158         @type parent:           wx object
  159         @keyword id:            The unique ID number.
  160         @type id:               int
  161         @keyword pos:           The position.
  162         @type pos:              wx.Size object
  163         @keyword size:          The size.
  164         @type size:             wx.Size object
  165         @keyword style:         The style.
  166         @type style:            int
  167         @keyword name:          The name for the panel.
  168         @type name:             unicode
  169         @keyword gui:           The main GUI class.
  170         @type gui:              gui.relax_gui.Main instance
  171         @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook).
  172         @type analysis_name:    str
  173         @keyword pipe_name:     The name of the original data pipe for this analysis.
  174         @type pipe_name:        str
  175         @keyword pipe_bundle:   The name of the data pipe bundle associated with this analysis.
  176         @type pipe_bundle:      str
  177         @keyword uf_exec:       The list of user function on_execute methods returned from the new analysis wizard.
  178         @type uf_exec:          list of methods
  179         @keyword data_index:    The index of the analysis in the relax data store (set to None if no data currently exists).
  180         @type data_index:       None or int
  181         """
  182 
  183         # Store the GUI main class.
  184         self.gui = gui
  185 
  186         # Init.
  187         self.init_flag = True
  188 
  189         # New data container.
  190         if data_index == None:
  191             # First create the data pipe if not already in existence.
  192             if not has_pipe(pipe_name):
  193                 self.gui.interpreter.apply('pipe.create', pipe_name=pipe_name, pipe_type='mf', bundle=pipe_bundle)
  194 
  195             # Create the data pipe bundle if needed.
  196             if not has_bundle(pipe_bundle):
  197                 self.gui.interpreter.apply('pipe.bundle', bundle=pipe_bundle, pipe=pipe_name)
  198 
  199             # Generate a storage container in the relax data store, and alias it for easy access.
  200             data_index = ds.relax_gui.analyses.add('model-free')
  201 
  202             # Store the analysis and pipe names.
  203             ds.relax_gui.analyses[data_index].analysis_name = analysis_name
  204             ds.relax_gui.analyses[data_index].pipe_name = pipe_name
  205             ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle
  206 
  207             # Initialise the variables.
  208             ds.relax_gui.analyses[data_index].grid_inc = None
  209             ds.relax_gui.analyses[data_index].diff_tensor_grid_inc = {'sphere': 11, 'prolate': 11, 'oblate': 11, 'ellipsoid': 6}
  210             ds.relax_gui.analyses[data_index].mc_sim_num = None
  211             ds.relax_gui.analyses[data_index].save_dir = self.gui.system_cwd_path
  212             ds.relax_gui.analyses[data_index].local_tm_models = ['tm0', 'tm1', 'tm2', 'tm3', 'tm4', 'tm5', 'tm6', 'tm7', 'tm8', 'tm9']
  213             ds.relax_gui.analyses[data_index].mf_models = ['m0', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8', 'm9']
  214             ds.relax_gui.analyses[data_index].max_iter = 30
  215 
  216         # Error checking.
  217         if ds.relax_gui.analyses[data_index].pipe_bundle == None:
  218             raise RelaxError("The pipe bundle must be supplied.")
  219 
  220         # Alias the data.
  221         self.data = ds.relax_gui.analyses[data_index]
  222         self.data_index = data_index
  223 
  224         # Backward compatibility.
  225         if not hasattr(self.data, 'local_tm_models'):
  226             self.data.local_tm_models = ['tm0', 'tm1', 'tm2', 'tm3', 'tm4', 'tm5', 'tm6', 'tm7', 'tm8', 'tm9']
  227         if not hasattr(self.data, 'mf_models'):
  228             self.data.mf_models = ['m0', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8', 'm9']
  229 
  230         # Initialise the mode selection window.
  231         self.mode_win = Protocol_mode_sel_window()
  232 
  233         # Register the method for updating the spin count for the completion of user functions.
  234         self.observer_register()
  235 
  236         # Execute the base class method to build the panel.
  237         super(Auto_model_free, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)
  238 
  239 
  240     def _about(self, event=None):
  241         """The about window.
  242 
  243         @keyword event: The wx event.
  244         @type event:    wx event
  245         """
  246 
  247         # Initialise the dialog.
  248         self.about_dialog = About_window(self)
  249 
  250         # Show the dialog.
  251         if status.show_gui:
  252             self.about_dialog.Show()
  253 
  254 
  255     def activate(self):
  256         """Activate or deactivate certain elements of the analysis in response to the execution lock."""
  257 
  258         # Flag for enabling or disabling the elements.
  259         enable = False
  260         if not status.exec_lock.locked():
  261             enable = True
  262 
  263         # Activate or deactivate the elements.
  264         wx.CallAfter(self.field_results_dir.Enable, enable)
  265         wx.CallAfter(self.spin_systems.Enable, enable)
  266         wx.CallAfter(self.relax_data.Enable, enable)
  267         wx.CallAfter(self.button_dipole_pair.Enable, enable)
  268         wx.CallAfter(self.button_csa.Enable, enable)
  269         wx.CallAfter(self.button_isotope_heteronuc.Enable, enable)
  270         wx.CallAfter(self.button_isotope_proton.Enable, enable)
  271         wx.CallAfter(self.local_tm_model_field.Enable, enable)
  272         wx.CallAfter(self.mf_model_field.Enable, enable)
  273         wx.CallAfter(self.grid_inc.Enable, enable)
  274         wx.CallAfter(self.mc_sim_num.Enable, enable)
  275         wx.CallAfter(self.max_iter.Enable, enable)
  276         wx.CallAfter(self.mode.Enable, enable)
  277         wx.CallAfter(self.button_exec_relax.Enable, enable)
  278 
  279 
  280     def add_values(self, box):
  281         """Create and add the value.set buttons for the model-free analysis.
  282 
  283         @param box:     The box element to pack the GUI element into.
  284         @type box:      wx.BoxSizer instance
  285         """
  286 
  287         # Sizer.
  288         sizer = wx.BoxSizer(wx.HORIZONTAL)
  289 
  290         # Dipole-dipole relaxation setup button.
  291         self.button_dipole_pair = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Dipolar relaxation")
  292         self.button_dipole_pair.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.dipole_pair", "22x22"), wx.BITMAP_TYPE_ANY))
  293         self.button_dipole_pair.SetFont(font.normal)
  294         self.button_dipole_pair.SetSize((-1, 25))
  295         self.button_dipole_pair.SetToolTip(wx.ToolTip("Define the magnetic dipole-dipole relaxation mechanism."))
  296         self.gui.Bind(wx.EVT_BUTTON, self.setup_dipole_pair, self.button_dipole_pair)
  297         sizer.Add(self.button_dipole_pair, 1, wx.ALL|wx.EXPAND, 0)
  298 
  299         # CSA button.
  300         self.button_csa = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " CSA relaxation")
  301         self.button_csa.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.align_tensor", "22x22"), wx.BITMAP_TYPE_ANY))
  302         self.button_csa.SetFont(font.normal)
  303         self.button_csa.SetSize((-1, 25))
  304         self.button_csa.SetToolTip(wx.ToolTip("Define the Chemical Shift Anisotropy (CSA) relaxation mechanism via the value.set user function."))
  305         self.gui.Bind(wx.EVT_BUTTON, self.value_set_csa, self.button_csa)
  306         sizer.Add(self.button_csa, 1, wx.ALL|wx.EXPAND, 0)
  307 
  308         # Isotope type button (heteronucleus).
  309         self.button_isotope_heteronuc = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " X isotope")
  310         self.button_isotope_heteronuc.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.nuclear_symbol", "22x22"), wx.BITMAP_TYPE_ANY))
  311         self.button_isotope_heteronuc.SetFont(font.normal)
  312         self.button_isotope_heteronuc.SetSize((-1, 25))
  313         self.button_isotope_heteronuc.SetToolTip(wx.ToolTip("Set the nuclear isotope types of the heteronuclear spins via the spin.isotope user function."))
  314         self.gui.Bind(wx.EVT_BUTTON, self.spin_isotope_heteronuc, self.button_isotope_heteronuc)
  315         sizer.Add(self.button_isotope_heteronuc, 1, wx.ALL|wx.EXPAND, 0)
  316 
  317         # Isotope type button (proton).
  318         self.button_isotope_proton = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " H isotope")
  319         self.button_isotope_proton.SetBitmapLabel(wx.Bitmap(fetch_icon("relax.nuclear_symbol", "22x22"), wx.BITMAP_TYPE_ANY))
  320         self.button_isotope_proton.SetFont(font.normal)
  321         self.button_isotope_proton.SetSize((-1, 25))
  322         self.button_isotope_proton.SetToolTip(wx.ToolTip("Set the nuclear isotope types of the proton spins via the spin.isotope user function."))
  323         self.gui.Bind(wx.EVT_BUTTON, self.spin_isotope_proton, self.button_isotope_proton)
  324         sizer.Add(self.button_isotope_proton, 1, wx.ALL|wx.EXPAND, 0)
  325 
  326         # Add the element to the box.
  327         box.Add(sizer, 0, wx.ALL|wx.EXPAND, 0)
  328 
  329 
  330     def assemble_data(self):
  331         """Assemble the data required for the auto-analysis.
  332 
  333         See the docstring for auto_analyses.dauvernge_protocol for details.  All data is taken from the relax data store, so data upload from the GUI to there must have been previously performed.
  334 
  335         @return:    A container with all the data required for the auto-analysis.
  336         @rtype:     class instance, list of str
  337         """
  338 
  339         # The data container.
  340         data = Container()
  341         missing = []
  342 
  343         # The pipe name and bundle.
  344         data.pipe_name = self.data.pipe_name
  345         data.pipe_bundle = self.data.pipe_bundle
  346 
  347         # The model-free models (do not change these unless absolutely necessary).
  348         data.local_tm_models = self.local_tm_model_field.GetValue()
  349         data.mf_models = self.mf_model_field.GetValue()
  350 
  351         # Automatic looping over all rounds until convergence (must be a boolean value of True or False).
  352         data.conv_loop = True
  353 
  354         # Increment size.
  355         data.inc = gui_to_int(self.grid_inc.GetValue())
  356         if hasattr(self.data, 'diff_tensor_grid_inc'):
  357             data.diff_tensor_grid_inc = self.data.diff_tensor_grid_inc
  358         else:
  359             data.diff_tensor_grid_inc = {'sphere': 11, 'prolate': 11, 'oblate': 11, 'ellipsoid': 6}
  360 
  361         # The number of Monte Carlo simulations to be used for error analysis at the end of the analysis.
  362         data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
  363 
  364         # Number of maximum iterations.
  365         data.max_iter = self.data.max_iter
  366 
  367         # Results directory.
  368         data.save_dir = self.data.save_dir
  369 
  370         # Check if sequence data is loaded
  371         if not exists_mol_res_spin_data():
  372             missing.append("Sequence data")
  373 
  374         # Relaxation data.
  375         if not hasattr(cdp, 'ri_ids') or len(cdp.ri_ids) == 0:
  376             missing.append("Relaxation data")
  377 
  378         # Insufficient data.
  379         if hasattr(cdp, 'ri_ids') and len(cdp.ri_ids) <= 3:
  380             missing.append("Insufficient relaxation data, 4 or more data sets are essential for the execution of the dauvergne_protocol auto-analysis. Check that you have entered data for a least two spectrometer fields.")
  381 
  382         # Interatomic data containers.
  383         if not hasattr(cdp, 'interatomic') or len(cdp.interatomic) == 0:
  384             missing.append("Interatomic data (for the dipole-dipole interaction)")
  385 
  386         # Get the mode.
  387         mode = gui_to_str(self.mode.GetValue())
  388 
  389         # Solve for all global models.
  390         if mode == 'Fully automated':
  391             # The global model list.
  392             data.global_models = ['local_tm', 'sphere', 'prolate', 'oblate', 'ellipsoid', 'final']
  393 
  394         # Any global model selected.
  395         else:
  396             data.global_models = [mode]
  397 
  398         # Check for vectors.
  399         vector_check = False
  400         if 'prolate' in data.global_models or 'oblate' in data.global_models or 'ellipsoid' in data.global_models:
  401             vector_check = True
  402 
  403         # Spin variables.
  404         for spin, spin_id in spin_loop(return_id=True):
  405             # Skip deselected spins.
  406             if not spin.select:
  407                 continue
  408 
  409             # The message skeleton.
  410             msg = "Spin '%s' - %s (try the %s user function)." % (spin_id, "%s", "%s")
  411 
  412             # Test if the nuclear isotope type has been set.
  413             if not hasattr(spin, 'isotope') or spin.isotope == None:
  414                 missing.append(msg % ("nuclear isotope data", "spin.isotope"))
  415 
  416             # Test if the CSA value has been set for the heteronuclei.
  417             if (hasattr(spin, 'isotope') and spin.isotope in ['15N', '13C']) and (not hasattr(spin, 'csa') or spin.csa == None):
  418                 missing.append(msg % ("CSA data", "value.set"))
  419 
  420         # Interatomic data container variables.
  421         for interatom in interatomic_loop():
  422             # Get the spin containers.
  423             spin1 = return_spin(spin_hash=interatom._spin_hash1)
  424             spin2 = return_spin(spin_hash=interatom._spin_hash2)
  425 
  426             # Skip deselected spins.
  427             if not spin1.select:
  428                 continue
  429             if not spin2.select:
  430                 continue
  431 
  432             # The message skeleton.
  433             msg = "Spin pair '%s' and '%s' - %s (try the %s user function)." % (interatom.spin_id1, interatom.spin_id2, "%s", "%s")
  434 
  435             # Test if the interatomic distance has been set.
  436             if not hasattr(interatom, 'r') or interatom.r == None:
  437                 missing.append(msg % ("bond length data", "value.set"))
  438 
  439             # Test if the unit vectors have been loaded.
  440             if vector_check and (not hasattr(interatom, 'vector') or interatom.vector is None):
  441                 missing.append(msg % ("unit vectors", "interatom.unit_vectors"))
  442 
  443         # Return the container and list of missing data.
  444         return data, missing
  445 
  446 
  447     def build_left_box(self):
  448         """Construct the left hand box to pack into the main model-free box.
  449 
  450         @return:    The left hand box element containing the bitmap and about button to pack into the main model-free box.
  451         @rtype:     wx.BoxSizer instance
  452         """
  453 
  454         # Build the left hand box.
  455         left_box = wx.BoxSizer(wx.VERTICAL)
  456 
  457         # The images.
  458         bitmaps = [ANALYSIS_IMAGE_PATH+"model_free"+sep+"model_free_200x200.png",
  459                    IMAGE_PATH+'modelfree.png']
  460 
  461         # Add the model-free bitmap picture.
  462         for i in range(len(bitmaps)):
  463             # The bitmap.
  464             bitmap = wx.StaticBitmap(self, -1, bitmap_setup(bitmaps[i]))
  465 
  466             # Add it.
  467             left_box.Add(bitmap, 0, wx.ALL, 0)
  468 
  469         # A spacer.
  470         left_box.AddStretchSpacer()
  471 
  472         # A button sizer, with some initial spacing.
  473         button_sizer = wx.BoxSizer(wx.HORIZONTAL)
  474 
  475         # An about button.
  476         button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, "About")
  477         button.SetBitmapLabel(wx.Bitmap(fetch_icon('oxygen.actions.help-about', "22x22"), wx.BITMAP_TYPE_ANY))
  478         button.SetFont(font.normal)
  479         button.SetToolTip(wx.ToolTip("Information about this automatic analysis"))
  480 
  481         # Bind the click.
  482         self.Bind(wx.EVT_BUTTON, self._about, button)
  483 
  484         # A cursor for the button.
  485         if dep_check.wx_classic:
  486             cursor = wx.StockCursor(wx.CURSOR_QUESTION_ARROW)
  487         else:
  488             cursor = wx.Cursor(wx.CURSOR_QUESTION_ARROW)
  489         button.SetCursor(cursor)
  490 
  491         # Pack the button.
  492         button_sizer.Add(button, 0, 0, 0)
  493         left_box.Add(button_sizer, 0, wx.ALL, 0)
  494 
  495         # Return the packed box.
  496         return left_box
  497 
  498 
  499     def build_right_box(self):
  500         """Construct the right hand box to pack into the main model-free box.
  501 
  502         @return:    The right hand box element containing all model-free GUI elements (excluding the bitmap) to pack into the main model-free box.
  503         @rtype:     wx.BoxSizer instance
  504         """
  505 
  506         # Use a vertical packing of elements.
  507         box = wx.BoxSizer(wx.VERTICAL)
  508 
  509         # Add the frame title.
  510         self.add_title(box, "Model-free analysis")
  511 
  512         # Display the data pipe.
  513         Text_ctrl(box, self, text="The data pipe bundle:", default=self.data.pipe_bundle, tooltip="This is the data pipe bundle associated with this analysis.", editable=False, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
  514 
  515         # Add the results directory GUI element.
  516         self.field_results_dir = Text_ctrl(box, self, text="Results directory:", icon=fetch_icon('oxygen.actions.document-open-folder', "16x16"), default=self.data.save_dir, tooltip="The directory in which all automatically created files will be saved.", tooltip_button="Select the results directory.", fn=self.results_directory, button=True, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
  517 
  518         # Add the spin GUI element.
  519         self.add_spin_systems(box, self)
  520 
  521         # Add the relaxation data list GUI element, with spacing.
  522         box.AddSpacer(10)
  523         self.relax_data = Relax_data_list(gui=self.gui, parent=self, box=box, id=str(self.data_index))
  524         box.AddSpacer(10)
  525 
  526         # Add the value.set buttons.
  527         self.add_values(box)
  528         box.AddSpacer(10)
  529 
  530         # Add the local tau_m models GUI element, with spacing.
  531         self.local_tm_model_field = Local_tm_list(self, box)
  532         self.local_tm_model_field.set_value(self.data.local_tm_models)
  533 
  534         # Add the model-free models GUI element, with spacing.
  535         self.mf_model_field = Mf_list(self, box)
  536         self.mf_model_field.set_value(self.data.mf_models)
  537 
  538         # The optimisation settings.
  539         self.grid_inc = Spin_ctrl(box, self, text="Grid search increments:", default=11, min=1, max=100, tooltip="This is the number of increments per dimension of the grid search performed prior to numerical optimisation.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
  540         self.mc_sim_num = Spin_ctrl(box, self, text="Monte Carlo simulation number:", default=500, min=1, max=100000, tooltip="This is the number of Monte Carlo simulations performed for error propagation and analysis.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
  541 
  542         # Add maximum iteration selector.
  543         self.max_iter = Spin_ctrl(box, self, text="Maximum iterations:", default=self.data.max_iter, tooltip="The maximum number of iterations for the protocol.  This is the limit for the global looping over the optimisation of the model-free models, model elimination, model selection and then optimisation of the diffusion tensor.", min=25, max=100, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
  544 
  545         # The calculation mode.
  546         self.mode = Text_ctrl(box, self, text="Protocol mode:", default='Fully automated', tooltip="Select if the dauvergne_protocol analysis will be fully automated or whether the individual global models will be optimised separately.", tooltip_button="Open the protocol mode selection window.", icon=fetch_icon('oxygen.actions.system-run', "16x16"), fn=self.mode_dialog, editable=False, button=True, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal)
  547 
  548         # Stretchable spacing (with a minimal space).
  549         box.AddSpacer(30)
  550         box.AddStretchSpacer()
  551 
  552         # Add the execution GUI element.
  553         self.button_exec_relax = self.add_execute_analysis(box, self.execute)
  554 
  555         # Return the box.
  556         return box
  557 
  558 
  559     def delete(self):
  560         """Unregister the spin count from the user functions."""
  561 
  562         # Unregister the observer methods.
  563         self.observer_register(remove=True)
  564 
  565         # Clean up the relaxation data list object.
  566         self.relax_data.delete()
  567 
  568         # Destroy the dipole-dipole interaction wizard.
  569         if hasattr(self, 'dipole_wizard'):
  570             self.dipole_wizard.Destroy()
  571             del self.dipole_wizard
  572 
  573         # Destroy the mode selection window.
  574         self.mode_win.Destroy()
  575         del self.mode_win
  576 
  577         # Destroy the model list windows.
  578         self.local_tm_model_field.model_win.Destroy()
  579         del self.local_tm_model_field
  580         self.mf_model_field.model_win.Destroy()
  581         del self.mf_model_field
  582 
  583         # Destroy the missing data dialog, if present.
  584         if hasattr(self, 'missing_data'):
  585             self.missing_data.Destroy()
  586             del self.missing_data
  587 
  588 
  589     def execute(self, event=None):
  590         """Set up, execute, and process the automatic model-free protocol.
  591 
  592         @keyword event: The wx event.
  593         @type event:    wx event
  594         """
  595 
  596         # Flush the GUI interpreter internal queue to make sure all user functions are complete.
  597         self.gui.interpreter.flush()
  598 
  599         # relax execution lock.
  600         if status.exec_lock.locked():
  601             error_message("relax is currently executing.", "relax execution lock")
  602             event.Skip()
  603             return
  604 
  605         # User warning to close windows.
  606         self.gui.close_windows()
  607 
  608         # Synchronise the frame data to the relax data store.
  609         self.sync_ds(upload=True)
  610 
  611         # Assemble all the data needed for the auto-analysis.
  612         data, missing = self.assemble_data()
  613 
  614         # Missing data.
  615         if len(missing):
  616             self.missing_data = Missing_data(missing)
  617             return
  618 
  619         # Display the relax controller, and go to the end of the log window.
  620         self.gui.show_controller(None)
  621         self.gui.controller.log_panel.on_goto_end(None)
  622 
  623         # Start the thread.
  624         self.thread = Execute_mf(self.gui, data, self.data_index)
  625         self.thread.start()
  626 
  627         # Terminate the event.
  628         event.Skip()
  629 
  630 
  631     def mode_dialog(self, event=None):
  632         """The calculation mode selection.
  633 
  634         @keyword event: The wx event.
  635         @type event:    wx event
  636         """
  637 
  638         # Show the model selector window.
  639         if status.show_gui:
  640             self.mode_win.ShowModal()
  641 
  642         # Set the model.
  643         self.mode.SetValue(str_to_gui(self.mode_win.select))
  644 
  645 
  646     def observer_register(self, remove=False):
  647         """Register and unregister methods with the observer objects.
  648 
  649         @keyword remove:    If set to True, then the methods will be unregistered.
  650         @type remove:       False
  651         """
  652 
  653         # Register.
  654         if not remove:
  655             status.observers.gui_uf.register(self.data.pipe_bundle, self.update_spin_count, method_name='update_spin_count')
  656             status.observers.exec_lock.register(self.data.pipe_bundle, self.activate, method_name='activate')
  657 
  658         # Unregister.
  659         else:
  660             # The model-free methods.
  661             status.observers.gui_uf.unregister(self.data.pipe_bundle)
  662             status.observers.exec_lock.unregister(self.data.pipe_bundle)
  663 
  664             # The embedded objects methods.
  665             self.relax_data.observer_register(remove=True)
  666 
  667 
  668     def results_directory(self, event=None):
  669         """The results directory selection.
  670 
  671         @keyword event: The wx event.
  672         @type event:    wx event
  673         """
  674 
  675         # The dialog.
  676         dialog = RelaxDirDialog(parent=self, message='Select the results directory', defaultPath=self.field_results_dir.GetValue())
  677 
  678         # Show the dialog and catch if no file has been selected.
  679         if status.show_gui and dialog.ShowModal() != wx.ID_OK:
  680             # Don't do anything.
  681             return
  682 
  683         # The path (don't do anything if not set).
  684         path = gui_to_str(dialog.get_path())
  685         if not path:
  686             return
  687 
  688         # Store the path.
  689         self.data.save_dir = path
  690 
  691         # Place the path in the text box.
  692         self.field_results_dir.SetValue(str_to_gui(path))
  693 
  694 
  695     def setup_dipole_pair(self, event=None):
  696         """Create the wizard for the dipole-dipole interaction.
  697 
  698         @keyword event: The wx event.
  699         @type event:    wx event
  700         """
  701 
  702         # Change the cursor to busy.
  703         wx.BeginBusyCursor()
  704 
  705         # Destroy the dipole-dipole interaction wizard, if it exists.
  706         if hasattr(self, 'dipole_wizard'):
  707             self.dipole_wizard.Destroy()
  708 
  709         # Create the wizard.
  710         self.dipole_wizard = Wiz_window(parent=self.gui, size_x=1000, size_y=750, title="Dipole-dipole interaction setup")
  711 
  712         # Structural data.
  713         if not hasattr(cdp, 'structure'):
  714             # Create the PDB reading page.
  715             page = uf_store['structure.read_pdb'].create_page(self.dipole_wizard, sync=True)
  716             self.dipole_wizard.add_page(page, skip_button=True)
  717 
  718             # Create the position reading page.
  719             page = uf_store['structure.get_pos'].create_page(self.dipole_wizard, sync=True)
  720             self.dipole_wizard.add_page(page, skip_button=True)
  721 
  722         # Create the interatom.define page.
  723         page = uf_store['interatom.define'].create_page(self.dipole_wizard, sync=True)
  724         page.SetValue('spin_id1', '@N')
  725         page.SetValue('spin_id2', '@H')
  726         self.dipole_wizard.add_page(page)
  727 
  728         # Create the interatom.set_dist page.
  729         page = uf_store['interatom.set_dist'].create_page(self.dipole_wizard, sync=True)
  730         page.SetValue('spin_id1', '@N*')
  731         page.SetValue('spin_id2', '@H*')
  732         page.SetValue('ave_dist', NH_BOND_LENGTH)
  733         self.dipole_wizard.add_page(page)
  734 
  735         # Create the interatom.unit_vectors page.
  736         page = uf_store['interatom.unit_vectors'].create_page(self.dipole_wizard, sync=True)
  737         self.dipole_wizard.add_page(page)
  738 
  739         # Reset the cursor.
  740         if wx.IsBusy():
  741             wx.EndBusyCursor()
  742 
  743         # Execute the wizard.
  744         self.dipole_wizard.run()
  745 
  746 
  747     def spin_isotope_heteronuc(self, event=None):
  748         """Set the nuclear isotope types of the heteronuclear spins via the spin.isotope user function.
  749 
  750         @keyword event: The wx event.
  751         @type event:    wx event
  752         """
  753 
  754         # Call the user function.
  755         uf_store['spin.isotope'](isotope='15N', spin_id='@N*')
  756 
  757 
  758     def spin_isotope_proton(self, event=None):
  759         """Set the nuclear isotope types of the proton spins via the spin.isotope user function.
  760 
  761         @keyword event: The wx event.
  762         @type event:    wx event
  763         """
  764 
  765         # Call the user function.
  766         uf_store['spin.isotope'](isotope='1H', spin_id='@H*')
  767 
  768 
  769     def sync_ds(self, upload=False):
  770         """Synchronise the analysis frame and the relax data store, both ways.
  771 
  772         This method allows the frame information to be uploaded into the relax data store, or for the information in the relax data store to be downloaded by the frame.
  773 
  774         @keyword upload:    A flag which if True will cause the frame to send data to the relax data store.  If False, data will be downloaded from the relax data store to update the frame.
  775         @type upload:       bool
  776         """
  777 
  778         # The local tau_m models to use.
  779         if upload:
  780             self.data.local_tm_models = self.local_tm_model_field.GetValue()
  781         else:
  782             self.local_tm_model_field.set_value(self.data.local_tm_models)
  783 
  784         # The model-free models to use.
  785         if upload:
  786             self.data.mf_models = self.mf_model_field.GetValue()
  787         else:
  788             self.mf_model_field.set_value(self.data.mf_models)
  789 
  790         # The grid incs.
  791         if upload:
  792             self.data.grid_inc = gui_to_int(self.grid_inc.GetValue())
  793         elif hasattr(self.data, 'grid_inc'):
  794             self.grid_inc.SetValue(int(self.data.grid_inc))
  795 
  796         # The MC sim number.
  797         if upload:
  798             self.data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue())
  799         elif hasattr(self.data, 'mc_sim_num'):
  800             self.mc_sim_num.SetValue(int(self.data.mc_sim_num))
  801 
  802         # The results directory.
  803         if upload:
  804             self.data.save_dir = str(self.field_results_dir.GetValue())
  805         else:
  806             self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))
  807 
  808         # Maximum iterations.
  809         if upload:
  810             self.data.max_iter = gui_to_int(self.max_iter.GetValue())
  811         else:
  812             self.max_iter.SetValue(int(self.data.max_iter))
  813 
  814 
  815     def value_set_csa(self, event=None):
  816         """Set the CSA via the value.set uf.
  817 
  818         @keyword event: The wx event.
  819         @type event:    wx event
  820         """
  821 
  822         # Get the default value.
  823         api = return_api()
  824         val = api.default_value('csa')
  825 
  826         # Call the user function.
  827         uf_store['value.set'](val=val, param='csa', spin_id='@N*')
  828 
  829 
  830 
  831 class Execute_mf(Execute):
  832     """The model-free analysis execution object."""
  833 
  834     def run_analysis(self):
  835         """Execute the calculation."""
  836 
  837         # Start the protocol.
  838         dauvergne_protocol.dAuvergne_protocol(pipe_name=self.data.pipe_name, pipe_bundle=self.data.pipe_bundle, results_dir=self.data.save_dir, diff_model=self.data.global_models, mf_models=self.data.mf_models, local_tm_models=self.data.local_tm_models, grid_inc=self.data.inc, diff_tensor_grid_inc=self.data.diff_tensor_grid_inc, mc_sim_num=self.data.mc_sim_num, max_iter=self.data.max_iter, conv_loop=self.data.conv_loop)
  839 
  840 
  841 
  842 class Local_tm_list(Model_list):
  843     """The local model-free model list GUI element."""
  844 
  845     # Some class variables.
  846     desc = "Local %s models:" % tm
  847     model_desc = [
  848         "Model m0 with a local molecular correlation time (%s)." % tm,
  849         "Model m1 with a local molecular correlation time (%s)." % tm,
  850         "Model m2 with a local molecular correlation time (%s)." % tm,
  851         "Model m3 with a local molecular correlation time (%s)." % tm,
  852         "Model m4 with a local molecular correlation time (%s)." % tm,
  853         "Model m5 with a local molecular correlation time (%s)." % tm,
  854         "Model m6 with a local molecular correlation time (%s)." % tm,
  855         "Model m7 with a local molecular correlation time (%s)." % tm,
  856         "Model m8 with a local molecular correlation time (%s)." % tm,
  857         "Model m9 with a local molecular correlation time (%s)." % tm
  858     ]
  859     models = [
  860         "tm0",
  861         "tm1",
  862         "tm2",
  863         "tm3",
  864         "tm4",
  865         "tm5",
  866         "tm6",
  867         "tm7",
  868         "tm8",
  869         "tm9"
  870     ]
  871     params = [
  872         "{%s}" % local_tm,
  873         "{%s, %s}" % (local_tm, s2),
  874         "{%s, %s, %s}" % (local_tm, s2, te),
  875         "{%s, %s, %s}" % (local_tm, s2, rex),
  876         "{%s, %s, %s, %s}" % (local_tm, s2, te, rex),
  877         "{%s, %s, %s, %s}" % (local_tm, s2, s2f, ts),
  878         "{%s, %s, %s, %s, %s}" % (local_tm, s2, tf, s2f, ts),
  879         "{%s, %s, %s, %s, %s}" % (local_tm, s2, s2f, ts, rex),
  880         "{%s, %s, %s, %s, %s, %s}" % (local_tm, s2, tf, s2f, ts, rex),
  881         "{%s, %s}" % (local_tm, rex)
  882     ]
  883     warning = "The model-free models used in dauvergne_protocol auto-analysis should almost never be changed!  The consequences will be unpredictable.  Please proceed only if you are sure of what you are doing.  Would you like to modify the model-free model list?"
  884     red_flag = True
  885     size = wx.Size(680, 350)
  886     tooltip = "The list model-free models with the %s parameter to optimise as the first step of the protocol (see the about window for details).  This really should not be changed." % local_tm
  887     tooltip_button = "Open the model list selector window."
  888 
  889 
  890 
  891 class Mf_list(Model_list):
  892     """The model-free model list GUI element."""
  893 
  894     # Some class variables.
  895     desc = "Model-free models:"
  896     model_desc = [
  897         "No statistically significant internal motions.",
  898         "The original model with a statistically insignificant %s." % te,
  899         "The original Lipari and Szabo model.",
  900         "The original model with chemical exchange relaxation but a statistically insignificant %s." % te,
  901         "The original model with chemical exchange relaxation.",
  902         "The extended model with a statistically insignificant %s." % tf,
  903         "The Clore et al., 1991 extended model-free model.",
  904         "The extended model with chemical exchange relaxation but a statistically insignificant %s." % tf,
  905         "The extended model with chemical exchange relaxation.",
  906         "No statistically significant internal motions but chemical exchange relaxation present."
  907     ]
  908     models = [
  909         "m0",
  910         "m1",
  911         "m2",
  912         "m3",
  913         "m4",
  914         "m5",
  915         "m6",
  916         "m7",
  917         "m8",
  918         "m9"
  919     ]
  920     params = [
  921         "{}",
  922         "{%s}" % s2,
  923         "{%s, %s}" % (s2, te),
  924         "{%s, %s}" % (s2, rex),
  925         "{%s, %s, %s}" % (s2, te, rex),
  926         "{%s, %s, %s}" % (s2, s2f, ts),
  927         "{%s, %s, %s, %s}" % (s2, tf, s2f, ts),
  928         "{%s, %s, %s, %s}" % (s2, s2f, ts, rex),
  929         "{%s, %s, %s, %s, %s}" % (s2, tf, s2f, ts, rex),
  930         "{%s}" % rex
  931     ]
  932     warning = "The model-free models used in dauvergne_protocol auto-analysis should almost never be changed!  The consequences will be unpredictable.  Please proceed only if you are sure of what you are doing.  Would you like to modify the model-free model list?"
  933     red_flag = True
  934     size = wx.Size(850, 350)
  935     tooltip = "The list model-free models to optimise as the iterative part of the protocol (see the about window for details).  This really should not be changed."
  936     tooltip_button = "Open the model list selector window."
  937 
  938 
  939 
  940 class Protocol_mode_sel_window(wx.Dialog):
  941     """The protocol mode selector window object."""
  942 
  943     def __init__(self):
  944         """Set up the window."""
  945 
  946         # Set up the dialog.
  947         wx.Dialog.__init__(self, None, id=-1, title="Protocol mode selection")
  948 
  949         # Initialise some values
  950         size_x = 600
  951         size_y = 600
  952         border = 10
  953         self.select = 'Fully automated'
  954 
  955         # Set the frame properties.
  956         self.SetSize((size_x, size_y))
  957         self.Centre()
  958         self.SetFont(font.normal)
  959 
  960         # The main box sizer.
  961         main_sizer = wx.BoxSizer(wx.VERTICAL)
  962 
  963         # Pack the sizer into the frame.
  964         self.SetSizer(main_sizer)
  965 
  966         # Build the central sizer, with borders.
  967         sizer = add_border(main_sizer, border=border, packing=wx.HORIZONTAL)
  968 
  969         # Build the automatic part.
  970         self.build_auto(sizer)
  971 
  972         # Line separator.
  973         sizer.Add(wx.StaticLine(self, -1, style=wx.LI_VERTICAL), 0, wx.EXPAND|wx.ALL, border)
  974 
  975         # Build the manual part.
  976         self.build_manual(sizer)
  977 
  978 
  979     def build_auto(self, sizer):
  980         """Build the fully automated part of the window.
  981 
  982         @param sizer:   The sizer to pack the elements into.
  983         @type sizer:    wx.BoxSizer instance
  984         """
  985 
  986         # Create a vertical sizer for the elements.
  987         sub_sizer = wx.BoxSizer(wx.VERTICAL)
  988 
  989         # The title.
  990         title = wx.StaticText(self, -1, "Fully automated")
  991         title.SetFont(font.subtitle)
  992         sub_sizer.Add(title, 0, wx.ALIGN_CENTRE_HORIZONTAL, 0)
  993 
  994         # Spacing.
  995         sub_sizer.AddStretchSpacer()
  996 
  997         # The button.
  998         button = wx.BitmapButton(self, -1, wx.Bitmap(fetch_icon('oxygen.actions.go-bottom', "48x48"), wx.BITMAP_TYPE_ANY))
  999         button.SetMinSize((80, 80))
 1000         button.SetToolTip(wx.ToolTip("Perform a fully automated analysis, looping over global models I to V and terminating with the final run.  Please click on the 'About' button for more information."))
 1001         sub_sizer.Add(button, 3, wx.EXPAND, 0)
 1002         self.Bind(wx.EVT_BUTTON, self.select_full_analysis, button)
 1003 
 1004         # Spacing.
 1005         sub_sizer.AddStretchSpacer()
 1006 
 1007         # Add the sub-sizer.
 1008         sizer.Add(sub_sizer, 1, wx.ALL|wx.EXPAND, 0)
 1009 
 1010 
 1011     def build_manual(self, sizer):
 1012         """Build the manual part of the window.
 1013 
 1014         @param sizer:   The sizer to pack the elements into.
 1015         @type sizer:    wx.BoxSizer instance
 1016         """
 1017 
 1018         # Create a vertical sizer for the elements.
 1019         sub_sizer = wx.BoxSizer(wx.VERTICAL)
 1020 
 1021         # The title.
 1022         title = wx.StaticText(self, -1, "Manual modes")
 1023         title.SetFont(font.subtitle)
 1024         sub_sizer.Add(title, 0, wx.ALIGN_CENTRE_HORIZONTAL, 0)
 1025 
 1026         # Spacing.
 1027         sub_sizer.AddSpacer(10)
 1028 
 1029         # The local_tm button.
 1030         button = wx.Button(self, -1, "Local %s" % tm)
 1031         button.SetToolTip(wx.ToolTip("Optimise global model I, the %s models.  Please click on the 'About' button for more information." % local_tm))
 1032         button.SetFont(font.normal)
 1033         sub_sizer.Add(button, 1, wx.EXPAND, 0)
 1034         self.Bind(wx.EVT_BUTTON, self.select_local_tm, button)
 1035 
 1036         # The sphere button.
 1037         button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, str_to_gui("   Sphere"))
 1038         button.SetBitmapLabel(wx.Bitmap(IMAGE_PATH+'sphere.png', wx.BITMAP_TYPE_ANY))
 1039         button.SetFont(font.normal)
 1040         button.SetToolTip(wx.ToolTip("Optimise global model II, the spherical diffusion model.  Please click on the 'About' button for more information."))
 1041         sub_sizer.Add(button, 1, wx.EXPAND, 0)
 1042         self.Bind(wx.EVT_BUTTON, self.select_sphere, button)
 1043 
 1044         # The prolate spheroid button.
 1045         button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, str_to_gui("   Prolate spheroid"))
 1046         button.SetBitmapLabel(wx.Bitmap(IMAGE_PATH+'prolate.png', wx.BITMAP_TYPE_ANY))
 1047         button.SetFont(font.normal)
 1048         button.SetToolTip(wx.ToolTip("Optimise global model III, the prolate spheroid diffusion model.  Please click on the 'About' button for more information."))
 1049         sub_sizer.Add(button, 1, wx.EXPAND, 0)
 1050         self.Bind(wx.EVT_BUTTON, self.select_prolate, button)
 1051 
 1052         # The oblate spheroid button.
 1053         button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, str_to_gui("   Oblate spheroid"))
 1054         button.SetBitmapLabel(wx.Bitmap(IMAGE_PATH+'oblate.png', wx.BITMAP_TYPE_ANY))
 1055         button.SetFont(font.normal)
 1056         button.SetToolTip(wx.ToolTip("Optimise global model IV, the oblate spheroid diffusion model.  Please click on the 'About' button for more information."))
 1057         sub_sizer.Add(button, 1, wx.EXPAND, 0)
 1058         self.Bind(wx.EVT_BUTTON, self.select_oblate, button)
 1059 
 1060         # The ellipsoid button.
 1061         button = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, str_to_gui("   Ellipsoid"))
 1062         button.SetBitmapLabel(wx.Bitmap(IMAGE_PATH+'ellipsoid.png', wx.BITMAP_TYPE_ANY))
 1063         button.SetFont(font.normal)
 1064         button.SetToolTip(wx.ToolTip("Optimise global model V, the ellipsoid diffusion model.  Please click on the 'About' button for more information."))
 1065         sub_sizer.Add(button, 1, wx.EXPAND, 0)
 1066         self.Bind(wx.EVT_BUTTON, self.select_ellipsoid, button)
 1067 
 1068         # The final button.
 1069         button = wx.Button(self, -1, str_to_gui("Final"))
 1070         button.SetToolTip(wx.ToolTip("The final run of the protocol.  Please click on the 'About' button for more information."))
 1071         button.SetFont(font.normal)
 1072         sub_sizer.Add(button, 1, wx.EXPAND, 0)
 1073         self.Bind(wx.EVT_BUTTON, self.select_final, button)
 1074 
 1075         # Add the sub-sizer.
 1076         sizer.Add(sub_sizer, 1, wx.ALL|wx.EXPAND, 0)
 1077 
 1078 
 1079     def select_ellipsoid(self, event=None):
 1080         """The ellipsoid global model has been selected.
 1081 
 1082         @keyword event: The wx event.
 1083         @type event:    wx event
 1084         """
 1085 
 1086         # Set the value.
 1087         self.select = 'ellipsoid'
 1088 
 1089         # Close the dialog.
 1090         self.Close()
 1091 
 1092 
 1093     def select_final(self, event=None):
 1094         """The final stage of the protocol has been selected.
 1095 
 1096         @keyword event: The wx event.
 1097         @type event:    wx event
 1098         """
 1099 
 1100         # Set the value.
 1101         self.select = 'final'
 1102 
 1103         # Close the dialog.
 1104         self.Close()
 1105 
 1106 
 1107     def select_full_analysis(self, event=None):
 1108         """The full analysis has been selected.
 1109 
 1110         @keyword event: The wx event.
 1111         @type event:    wx event
 1112         """
 1113 
 1114         # Set the value.
 1115         self.select = 'Fully automated'
 1116 
 1117         # Close the dialog.
 1118         self.Close()
 1119 
 1120 
 1121     def select_local_tm(self, event=None):
 1122         """The local_tm global model has been selected.
 1123 
 1124         @keyword event: The wx event.
 1125         @type event:    wx event
 1126         """
 1127 
 1128         # Set the value.
 1129         self.select = 'local_tm'
 1130 
 1131         # Close the dialog.
 1132         self.Close()
 1133 
 1134 
 1135     def select_prolate(self, event=None):
 1136         """The prolate global model has been selected.
 1137 
 1138         @keyword event: The wx event.
 1139         @type event:    wx event
 1140         """
 1141 
 1142         # Set the value.
 1143         self.select = 'prolate'
 1144 
 1145         # Close the dialog.
 1146         self.Close()
 1147 
 1148 
 1149     def select_oblate(self, event=None):
 1150         """The oblate global model has been selected.
 1151 
 1152         @keyword event: The wx event.
 1153         @type event:    wx event
 1154         """
 1155 
 1156         # Set the value.
 1157         self.select = 'oblate'
 1158 
 1159         # Close the dialog.
 1160         self.Close()
 1161 
 1162 
 1163     def select_sphere(self, event=None):
 1164         """The sphere global model has been selected.
 1165 
 1166         @keyword event: The wx event.
 1167         @type event:    wx event
 1168         """
 1169 
 1170         # Set the value.
 1171         self.select = 'sphere'
 1172 
 1173         # Close the dialog.
 1174         self.Close()