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

Source Code for Module gui.analyses.auto_noe

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2009 Michael Bieri                                            # 
  4  # Copyright (C) 2010-2012 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 NOE analysis frames.""" 
 25   
 26  # Python module imports. 
 27  from os import sep 
 28  import sys 
 29  import wx 
 30   
 31  # relax module imports. 
 32  from auto_analyses.noe import NOE_calc 
 33  from data import Relax_data_store; ds = Relax_data_store() 
 34  from generic_fns.mol_res_spin import are_spins_named, exists_mol_res_spin_data 
 35  from generic_fns.pipes import has_bundle, has_pipe 
 36  from status import Status; status = Status() 
 37   
 38  # relax GUI module imports. 
 39  from gui.analyses.base import Base_analysis, Spectral_error_type_page 
 40  from gui.analyses.elements import Text_ctrl 
 41  from gui.analyses.execute import Execute 
 42  from gui.analyses.results_analysis import color_code_noe 
 43  from gui.base_classes import Container 
 44  from gui.components.spectrum import Spectra_list 
 45  from gui.filedialog import RelaxDirDialog 
 46  from gui.message import error_message, Missing_data, Question 
 47  from gui.misc import protected_exec 
 48  from gui import paths 
 49  from gui.string_conv import gui_to_str, str_to_gui 
 50  from gui.uf_objects import Uf_storage; uf_store = Uf_storage() 
 51  from gui.wizard import Wiz_window 
 52   
 53   
 54   
