"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/wizards/peak_intensity.py" (2 Dec 2019, 24995 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 "peak_intensity.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-2011 Michael Bieri                                       #
    4 # Copyright (C) 2009-2014 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 containing the base class for the automatic R1 and R2 analysis frames."""
   25 
   26 # Python module imports.
   27 from os import sep
   28 import wx
   29 from wx.lib import scrolledpanel
   30 
   31 # relax module imports.
   32 from graphics import WIZARD_IMAGE_PATH
   33 from gui.fonts import font
   34 from gui.message import Question
   35 from gui.misc import format_table
   36 from gui.string_conv import gui_to_str
   37 from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
   38 from gui.wizards.wiz_objects import Wiz_page, Wiz_window
   39 from pipe_control.mol_res_spin import are_spins_named
   40 from specific_analyses.relax_disp.data import has_cpmg_exp_type, has_r1rho_exp_type
   41 from status import Status; status = Status()
   42 from user_functions.data import Uf_info; uf_info = Uf_info()
   43 from user_functions.data import Uf_tables; uf_tables = Uf_tables()
   44 
   45 
   46 class Peak_intensity_wizard(Wiz_window):
   47     """The wizard for loading peak intensity data."""
   48 
   49     def __init__(self, parent=None, size_x=1000, size_y=750, title="Peak intensity loading wizard", noe=False, relax_fit=False, relax_disp=False):
   50         """Set up the peak intensity loading wizard.
   51 
   52         @keyword parent:            The parent window.
   53         @type parent:               wx.Window instance
   54         @keyword size_x:            The width of the wizard.
   55         @type size_x:               int
   56         @keyword size_y:            The height of the wizard.
   57         @type size_y:               int
   58         @keyword title:             The title of the wizard dialog.
   59         @type title:                str
   60         @keyword noe:               A flag which when True will enable the steady-state NOE portions of the wizard.
   61         @type noe:                  bool
   62         @keyword relax_fit:         A flag which when True will enable the relaxation curve-fitting portions of the wizard.
   63         @type relax_fit:            bool
   64         @keyword relax_disp:        A flag which when True will enable the relaxation dispersion portions of the wizard.
   65         @type relax_disp:           bool
   66         """
   67 
   68         # Store the args.
   69         self.noe_flag = noe
   70         self.relax_fit_flag = relax_fit
   71         self.relax_disp_flag = relax_disp
   72 
   73         # Get the app and store the GUI instance.
   74         app = wx.GetApp()
   75         self.gui = app.gui
   76 
   77         # Execute the base class method.
   78         Wiz_window.__init__(self, parent=self.gui, size_x=size_x, size_y=size_y, title=title, style=wx.DEFAULT_DIALOG_STYLE)
   79 
   80         # Change the cursor to busy.
   81         wx.BeginBusyCursor()
   82 
   83         # Initialise the page_indices structure.
   84         self.page_indices = {}
   85 
   86         # First check that at least a single spin is named!
   87         if not are_spins_named():
   88             # The message.
   89             msg = "No spins have been named.  Please use the spin.name user function first, otherwise it is unlikely that any data will be loaded from the peak intensity file.\n\nThis message can be ignored if the generic file format is used and spin names have not been specified.  Would you like to name the spins already loaded into the relax data store?"
   90 
   91             # Ask about naming spins, and add the spin.name user function page.
   92             if (status.show_gui and Question(msg, title="Incomplete setup", size=(450, 250), default=True).ShowModal() == wx.ID_YES) or not status.show_gui:
   93                 page = uf_store['spin.name'].create_page(self, sync=True)
   94                 self.page_indices['name'] = self.add_page(page, proceed_on_error=False)
   95 
   96         # The spectrum.read_intensities page.
   97         self.page_intensity = uf_store['spectrum.read_intensities'].create_page(self, sync=True)
   98         self.page_indices['read'] = self.add_page(self.page_intensity, skip_button=True, proceed_on_error=False)
   99 
  100         # Error type selection page.
  101         self.page_error_type = Spectral_error_type_page(parent=self, height_desc=520)
  102         self.page_indices['err_type'] = self.add_page(self.page_error_type, apply_button=False)
  103         self.set_seq_next_fn(self.page_indices['err_type'], self.wizard_page_after_error_type)
  104 
  105         # The spectrum.replicated page.
  106         page = uf_store['spectrum.replicated'].create_page(self, sync=True)
  107         self.page_indices['repl'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  108         self.set_seq_next_fn(self.page_indices['repl'], self.wizard_page_after_repl)
  109         page.on_init = self.wizard_update_repl
  110 
  111         # The spectrum.baseplane_rmsd page.
  112         page = uf_store['spectrum.baseplane_rmsd'].create_page(self, sync=True)
  113         self.page_indices['rmsd'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  114         self.set_seq_next_fn(self.page_indices['rmsd'], self.wizard_page_after_rmsd)
  115         page.on_init = self.wizard_update_rmsd
  116 
  117         # The spectrum.integration_points page.
  118         page = uf_store['spectrum.integration_points'].create_page(self, sync=True)
  119         self.page_indices['pts'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  120         page.on_init = self.wizard_update_pts
  121 
  122         # NOE pages.
  123         if self.noe_flag:
  124             # The noe.spectrum_type page.
  125             page = uf_store['noe.spectrum_type'].create_page(self, sync=True)
  126             self.page_indices['spectrum_type'] = self.add_page(page, skip_button=False, proceed_on_error=False)
  127             page.on_display_post = self.wizard_update_noe_spectrum_type
  128 
  129         # Relaxation curve-fitting pages.
  130         if self.relax_fit_flag:
  131             # The relax_fit.relax_time page.
  132             page = uf_store['relax_fit.relax_time'].create_page(self, sync=True)
  133             self.page_indices['relax_time'] = self.add_page(page, skip_button=False, proceed_on_error=False)
  134             page.on_init = self.wizard_update_relax_fit_relax_time
  135 
  136         # Relaxation dispersion pages.
  137         if self.relax_disp_flag:
  138             # The relax_disp.exp_type page.
  139             page = uf_store['relax_disp.exp_type'].create_page(self, sync=True)
  140             self.page_indices['exp_type'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  141             page.on_init = self.wizard_update_relax_disp_exp_type
  142 
  143             # The spectrometer.frequency page.
  144             page = uf_store['spectrometer.frequency'].create_page(self, sync=True)
  145             self.page_indices['spectrometer_frequency'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  146             page.on_init = self.wizard_update_spectrometer_frequency
  147 
  148             # The relax_disp.relax_time page.
  149             page = uf_store['relax_disp.relax_time'].create_page(self, sync=True)
  150             self.page_indices['relax_time'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  151             page.on_init = self.wizard_update_relax_disp_relax_time
  152             self.set_seq_next_fn(self.page_indices['relax_time'], self.wizard_page_after_relax_time)
  153 
  154             # The relax_disp.cpmg_setup page.
  155             page = uf_store['relax_disp.cpmg_setup'].create_page(self, sync=True)
  156             self.page_indices['cpmg_setup'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  157             page.on_init = self.wizard_update_relax_disp_cpmg_setup
  158             self.set_seq_next_fn(self.page_indices['cpmg_setup'], self.wizard_page_after_cpmg_setup)
  159 
  160             # The relax_disp.spin_lock_field page.
  161             page = uf_store['relax_disp.spin_lock_field'].create_page(self, sync=True)
  162             self.page_indices['spin_lock_field'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  163             page.on_init = self.wizard_update_relax_disp_spin_lock_field
  164 
  165             # The relax_disp.spin_lock_offset page.
  166             page = uf_store['relax_disp.spin_lock_offset'].create_page(self, sync=True)
  167             self.page_indices['spin_lock_offset'] = self.add_page(page, skip_button=True, proceed_on_error=False)
  168             page.on_init = self.wizard_update_relax_disp_spin_lock_offset
  169 
  170         # Reset the cursor.
  171         if wx.IsBusy():
  172             wx.EndBusyCursor()
  173 
  174         # Run the wizard.
  175         self.run()
  176 
  177 
  178     def wizard_page_after_cpmg_setup(self):
  179         """Set the page after the CPMG setup page.
  180 
  181         This should either be the R1rho page if R1rho experiment types exist, or terminate the wizard.
  182 
  183 
  184         @return:    The index of the next page, which is the current page index plus one.
  185         @rtype:     int
  186         """
  187 
  188         # R1rho experiments exists.
  189         if has_r1rho_exp_type():
  190             return self.page_indices['spin_lock_field']
  191 
  192         # Nothing left, so run off the end.
  193         else:
  194             return self._num_pages + 1
  195 
  196 
  197     def wizard_page_after_error_type(self):
  198         """Set the page after the error type choice.
  199 
  200         @return:    The index of the next page, which is the current page index plus one.
  201         @rtype:     int
  202         """
  203 
  204         # Go to the spectrum.baseplane_rmsd page.
  205         if self.page_error_type.selection == 'rmsd':
  206             return self.page_indices['rmsd']
  207 
  208         # Go to the spectrum.replicated page.
  209         elif self.page_error_type.selection == 'repl':
  210             return self.page_indices['repl']
  211 
  212 
  213     def wizard_page_after_relax_time(self):
  214         """Set the page after the relaxation time period page.
  215 
  216         This should either be the CPMG page if CPMG experiment types exist, the R1rho page if R1rho experiment types exist, or terminate the wizard.
  217 
  218 
  219         @return:    The index of the next page, which is the current page index plus one.
  220         @rtype:     int
  221         """
  222 
  223         # CPMG experiments exists.
  224         if has_cpmg_exp_type():
  225             return self.page_indices['cpmg_setup']
  226 
  227         # R1rho experiments exists.
  228         elif has_r1rho_exp_type():
  229             return self.page_indices['spin_lock_field']
  230 
  231         # Nothing left, so run off the end.
  232         else:
  233             return self._num_pages + 1
  234 
  235 
  236     def wizard_page_after_repl(self):
  237         """Set the page that comes after the spectrum.replicated page.
  238 
  239         @return:    The index of the next page.
  240         @rtype:     int
  241         """
  242 
  243         # Go to the spectrum.integration_points page.
  244         int_method = gui_to_str(self.page_intensity.uf_args['int_method'].GetValue())
  245         if int_method != 'height' and self.page_error_type.selection == 'rmsd':
  246             return self.page_indices['pts']
  247 
  248         # Skip to the noe.spectrum_type page.
  249         elif self.noe_flag:
  250             return self.page_indices['spectrum_type']
  251 
  252         # Skip to the relax_fit.relax_time page.
  253         elif self.relax_fit_flag:
  254             return self.page_indices['relax_time']
  255 
  256         # Skip to the first dispersion page.
  257         elif self.relax_disp_flag:
  258             return self.page_indices['exp_type']
  259 
  260         # Nothing left, so run off the end.
  261         else:
  262             return self._num_pages + 1
  263 
  264 
  265     def wizard_page_after_rmsd(self):
  266         """Set the page that comes after the spectrum.baseplane_rmsd page.
  267 
  268         @return:    The index of the next page.
  269         @rtype:     int
  270         """
  271 
  272         # Go to the spectrum.integration_points page.
  273         int_method = gui_to_str(self.page_intensity.uf_args['int_method'].GetValue())
  274         if int_method != 'height':
  275             return self.page_indices['pts']
  276 
  277         # Skip to the noe.spectrum_type page.
  278         elif self.noe_flag:
  279             return self.page_indices['spectrum_type']
  280 
  281         # Skip to the relax_fit.relax_time page.
  282         elif self.relax_fit_flag:
  283             return self.page_indices['relax_time']
  284 
  285         # Skip to the first dispersion page.
  286         elif self.relax_disp_flag:
  287             return self.page_indices['exp_type']
  288 
  289         # Nothing left, so run off the end.
  290         else:
  291             return self._num_pages + 1
  292 
  293 
  294     def wizard_update_ids(self, page_key=None, arg_key='spectrum_id', index=None):
  295         """Update the spectrum ID on the page specified by the key based on previous data.
  296 
  297         @keyword page_key:  The key of the page to update.
  298         @type page_key:     str
  299         @keyword arg_key:   The key of the page argument to change to the spectrum ID.
  300         @type arg_key:      str
  301         @keyword index:     The index for list type structures.
  302         @type index:        None or int
  303         """
  304 
  305         # The spectrum.read_intensities page.
  306         page = self.get_page(self.page_indices['read'])
  307 
  308         # Set the spectrum ID.
  309         id = page.uf_args['spectrum_id'].GetValue()
  310 
  311         # Handle list arguments.
  312         if isinstance(id, list):
  313             # If an empty list is encountered, exit this method without doing anything.
  314             if id == []:
  315                 return
  316 
  317             # Use only the first element.
  318             id = id[0]
  319 
  320         # Handle keyword 'auto'.
  321         if id == 'auto':
  322             # Return the first ID.
  323             id = 'Z_A0'
  324 
  325         # Set the ID in the page.
  326         page = self.get_page(self.page_indices[page_key])
  327         if index == None:
  328             page.uf_args[arg_key].SetValue(id)
  329         else:
  330             page.uf_args[arg_key].SetValue(value=id, index=index)
  331 
  332 
  333     def wizard_update_noe_spectrum_type(self):
  334         """Update the noe.spectrum_type page based on previous data."""
  335 
  336         # Update the spectrum ID.
  337         self.wizard_update_ids(page_key='spectrum_type')
  338 
  339 
  340     def wizard_update_pts(self):
  341         """Update the spectrum.replicated page based on previous data."""
  342 
  343         # Update the spectrum ID.
  344         self.wizard_update_ids(page_key='pts')
  345 
  346 
  347     def wizard_update_relax_disp_cpmg_setup(self):
  348         """Update the relax_disp.cpmg_setup page based on previous data."""
  349 
  350         # Update the spectrum ID.
  351         self.wizard_update_ids(page_key='cpmg_setup')
  352 
  353 
  354     def wizard_update_relax_disp_exp_type(self):
  355         """Update the relax_disp.exp_type page based on previous data."""
  356 
  357         # Update the spectrum ID.
  358         self.wizard_update_ids(page_key='exp_type')
  359 
  360 
  361     def wizard_update_relax_disp_relax_time(self):
  362         """Update the relax_disp.relax_time page based on previous data."""
  363 
  364         # Update the spectrum ID.
  365         self.wizard_update_ids(page_key='relax_time')
  366 
  367 
  368     def wizard_update_relax_disp_spin_lock_field(self):
  369         """Update the relax_disp.spin_lock_field page based on previous data."""
  370 
  371         # Update the spectrum ID.
  372         self.wizard_update_ids(page_key='spin_lock_field')
  373 
  374 
  375     def wizard_update_relax_disp_spin_lock_offset(self):
  376         """Update the relax_disp.spin_lock_offset page based on previous data."""
  377 
  378         # Update the spectrum ID.
  379         self.wizard_update_ids(page_key='spin_lock_offset')
  380 
  381 
  382     def wizard_update_relax_fit_relax_time(self):
  383         """Update the relax_fit.relax_time page based on previous data."""
  384 
  385         # Update the spectrum ID.
  386         self.wizard_update_ids(page_key='relax_time')
  387 
  388 
  389     def wizard_update_repl(self):
  390         """Update the spectrum.replicated page based on previous data."""
  391 
  392         # Update the spectrum ID.
  393         self.wizard_update_ids(page_key='repl', arg_key='spectrum_ids', index=0)
  394 
  395 
  396     def wizard_update_rmsd(self):
  397         """Update the spectrum.baseplane_rmsd page based on previous data."""
  398 
  399         # Update the spectrum ID.
  400         self.wizard_update_ids(page_key='rmsd')
  401 
  402 
  403     def wizard_update_spectrometer_frequency(self):
  404         """Update the spectrometer.frequency page based on previous data."""
  405 
  406         # Update the spectrum ID.
  407         self.wizard_update_ids(page_key='spectrometer_frequency', arg_key='id')
  408 
  409 
  410 
  411 class Spectral_error_type_page(Wiz_page):
  412     """The peak intensity reading wizard page for specifying the type of error to be used."""
  413 
  414     # Class variables.
  415     image_path = WIZARD_IMAGE_PATH + 'spectrum' + sep + 'spectrum_200.png'
  416     title = "Specify the type of error to be used"
  417     main_text = "Please specify from where the peak intensity errors will be obtained.  The is required for the execution of the spectrum.error_analysis user function which you will find as a button in the main spectra list GUI element.  To understand how the errors will be propagated and analysed, the main parts of the spectrum.error_analysis user function description are given below."
  418     uf_path = ['spectrum', 'error_analysis']
  419 
  420     def _on_select(self, event):
  421         """Handle the radio button switching.
  422 
  423         @param event:   The wx event.
  424         @type event:    wx event
  425         """
  426 
  427         # The button.
  428         button = event.GetEventObject()
  429 
  430         # RMSD.
  431         if button == self.radio_rmsd:
  432             self.selection = 'rmsd'
  433         elif button == self.radio_repl:
  434             self.selection = 'repl'
  435 
  436 
  437     def add_contents(self, sizer):
  438         """Add the specific GUI elements.
  439 
  440         @param sizer:   A sizer object.
  441         @type sizer:    wx.Sizer instance
  442         """
  443 
  444         # A box sizer for placing the box sizer in.
  445         sizer2 = wx.BoxSizer(wx.HORIZONTAL)
  446         sizer.Add(sizer2, 1, wx.ALL|wx.EXPAND, 0)
  447 
  448         # Bottom spacing.
  449         sizer.AddStretchSpacer()
  450 
  451         # A bit of indentation.
  452         sizer2.AddStretchSpacer()
  453 
  454         # A vertical sizer for the radio buttons.
  455         sizer_radio = wx.BoxSizer(wx.VERTICAL)
  456         sizer2.Add(sizer_radio, 1, wx.ALL|wx.EXPAND, 0)
  457 
  458         # The RMSD radio button.
  459         self.radio_rmsd = wx.RadioButton(self, -1, "Baseplane RMSD.", style=wx.RB_GROUP)
  460         sizer_radio.Add(self.radio_rmsd, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0)
  461 
  462         # Spacing.
  463         sizer_radio.AddSpacer(10)
  464 
  465         # The replicated spectra radio button.
  466         self.radio_repl = wx.RadioButton(self, -1, "Replicated spectra.")
  467         sizer_radio.Add(self.radio_repl, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0)
  468 
  469         # Bind the buttons.
  470         self.Bind(wx.EVT_RADIOBUTTON, self._on_select, self.radio_rmsd)
  471         self.Bind(wx.EVT_RADIOBUTTON, self._on_select, self.radio_repl)
  472 
  473         # Right side spacing.
  474         sizer2.AddStretchSpacer(3)
  475 
  476         # Bottom spacing.
  477         sizer.AddStretchSpacer()
  478 
  479         # Set the default selection.
  480         self.selection = 'rmsd'
  481 
  482 
  483     def add_desc(self, sizer, max_y=520):
  484         """Add the description to the dialog.
  485 
  486         @param sizer:   A sizer object.
  487         @type sizer:    wx.Sizer instance
  488         @keyword max_y: The maximum height, in number of pixels, for the description.
  489         @type max_y:    int
  490         """
  491 
  492         # Initialise.
  493         spacing = 15
  494 
  495         # A line with spacing.
  496         sizer.AddSpacer(5)
  497         sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0)
  498         sizer.AddSpacer(5)
  499 
  500         # Create a scrolled panel.
  501         panel = scrolledpanel.ScrolledPanel(self, -1, name="desc")
  502 
  503         # A sizer for the panel.
  504         panel_sizer = wx.BoxSizer(wx.VERTICAL)
  505 
  506         # Initialise the text elements.
  507         tot_y = 0
  508         text_elements = []
  509         text_types = []
  510 
  511         # The wrapped text.
  512         text = wx.StaticText(panel, -1, self.main_text, style=wx.TE_MULTILINE)
  513         text.SetFont(font.normal)
  514         text.Wrap(self._main_size - 20)
  515         text_elements.append(text)
  516         text_types.append('title')
  517 
  518         # The text size, then spacing.
  519         x, y = text.GetSize()
  520         tot_y += y
  521         tot_y += spacing
  522 
  523         # Get the spectrum.error_analysis user function data object.
  524         uf_data = uf_info.get_uf('spectrum.error_analysis')
  525 
  526         # The description sections.
  527         if uf_data.desc != None:
  528             # Loop over the sections.
  529             for i in range(len(uf_data.desc)):
  530                 # Alias.
  531                 desc = uf_data.desc[i]
  532 
  533                 # Skip the prompt examples.
  534                 if desc.get_title() == 'Prompt examples':
  535                     continue
  536 
  537                 # Loop over the text elements.
  538                 for type, element in desc.element_loop(title=True):
  539                     # The text version of the elements.
  540                     text = ''
  541                     if isinstance(element, str):
  542                         text = element
  543 
  544                     # Format the tables.
  545                     if type == 'table':
  546                         text = format_table(uf_tables.get_table(element))
  547 
  548                     # Format the lists.
  549                     elif type == 'list':
  550                         # Loop over the list elements.
  551                         for j in range(len(element)):
  552                             text += "    - %s\n" % element[j]
  553 
  554                     # Format the item lists.
  555                     elif type == 'item list':
  556                         # Loop over the list elements.
  557                         for j in range(len(element)):
  558                             # No item.
  559                             if element[j][0] in [None, '']:
  560                                 text += "    %s\n" % element[j][1]
  561                             else:
  562                                 text += "    %s:  %s\n" % (element[j][0], element[j][1])
  563 
  564                     # The text object.
  565                     text_obj = wx.StaticText(panel, -1, text, style=wx.TE_MULTILINE)
  566 
  567                     # Format.
  568                     if type == 'title':
  569                         text_obj.SetFont(font.subtitle)
  570                     elif type == 'paragraph':
  571                         text_obj.SetFont(font.normal)
  572                     elif type in ['table', 'verbatim']:
  573                         text_obj.SetFont(font.modern_small)
  574                     else:
  575                         text_obj.SetFont(font.normal)
  576 
  577                     # Wrap the paragraphs and lists (with spacing for scrollbars).
  578                     if type in ['paragraph', 'list', 'item list']:
  579                         text_obj.Wrap(self._main_size - 20)
  580 
  581                     # The text size.
  582                     x, y = text_obj.GetSize()
  583                     tot_y += y
  584 
  585                     # The spacing after each element (except the last).
  586                     tot_y += spacing
  587 
  588                     # The spacing before each section (not including the first).
  589                     if i != 0 and type == 'title':
  590                         tot_y += spacing
  591 
  592                     # Append the text objects.
  593                     text_elements.append(text_obj)
  594                     text_types.append(type)
  595 
  596         # Some extra space for who knows what?!
  597         tot_y -= spacing
  598         tot_y += 20
  599 
  600         # Set the panel size - scrolling needed.
  601         if tot_y > max_y:
  602             panel.SetInitialSize((self._main_size, max_y))
  603 
  604         # Set the panel size - no scrolling.
  605         else:
  606             panel.SetInitialSize((self._main_size, tot_y))
  607 
  608         # Add the text.
  609         n = len(text_elements)
  610         for i in range(n):
  611             # Spacing before each section (not including the first).
  612             if i > 1 and text_types[i] == 'title':
  613                 panel_sizer.AddSpacer(spacing)
  614 
  615             # The text.
  616             panel_sizer.Add(text_elements[i], 0, wx.ALIGN_LEFT, 0)
  617 
  618             # Spacer after all sections (except the end).
  619             if i != n - 1:
  620                 panel_sizer.AddSpacer(spacing)
  621 
  622         # Set up and add the panel to the sizer.
  623         panel.SetSizer(panel_sizer)
  624         panel.SetAutoLayout(1)
  625         panel.SetupScrolling(scroll_x=False, scroll_y=True)
  626         sizer.Add(panel, 0, wx.ALL|wx.EXPAND)
  627 
  628         # A line with spacing.
  629         sizer.AddSpacer(5)
  630         sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0)
  631         sizer.AddSpacer(5)