Package gui :: Package analyses :: Module auto_rx_base
[hide private]
[frames] | no frames]

Source Code for Module gui.analyses.auto_rx_base

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2009-2011 Michael Bieri                                       # 
  4  # Copyright (C) 2009-2015 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  import wx 
 28   
 29  # relax module imports. 
 30  from auto_analyses.relax_fit import Relax_fit 
 31  from data_store import Relax_data_store; ds = Relax_data_store() 
 32  from graphics import fetch_icon 
 33  from gui.analyses.base import Base_analysis 
 34  from gui.analyses.elements.spin_element import Spin_ctrl 
 35  from gui.analyses.elements.text_element import Text_ctrl 
 36  from gui.analyses.execute import Execute 
 37  from gui.base_classes import Container 
 38  from gui.components.spectrum import Spectra_list 
 39  from gui.filedialog import RelaxDirDialog 
 40  from gui.fonts import font 
 41  from gui.message import error_message, Missing_data 
 42  from gui.string_conv import gui_to_int, gui_to_str, str_to_gui 
 43  from gui.uf_objects import Uf_storage; uf_store = Uf_storage() 
 44  from gui.wizards.peak_intensity import Peak_intensity_wizard 
 45  from pipe_control.mol_res_spin import exists_mol_res_spin_data 
 46  from pipe_control.pipes import has_bundle, has_pipe 
 47  from status import Status; status = Status() 
 48   
 49   