55 -class Auto_noe(Base_analysis):
56 """The base class for the noe frames.""" 57 58 # Hardcoded variables. 59 analysis_type = None 60 bitmap = [paths.ANALYSIS_IMAGE_PATH+"noe_200x200.png", 61 paths.IMAGE_PATH+'noe.png'] 62 label = None 63
64 - 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, data_index=None):
65 """Build the automatic NOE analysis GUI frame elements. 66 67 @param parent: The parent wx element. 68 @type parent: wx object 69 @keyword id: The unique ID number. 70 @type id: int 71 @keyword pos: The position. 72 @type pos: wx.Size object 73 @keyword size: The size. 74 @type size: wx.Size object 75 @keyword style: The style. 76 @type style: int 77 @keyword name: The name for the panel. 78 @type name: unicode 79 @keyword gui: The main GUI class. 80 @type gui: gui.relax_gui.Main instance 81 @keyword analysis_name: The name of the analysis (the name in the tab part of the notebook). 82 @type analysis_name: str 83 @keyword pipe_name: The name of the data pipe associated with this analysis. 84 @type pipe_name: str 85 @keyword pipe_bundle: The name of the data pipe bundle associated with this analysis. 86 @type pipe_bundle: str 87 @keyword data_index: The index of the analysis in the relax data store (set to None if no data currently exists). 88 @type data_index: None or int 89 """ 90 91 # Store the GUI main class. 92 self.gui = gui 93 94 # Init. 95 self.init_flag = True 96 97 # New data container. 98 if data_index == None: 99 # First create the data pipe if not already in existence. 100 if not has_pipe(pipe_name): 101 self.gui.interpreter.apply('pipe.create', pipe_name=pipe_name, pipe_type='noe', bundle=pipe_bundle) 102 103 # Create the data pipe bundle if needed. 104 if not has_bundle(pipe_bundle): 105 self.gui.interpreter.apply('pipe.bundle', bundle=pipe_bundle, pipe=pipe_name) 106 107 # Generate a storage container in the relax data store, and alias it for easy access. 108 data_index = ds.relax_gui.analyses.add('NOE') 109 110 # Store the analysis and pipe names. 111 ds.relax_gui.analyses[data_index].analysis_name = analysis_name 112 ds.relax_gui.analyses[data_index].pipe_name = pipe_name 113 ds.relax_gui.analyses[data_index].pipe_bundle = pipe_bundle 114 115 # Initialise the variables. 116 ds.relax_gui.analyses[data_index].frq = '' 117 ds.relax_gui.analyses[data_index].save_dir = self.gui.launch_dir 118 119 # Alias the data. 120 self.data = ds.relax_gui.analyses[data_index] 121 self.data_index = data_index 122 123 # Register the method for updating the spin count for the completion of user functions. 124 self.observer_register() 125 126 # Execute the base class method to build the panel. 127 super(Auto_noe, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)
128 129
130 - def activate(self):
131 """Activate or deactivate certain elements of the analysis in response to the execution lock.""" 132 133 # Flag for enabling or disabling the elements. 134 enable = False 135 if not status.exec_lock.locked(): 136 enable = True 137 138 # Activate or deactivate the elements. 139 wx.CallAfter(self.field_nmr_frq.Enable, enable) 140 wx.CallAfter(self.field_results_dir.Enable, enable) 141 wx.CallAfter(self.spin_systems.Enable, enable) 142 wx.CallAfter(self.peak_intensity.Enable, enable) 143 wx.CallAfter(self.button_exec_relax.Enable, enable)
144 145
146 - def assemble_data(self):
147 """Assemble the data required for the Auto_noe class. 148 149 @return: A container with all the data required for the auto-analysis. 150 @rtype: class instance, list of str 151 """ 152 153 # The data container. 154 data = Container() 155 missing = [] 156 157 # The pipe name and bundle. 158 data.pipe_name = self.data.pipe_name 159 data.pipe_bundle = self.data.pipe_bundle 160 161 # The frequency. 162 frq = gui_to_str(self.field_nmr_frq.GetValue()) 163 if frq == None: 164 missing.append('NMR frequency') 165 166 # Filename. 167 data.file_root = 'noe.%s' % frq 168 169 # Results directory. 170 data.save_dir = self.data.save_dir 171 172 # Check if sequence data is loaded 173 if not exists_mol_res_spin_data(): 174 missing.append("Sequence data") 175 176 # Spectral data. 177 if not hasattr(cdp, 'spectrum_ids') or len(cdp.spectrum_ids) < 2: 178 missing.append("Spectral data") 179 180 # Return the container and list of missing data. 181 return data, missing
182 183
184 - def build_right_box(self):
185 """Construct the right hand box to pack into the main NOE box. 186 187 @return: The right hand box element containing all NOE GUI elements (excluding the bitmap) to pack into the main Rx box. 188 @rtype: wx.BoxSizer instance 189 """ 190 191 # Use a vertical packing of elements. 192 box = wx.BoxSizer(wx.VERTICAL) 193 194 # Add the frame title. 195 self.add_title(box, "Setup for steady-state NOE analysis") 196 197 # Display the data pipe. 198 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) 199 200 # Add the frequency selection GUI element. 201 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 NOE values will be located in the file 'noe.600.out'.", width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 202 203 # Add the results directory GUI element. 204 self.field_results_dir = Text_ctrl(box, self, text="Results directory", icon=paths.icon_16x16.open_folder, default=self.data.save_dir, fn=self.results_directory, button=True, width_text=self.width_text, width_button=self.width_button, spacer=self.spacer_horizontal) 205 206 # Add the spin GUI element. 207 self.add_spin_systems(box, self) 208 209 # Add the peak list selection GUI element, with spacing. 210 box.AddSpacer(40) 211 self.peak_intensity = Spectra_list(gui=self.gui, parent=self, box=box, id=str(self.data_index), fn_add=self.peak_wizard) 212 213 # Stretchable spacing (with a minimal space). 214 box.AddSpacer(30) 215 box.AddStretchSpacer() 216 217 # Add the execution GUI element. 218 self.button_exec_relax = self.add_execute_relax(box, self.execute) 219 220 # Return the box. 221 return box
222 223
224 - def delete(self):
225 """Unregister the spin count from the user functions.""" 226 227 # Unregister the observer methods. 228 self.observer_register(remove=True) 229 230 # Clean up the peak intensity object. 231 self.peak_intensity.delete()
232 233
234 - def execute(self, event):
235 """Set up, execute, and process the automatic Rx analysis. 236 237 @param event: The wx event. 238 @type event: wx event 239 """ 240 241 # Flush the GUI interpreter internal queue to make sure all user functions are complete. 242 self.gui.interpreter.flush() 243 244 # relax execution lock. 245 if status.exec_lock.locked(): 246 error_message("relax is currently executing.", "relax execution lock") 247 event.Skip() 248 return 249 250 # User warning to close windows. 251 self.gui.close_windows() 252 253 # Synchronise the frame data to the relax data store. 254 self.sync_ds(upload=True) 255 256 # Assemble all the data needed for the auto-analysis. 257 data, missing = self.assemble_data() 258 259 # Missing data. 260 if len(missing): 261 Missing_data(missing) 262 return 263 264 # Display the relax controller, and go to the end of the log window. 265 self.gui.show_controller(None) 266 self.gui.controller.log_panel.on_goto_end(None) 267 268 # Start the thread. 269 self.thread = Execute_noe(self.gui, data, self.data_index) 270 self.thread.start() 271 272 # Terminate the event. 273 event.Skip()
274 275
276 - def observer_register(self, remove=False):
277 """Register and unregister methods with the observer objects. 278 279 @keyword remove: If set to True, then the methods will be unregistered. 280 @type remove: False 281 """ 282 283 # Register. 284 if not remove: 285 status.observers.gui_uf.register(self.data.pipe_bundle, self.update_spin_count, method_name='update_spin_count') 286 status.observers.exec_lock.register(self.data.pipe_bundle, self.activate, method_name='activate') 287 288 # Unregister. 289 else: 290 # The model-free methods. 291 status.observers.gui_uf.unregister(self.data.pipe_bundle) 292 status.observers.exec_lock.unregister(self.data.pipe_bundle) 293 294 # The embedded objects methods. 295 self.peak_intensity.observer_register(remove=True)
296 297
298 - def peak_wizard(self, event):
299 """Launch the NOE peak loading wizard. 300 301 @param event: The wx event. 302 @type event: wx event 303 """ 304 305 # Change the cursor to busy. 306 wx.BeginBusyCursor() 307 308 # Initialise a wizard. 309 self.wizard = Wiz_window(parent=self.gui, size_x=1000, size_y=750, title="Set up the NOE peak intensities") 310 self.page_indices = {} 311 312 # First check that at least a single spin is named! 313 if not are_spins_named(): 314 # The message. 315 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?" 316 317 # Ask about naming spins, and add the spin.name user function page. 318 if status.show_gui and Question(msg, title="Incomplete setup", size=(450, 250), default=True).ShowModal() == wx.ID_YES: 319 page = uf_store['spin.name'].create_page(self.wizard, sync=True) 320 self.page_indices['read'] = self.wizard.add_page(page, proceed_on_error=False) 321 322 323 # The spectrum.read_intensities page. 324 self.page_intensity = uf_store['spectrum.read_intensities'].create_page(self.wizard, sync=True) 325 self.page_indices['read'] = self.wizard.add_page(self.page_intensity, skip_button=True, proceed_on_error=False) 326 327 # Error type selection page. 328 self.page_error_type = Spectral_error_type_page(parent=self.wizard, height_desc=520) 329 self.page_indices['err_type'] = self.wizard.add_page(self.page_error_type, apply_button=False) 330 self.wizard.set_seq_next_fn(self.page_indices['err_type'], self.wizard_page_after_error_type) 331 332 # The spectrum.replicated page. 333 page = uf_store['spectrum.replicated'].create_page(self.wizard, sync=True) 334 self.page_indices['repl'] = self.wizard.add_page(page, skip_button=True, proceed_on_error=False) 335 self.wizard.set_seq_next_fn(self.page_indices['repl'], self.wizard_page_after_repl) 336 page.on_display_post = self.wizard_update_repl 337 338 # The spectrum.baseplane_rmsd page. 339 page = uf_store['spectrum.baseplane_rmsd'].create_page(self.wizard, sync=True) 340 self.page_indices['rmsd'] = self.wizard.add_page(page, skip_button=True, proceed_on_error=False) 341 self.wizard.set_seq_next_fn(self.page_indices['rmsd'], self.wizard_page_after_rmsd) 342 page.on_display_post = self.wizard_update_rmsd 343 344 # The spectrum.integration_points page. 345 page = uf_store['spectrum.integration_points'].create_page(self.wizard, sync=True) 346 self.page_indices['pts'] = self.wizard.add_page(page, skip_button=True, proceed_on_error=False) 347 page.on_display_post = self.wizard_update_pts 348 349 # The noe.spectrum_type page. 350 page = uf_store['noe.spectrum_type'].create_page(self.wizard, sync=True) 351 self.page_indices['spectrum_type'] = self.wizard.add_page(page, skip_button=False, proceed_on_error=False) 352 page.on_display_post = self.wizard_update_spectrum_type 353 354 # Reset the cursor. 355 if wx.IsBusy(): 356 wx.EndBusyCursor() 357 358 # Run the wizard. 359 self.wizard.run()
360 361
362 - def results_directory(self, event):
363 """The results directory selection. 364 365 @param event: The wx event. 366 @type event: wx event 367 """ 368 369 # The dialog. 370 dialog = RelaxDirDialog(parent=self, message='Select the results directory', defaultPath=self.field_results_dir.GetValue()) 371 372 # Show the dialog and catch if no file has been selected. 373 if status.show_gui and dialog.ShowModal() != wx.ID_OK: 374 # Don't do anything. 375 return 376 377 # The path (don't do anything if not set). 378 path = gui_to_str(dialog.get_path()) 379 if not path: 380 return 381 382 # Store the path. 383 self.data.save_dir = path 384 385 # Place the path in the text box. 386 self.field_results_dir.SetValue(str_to_gui(path))
387 388
389 - def sync_ds(self, upload=False):
390 """Synchronise the noe analysis frame and the relax data store, both ways. 391 392 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. 393 394 @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. 395 @type upload: bool 396 """ 397 398 # The frequency. 399 if upload: 400 self.data.frq = gui_to_str(self.field_nmr_frq.GetValue()) 401 else: 402 self.field_nmr_frq.SetValue(str_to_gui(self.data.frq)) 403 404 # The results directory. 405 if upload: 406 self.data.save_dir = gui_to_str(self.field_results_dir.GetValue()) 407 else: 408 self.field_results_dir.SetValue(str_to_gui(self.data.save_dir))
409 410
412 """Set the page after the error type choice. 413 414 @return: The index of the next page, which is the current page index plus one. 415 @rtype: int 416 """ 417 418 # Go to the spectrum.baseplane_rmsd page. 419 if self.page_error_type.selection == 'rmsd': 420 return self.page_indices['rmsd'] 421 422 # Go to the spectrum.replicated page. 423 elif self.page_error_type.selection == 'repl': 424 return self.page_indices['repl']
425 426
427 - def wizard_page_after_repl(self):
428 """Set the page that comes after the spectrum.replicated page. 429 430 @return: The index of the next page. 431 @rtype: int 432 """ 433 434 # Go to the spectrum.integration_points page. 435 int_method = gui_to_str(self.page_intensity.uf_args['int_method'].GetValue()) 436 if int_method != 'height': 437 return self.page_indices['pts'] 438 439 # Skip to the noe.spectrum_type page. 440 else: 441 return self.page_indices['spectrum_type']
442 443
444 - def wizard_page_after_rmsd(self):
445 """Set the page that comes after the spectrum.baseplane_rmsd page. 446 447 @return: The index of the next page. 448 @rtype: int 449 """ 450 451 # Go to the spectrum.integration_points page. 452 int_method = gui_to_str(self.page_intensity.uf_args['int_method'].GetValue()) 453 if int_method != 'height': 454 return self.page_indices['pts'] 455 456 # Skip to the noe.spectrum_type page. 457 else: 458 return self.page_indices['spectrum_type']
459 460
461 - def wizard_update_pts(self):
462 """Update the spectrum.replicated page based on previous data.""" 463 464 # The spectrum.read_intensities page. 465 page = self.wizard.get_page(self.page_indices['read']) 466 467 # Set the spectrum ID. 468 id = page.uf_args['spectrum_id'].GetValue() 469 470 # Set the ID in the spectrum.replicated page. 471 page = self.wizard.get_page(self.page_indices['pts']) 472 page.uf_args['spectrum_id'].SetValue(id)
473 474
475 - def wizard_update_repl(self):
476 """Update the spectrum.replicated page based on previous data.""" 477 478 # The spectrum.read_intensities page. 479 page = self.wizard.get_page(self.page_indices['read']) 480 481 # Set the spectrum ID. 482 id = page.uf_args['spectrum_id'].GetValue() 483 484 # Set the ID in the spectrum.replicated page. 485 page = self.wizard.get_page(self.page_indices['repl']) 486 page.uf_args['spectrum_ids'].SetValue(value=id, index=0)
487 488
489 - def wizard_update_rmsd(self):
490 """Update the spectrum.baseplane_rmsd page based on previous data.""" 491 492 # The spectrum.read_intensities page. 493 page = self.wizard.get_page(self.page_indices['read']) 494 495 # Set the spectrum ID. 496 id = page.uf_args['spectrum_id'].GetValue() 497 498 # Set the ID in the spectrum.baseplane_rmsd page. 499 page = self.wizard.get_page(self.page_indices['rmsd']) 500 page.uf_args['spectrum_id'].SetValue(id)
501 502
504 """Update the noe.spectrum_type page based on previous data.""" 505 506 # The spectrum.read_intensities page. 507 page = self.wizard.get_page(self.page_indices['read']) 508 509 # Set the spectrum ID. 510 id = page.uf_args['spectrum_id'].GetValue() 511 512 # Set the ID in the noe.spectrum_type page. 513 page = self.wizard.get_page(self.page_indices['spectrum_type']) 514 page.uf_args['spectrum_id'].SetValue(id)
515 516 517
518 -class Execute_noe(Execute):
519 """The NOE analysis execution object.""" 520
521 - def run_analysis(self):
522 """Execute the calculation.""" 523 524 # Execute. 525 NOE_calc(pipe_name=self.data.pipe_name, pipe_bundle=self.data.pipe_bundle, file_root=self.data.file_root, results_dir=self.data.save_dir, save_state=False) 526 527 # Alias the relax data store data. 528 data = ds.relax_gui.analyses[self.data_index] 529 530 # FIXME: This must be shifted to the core of relax!!! 531 # Create a PyMOL macro, if a structure exists. 532 if hasattr(data, 'structure_file'): 533 # The macro. 534 color_code_noe(data.save_dir, data.structure_file) 535 536 # Add the macro to the results list. 537 cdp.result_files.append(['pymol', 'PyMOL', data.save_dir+sep+'noe.pml']) 538 status.observers.result_file.notify()
539