50 -class Auto_rx(Base_analysis):
51 """The base class for the R1 and R2 frames.""" 52 53 # Hardcoded variables. 54 analysis_type = None 55 bitmap = None 56 label = None 57
58 - 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):
59 """Build the automatic R1 and R2 analysis GUI frame elements. 60 61 @param parent: The parent wx element. 62 @type parent: wx object 63 @keyword id: The unique ID number. 64 @type id: int 65 @keyword pos: The position. 66 @type pos: wx.Size object 67 @keyword size: The size. 68 @type size: wx.Size object 69 @keyword style: The style. 70 @type style: int 71 @keyword name: The name for the panel. 72 @type name: unicode 73 @keyword gui: The main GUI class. 74 @type gui: gui.relax_gui.Main instance 75 @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook). 76 @type analysis_name: str 77 @keyword pipe_name: The name of the data pipe associated with this analysis. 78 @type pipe_name: str 79 @keyword pipe_bundle: The name of the data pipe bundle associated with this analysis. 80 @type pipe_bundle: str 81 @keyword uf_exec: The list of user function on_execute methods returned from the new analysis wizard. 82 @type uf_exec: list of methods 83 @keyword data_index: The index of the analysis in the relax data store (set to None if no data currently exists). 84 @type data_index: None or int 85 """ 86 87 # Store the GUI main class. 88 self.gui = gui 89 90 # Init. 91 self.init_flag = True 92 93 # New data container. 94 if data_index == None: 95 # First create the data pipe if not already in existence. 96 if not has_pipe(pipe_name): 97 self.gui.interpreter.apply('pipe.create', pipe_name=pipe_name, pipe_type='relax_fit', bundle=pipe_bundle) 98 99 # Create the data pipe bundle if needed. 100 if not has_bundle(pipe_bundle): 101 self.gui.interpreter.apply('pipe.bundle', bundle=pipe_bundle, pipe=pipe_name) 102 103 # Generate a storage container in the relax data store, and alias it for easy access. 104 data_index = ds.relax_gui.analyses.add(self.label) 105 106 # Store the analysis and pipe names. 107 ds.relax_gui.analyses[data_index].analysis_name = analysis_name 108 ds.relax_gui.analyses[data_index].pipe_name = pipe_name 109 ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle 110 111 # Initialise the variables. 112 ds.relax_gui.analyses[data_index].frq = '' 113 ds.relax_gui.analyses[data_index].grid_inc = None 114 ds.relax_gui.analyses[data_index].mc_sim_num = None 115 ds.relax_gui.analyses[data_index].save_dir = self.gui.system_cwd_path 116 117 # Alias the data. 118 self.data = ds.relax_gui.analyses[data_index] 119 self.data_index = data_index 120 121 # Register the method for updating the spin count for the completion of user functions. 122 self.observer_register() 123 124 # Execute the base class method to build the panel. 125 super(Auto_rx, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)
126 127
128 - def activate(self):
129 """Activate or deactivate certain elements of the analysis in response to the execution lock.""" 130 131 # Flag for enabling or disabling the elements. 132 enable = False 133 if not status.exec_lock.locked(): 134 enable = True 135 136 # Activate or deactivate the elements. 137 wx.CallAfter(self.field_nmr_frq.Enable, enable) 138 wx.CallAfter(self.field_results_dir.Enable, enable) 139 wx.CallAfter(self.spin_systems.Enable, enable) 140 wx.CallAfter(self.peak_intensity.Enable, enable) 141 wx.CallAfter(self.grid_inc.Enable, enable) 142 wx.CallAfter(self.mc_sim_num.Enable, enable) 143 wx.CallAfter(self.button_exec_relax.Enable, enable)
144 145
146 - def add_buttons(self, box):
147 """Add all of the buttons. 148 149 @param box: The box element to pack the GUI element into. 150 @type box: wx.BoxSizer instance 151 """ 152 153 # Sizer. 154 sizer = wx.BoxSizer(wx.HORIZONTAL) 155 156 # Isotope type button. 157 self.button_select_model = wx.lib.buttons.ThemedGenBitmapTextButton(self, -1, None, " Exponential curve model") 158 self.button_select_model.SetBitmapLabel(wx.Bitmap(fetch_icon("oxygen.actions.list-add", "22x22"), wx.BITMAP_TYPE_ANY)) 159 self.button_select_model.SetFont(font.normal) 160 self.button_select_model.SetSize((-1, 25)) 161 self.button_select_model.SetToolTipString("Select the model for the exponential curve via the relax_fit.select_model user function.") 162 self.gui.Bind(wx.EVT_BUTTON, self.select_model, self.button_select_model) 163 sizer.Add(self.button_select_model, 1, wx.ALL|wx.EXPAND, 0) 164 165 # 3 invisible 'buttons' for better button layout. 166 sizer.AddStretchSpacer() 167 sizer.AddStretchSpacer() 168 sizer.AddStretchSpacer() 169 170 # Add the element to the box. 171 box.Add(sizer, 0, wx.ALL|wx.EXPAND, 0)
172 173
174 - def assemble_data(self):
175 """Assemble the data required for the auto-analysis. 176 177 See the docstring for auto_analyses.relax_fit for details. All data is taken from the relax data store, so data upload from the GUI to there must have been previously performed. 178 179 @return: A container with all the data required for the auto-analysis. 180 @rtype: class instance, list of str 181 """ 182 183 # The data container. 184 data = Container() 185 missing = [] 186 187 # The pipe name and bundle. 188 data.pipe_name = self.data.pipe_name 189 data.pipe_bundle = self.data.pipe_bundle 190 191 # The frequency. 192 frq = gui_to_str(self.field_nmr_frq.GetValue()) 193 if frq == None: 194 missing.append('NMR frequency') 195 196 # File root. 197 data.file_root = '%s.%s' % (self.analysis_type, frq) 198 199 # Check if sequence data is loaded 200 if not exists_mol_res_spin_data(): 201 missing.append("Sequence data") 202 203 # Spectral data. 204 if not hasattr(cdp, 'spectrum_ids') or len(cdp.spectrum_ids) < 3: 205 missing.append("Spectral data") 206 207 # Increment size. 208 data.inc = gui_to_int(self.grid_inc.GetValue()) 209 210 # The number of Monte Carlo simulations to be used for error analysis at the end of the analysis. 211 data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue()) 212 213 # Results directory. 214 data.save_dir = self.data.save_dir 215 216 # Return the container and list of missing data. 217 return data, missing
218 219
220 - def build_right_box(self):
221 """Construct the right hand box to pack into the main Rx box. 222 223 @return: The right hand box element containing all Rx GUI elements (excluding the bitmap) to pack into the main Rx box. 224 @rtype: wx.BoxSizer instance 225 """ 226 227 # Use a vertical packing of elements. 228 box = wx.BoxSizer(wx.VERTICAL) 229 230 # Add the frame title. 231 self.add_title(box, "%s relaxation analysis" % self.gui_label) 232 233 # Display the data pipe. 234 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) 235 236 # Add the frequency selection GUI element. 237 self.field_nmr_frq = Text_ctrl(box, self, text="NMR frequency label [MHz]:", default=self.data.frq, tooltip="This label is added to the output files. For example if the label is '600', the %s values will be located in the file '%s.600.out'." % (self.gui_label, self.label.lower()), width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 238 239 # Add the results directory GUI element. 240 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) 241 242 # Add the spin GUI element. 243 self.add_spin_systems(box, self) 244 245 # Add the peak list wizard and selection GUI element, with spacing. 246 box.AddSpacer(20) 247 self.peak_intensity = Spectra_list(gui=self.gui, parent=self, box=box, id=str(self.data_index), fn_add=self.peak_wizard_launch, relax_fit_flag=True) 248 box.AddSpacer(10) 249 250 # Add the buttons, with spacing. 251 box.AddSpacer(10) 252 self.add_buttons(box=box) 253 box.AddSpacer(10) 254 255 # The optimisation settings. 256 self.grid_inc = Spin_ctrl(box, self, text="Grid search increments:", default=21, 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) 257 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. For best results, at least 500 is recommended.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 258 259 # Stretchable spacing (with a minimal space). 260 box.AddSpacer(30) 261 box.AddStretchSpacer() 262 263 # Add the execution GUI element. 264 self.button_exec_relax = self.add_execute_analysis(box, self.execute) 265 266 # Return the box. 267 return box
268 269
270 - def delete(self):
271 """Unregister the spin count from the user functions.""" 272 273 # Unregister the observer methods. 274 self.observer_register(remove=True) 275 276 # Clean up the peak intensity object. 277 self.peak_intensity.delete() 278 279 # Destroy the peak intensity wizard, if it exists. 280 if hasattr(self, 'peak_wizard'): 281 self.peak_wizard.Destroy() 282 del self.peak_wizard 283 284 # Destroy the missing data dialog, if present. 285 if hasattr(self, 'missing_data'): 286 self.missing_data.Destroy() 287 del self.missing_data
288 289
290 - def execute(self, event):
291 """Set up, execute, and process the automatic Rx analysis. 292 293 @param event: The wx event. 294 @type event: wx event 295 """ 296 297 # Flush the GUI interpreter internal queue to make sure all user functions are complete. 298 self.gui.interpreter.flush() 299 300 # relax execution lock. 301 if status.exec_lock.locked(): 302 error_message("relax is currently executing.", "relax execution lock") 303 event.Skip() 304 return 305 306 # User warning to close windows. 307 self.gui.close_windows() 308 309 # Synchronise the frame data to the relax data store. 310 self.sync_ds(upload=True) 311 312 # Assemble all the data needed for the auto-analysis. 313 data, missing = self.assemble_data() 314 315 # Missing data. 316 if len(missing): 317 self.missing_data = Missing_data(missing) 318 return 319 320 # Display the relax controller, and go to the end of the log window. 321 self.gui.show_controller(None) 322 self.gui.controller.log_panel.on_goto_end(None) 323 324 # Start the thread. 325 self.thread = Execute_rx(self.gui, data, self.data_index) 326 self.thread.start() 327 328 # Terminate the event. 329 event.Skip()
330 331
332 - def observer_register(self, remove=False):
333 """Register and unregister methods with the observer objects. 334 335 @keyword remove: If set to True, then the methods will be unregistered. 336 @type remove: False 337 """ 338 339 # Register. 340 if not remove: 341 status.observers.gui_uf.register(self.data.pipe_bundle, self.update_spin_count, method_name='update_spin_count') 342 status.observers.exec_lock.register(self.data.pipe_bundle, self.activate, method_name='activate') 343 344 # Unregister. 345 else: 346 # The model-free methods. 347 status.observers.gui_uf.unregister(self.data.pipe_bundle) 348 status.observers.exec_lock.unregister(self.data.pipe_bundle) 349 350 # The embedded objects methods. 351 self.peak_intensity.observer_register(remove=True)
352 353
354 - def peak_wizard_launch(self, event):
355 """Launch the peak loading wizard. 356 357 @param event: The wx event. 358 @type event: wx event 359 """ 360 361 # Destroy the peak intensity wizard, if it exists. 362 if hasattr(self, 'peak_wizard'): 363 self.peak_wizard.Destroy() 364 365 # A new wizard instance. 366 self.peak_wizard = Peak_intensity_wizard(relax_fit=True)
367 368
369 - def results_directory(self, event):
370 """The results directory selection. 371 372 @param event: The wx event. 373 @type event: wx event 374 """ 375 376 # The dialog. 377 dialog = RelaxDirDialog(parent=self, message='Select the results directory', defaultPath=self.field_results_dir.GetValue()) 378 379 # Show the dialog and catch if no file has been selected. 380 if status.show_gui and dialog.ShowModal() != wx.ID_OK: 381 # Don't do anything. 382 return 383 384 # The path (don't do anything if not set). 385 path = gui_to_str(dialog.get_path()) 386 if not path: 387 return 388 389 # Store the path. 390 self.data.save_dir = path 391 392 # Place the path in the text box. 393 self.field_results_dir.SetValue(str_to_gui(path))
394 395
396 - def select_model(self, event=None):
397 """Launch the relax_fit.select_model user function. 398 399 @keyword event: The wx event. 400 @type event: wx event 401 """ 402 403 # Call the user function. 404 uf_store['relax_fit.select_model'](wx_wizard_modal=True)
405 406
407 - def sync_ds(self, upload=False):
408 """Synchronise the analysis frame and the relax data store, both ways. 409 410 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. 411 412 @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. 413 @type upload: bool 414 """ 415 416 # The frequency. 417 if upload: 418 self.data.frq = gui_to_str(self.field_nmr_frq.GetValue()) 419 else: 420 self.field_nmr_frq.SetValue(str_to_gui(self.data.frq)) 421 422 # The grid incs. 423 if upload: 424 self.data.grid_inc = gui_to_int(self.grid_inc.GetValue()) 425 elif hasattr(self.data, 'grid_inc'): 426 self.grid_inc.SetValue(int(self.data.grid_inc)) 427 428 # The MC sim number. 429 if upload: 430 self.data.mc_sim_num = gui_to_int(self.mc_sim_num.GetValue()) 431 elif hasattr(self.data, 'mc_sim_num'): 432 self.mc_sim_num.SetValue(int(self.data.mc_sim_num)) 433 434 # The results directory. 435 if upload: 436 self.data.save_dir = gui_to_str(self.field_results_dir.GetValue()) 437 else: 438 self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))
439 440 441
442 -class Execute_rx(Execute):
443 """The Rx analysis execution object.""" 444
445 - def run_analysis(self):
446 """Execute the calculation.""" 447 448 # Execute. 449 Relax_fit(pipe_name=self.data.pipe_name, pipe_bundle=self.data.pipe_bundle, file_root=self.data.file_root, results_dir=self.data.save_dir, grid_inc=self.data.inc, mc_sim_num=self.data.mc_sim_num, view_plots=False) 450 451 # Alias the relax data store data. 452 data = ds.relax_gui.analyses[self.data_index]
453