1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21   
 22   
 23  """Package for the automatic and custom analysis GUI elements.""" 
 24   
 25   
 26  import inspect 
 27  import sys 
 28  import wx 
 29  from types import ListType 
 30   
 31   
 32  from data_store import Relax_data_store; ds = Relax_data_store() 
 33  from data_store.gui import Gui 
 34  import dep_check 
 35  from gui.analyses.auto_model_free import Auto_model_free 
 36  from gui.analyses.auto_noe import Auto_noe 
 37  from gui.analyses.auto_r1 import Auto_r1 
 38  from gui.analyses.auto_r2 import Auto_r2 
 39  from gui.analyses.auto_relax_disp import Auto_relax_disp 
 40  from gui.analyses.wizard import Analysis_wizard 
 41  from gui.message import error_message, Question 
 42  from lib.errors import RelaxError 
 43  from pipe_control import pipes 
 44  from pipe_control.reset import reset 
 45  from status import Status; status = Status() 
 46   
 47   
 48   
 49  __all__ = ['auto_model_free', 
 50             'auto_noe', 
 51             'auto_r1', 
 52             'auto_r2', 
 53             'auto_relax_disp', 
 54             'auto_rx_base', 
 55             'base', 
 56             'elements', 
 57             'relax_control', 
 58             'results_analysis', 
 59             'results'] 
 60   
 61   
 63      """Class for controlling all aspects of analyses.""" 
 64   
 66          """Initialise the analysis controller. 
 67   
 68          @param gui:         The gui object. 
 69          @type gui:          wx object 
 70          """ 
 71   
 72           
 73          self.gui = gui 
 74   
 75           
 76          self.init_state = True 
 77          self._current = None 
 78          self._num_analyses = 0 
 79          self._switch_flag = True 
 80   
 81           
 82          self._analyses = [] 
 83   
 84           
 85          self.name = 'notebook page switcher' 
 86          status.observers.pipe_alteration.register(self.name, self.pipe_switch, method_name='pipe_switch') 
 87   
 88           
 89          status.observers.pipe_alteration.register('notebook pipe deletion', self.pipe_deletion, method_name='pipe_deletion') 
 90   
 91           
 92          status.observers.reset.register('gui analyses', self.post_reset, method_name='post_reset') 
 93   
 94           
 95          status.observers.state_load.register('gui analyses', self.load_from_store, method_name='load_from_store') 
  96   
 97   
 99          """Loop over the analyses, yielding the data objects. 
100   
101          @return:    The analysis data object from the relax data store. 
102          @rtype:     data.gui.Analyses instance 
103          """ 
104   
105           
106          for i in range(self._num_analyses): 
107              yield ds.relax_gui.analyses[i] 
 108   
109   
111          """Loop over the analyses, yielding the page objects. 
112   
113          @return:    The page object. 
114          @rtype:     wx.Frame object 
115          """ 
116   
117           
118          for i in range(self._num_analyses): 
119              yield self._analyses[i] 
 120   
121   
123          """Return the data container of the current analysis from the relax data store. 
124   
125          @return:    The data container of the current analysis. 
126          @rtype:     str 
127          """ 
128   
129           
130          if self._current == None: 
131              return 
132   
133           
134          return ds.relax_gui.analyses[self._current] 
 135   
136   
138          """Return the name of the current analysis. 
139   
140          @return:    The name of the current analysis. 
141          @rtype:     str 
142          """ 
143   
144           
145          if self._current == None: 
146              return 
147   
148           
149          return ds.relax_gui.analyses[self._current].analysis_name 
 150   
151   
153          """Return the type of the current analysis. 
154   
155          @return:    The type of the current analysis. 
156          @rtype:     str 
157          """ 
158   
159           
160          if self._current == None: 
161              return 
162   
163           
164          return ds.relax_gui.analyses[self._current].analysis_type 
 165   
166   
168          """Remove all analyses.""" 
169   
170           
171          if status.debug: 
172              fn_name = sys._getframe().f_code.co_name 
173              mod_name = inspect.getmodule(inspect.stack()[1][0]).__name__ 
174              class_name = self.__class__.__name__ 
175              full_name = "%s.%s.%s" % (mod_name, class_name, fn_name) 
176              print("\n\n") 
177              print("debug> %s:  Deleting all analyses." % full_name) 
178   
179           
180          if status.debug: 
181              print("debug> %s:  Unregistering all methods with the observer objects." % full_name) 
182          for i in range(self._num_analyses): 
183              self._analyses[i].observer_register(remove=True) 
184   
185           
186          while self._num_analyses: 
187               
188               
189   
190               
191              if status.debug: 
192                  print("debug> %s:  Deleting the analysis at index %s." % (full_name, self._num_analyses-1)) 
193              self.delete_analysis(self._num_analyses-1) 
194   
195           
196          if status.debug: 
197              print("debug> %s:  All analyses now deleted." % full_name) 
198          status.observers.gui_analysis.notify() 
 199   
200   
202          """Delete the analysis tab and data store corresponding to the index. 
203   
204          The order of these operations is very important due to the notification of observer objects and the updates, synchronisations, etc. that follow.  If the program debugging mode is on, then printouts at each stage will occur to allow the following of the code and observer object notifications. 
205   
206   
207          @param index:   The index of the analysis to delete. 
208          @type index:    int 
209          """ 
210   
211           
212          if status.debug: 
213              fn_name = sys._getframe().f_code.co_name 
214              mod_name = inspect.getmodule(inspect.stack()[1][0]).__name__ 
215              class_name = self.__class__.__name__ 
216              full_name = "%s.%s.%s" % (mod_name, class_name, fn_name) 
217              print("\n\n") 
218              print("debug> %s:  Deleting the analysis at index %s." % (full_name, index)) 
219   
220           
221          self._num_analyses -= 1 
222   
223           
224          if self._current > index: 
225              self._current -= 1 
226              if status.debug: 
227                  print("debug> %s:  Switching the current analysis to index %s." % (full_name, self._current)) 
228   
229           
230          if hasattr(self._analyses[index], 'delete'): 
231              if status.debug: 
232                  print("debug> %s:  Executing the analysis specific delete() method." % full_name) 
233              self._analyses[index].delete() 
234   
235           
236          if status.debug: 
237              print("debug> %s:  Deleting the notebook page." % full_name) 
238          self.notebook.DeletePage(index) 
239   
240           
241          if status.debug: 
242              print("debug> %s:  Deleting the analysis GUI object." % full_name) 
243          self._analyses.pop(index) 
244   
245           
246          pipe_bundle = ds.relax_gui.analyses[index].pipe_bundle 
247   
248           
249          if status.debug: 
250              print("debug> %s:  Deleting the data store object." % full_name) 
251          ds.relax_gui.analyses.pop(index) 
252   
253           
254          for pipe in pipes.pipe_names(): 
255              if pipes.get_bundle(pipe) == pipe_bundle: 
256                  if status.debug: 
257                      print("debug> %s:  Deleting the data pipe '%s' from the '%s' bundle." % (full_name, pipe, pipe_bundle)) 
258                  pipes.delete(pipe) 
259   
260           
261          if self._num_analyses == 0: 
262              if status.debug: 
263                  print("debug> %s:  Setting the initial state." % full_name) 
264              self.set_init_state() 
265   
266           
267          elif index == self._current: 
268               
269              page_index = self._current 
270   
271               
272              if self._num_analyses <= self._current: 
273                  page_index = self._current - 1 
274   
275               
276              if status.debug: 
277                  print("debug> %s:  Switching to page %s." % (full_name, page_index)) 
278              self.switch_page(page_index) 
279   
280           
281          status.observers.gui_analysis.notify() 
 282   
283   
284 -    def get_page_from_name(self, name): 
 285          """Return the page corresponding to the given name. 
286   
287          @return:    The page which matches the given name, or nothing otherwise. 
288          @rtype:     wx.Frame object or None 
289          """ 
290   
291           
292          found = False 
293          for index in range(self._num_analyses): 
294               
295              if name == ds.relax_gui.analyses[index].analysis_name: 
296                  found = True 
297                  break 
298   
299           
300          if not found: 
301              return 
302   
303           
304          return self._analyses[index] 
 305   
306   
308          """Recreate the analyses from the relax data store.""" 
309   
310           
311          if not hasattr(ds, 'relax_gui'): 
312              return 
313   
314           
315          map = { 
316              'NOE': 'noe', 
317              'R1': 'r1', 
318              'R2': 'r2', 
319              'model-free': 'mf', 
320              'Relax-disp': 'relax_disp' 
321          } 
322   
323           
324          for i in range(len(ds.relax_gui.analyses)): 
325               
326              if hasattr(ds.relax_gui.analyses[i], 'analysis_name'): 
327                  analysis_name = ds.relax_gui.analyses[i].analysis_name 
328              elif ds.relax_gui.analyses[i].analysis_type == 'NOE': 
329                  analysis_name = 'Steady-state NOE' 
330              elif ds.relax_gui.analyses[i].analysis_type == 'R1': 
331                  analysis_name = 'R1 relaxation' 
332              elif ds.relax_gui.analyses[i].analysis_type == 'R2': 
333                  analysis_name = 'R2 relaxation' 
334              elif ds.relax_gui.analyses[i].analysis_type == 'model-free': 
335                  analysis_name = 'Model-free' 
336              elif ds.relax_gui.analyses[i].analysis_type == 'Relax-disp': 
337                  analysis_name = 'Relaxation dispersion' 
338   
339               
340              if not hasattr(ds.relax_gui.analyses[i], 'pipe_bundle'): 
341                   
342                  ds.relax_gui.analyses[i].pipe_bundle = ds.relax_gui.analyses[i].pipe_name 
343   
344                   
345                  self.gui.interpreter.apply('pipe.bundle', pipe=ds.relax_gui.analyses[i].pipe_name, bundle=ds.relax_gui.analyses[i].pipe_name) 
346   
347               
348              self._switch_flag = False 
349              self.new_analysis(map[ds.relax_gui.analyses[i].analysis_type], analysis_name, index=i) 
350   
351           
352          self.pipe_switch() 
353   
354           
355          self._switch_flag = True 
356   
357           
358          status.observers.gui_analysis.notify() 
 359   
360   
362          """Close the currently opened analysis. 
363   
364          @param event:   The wx event. 
365          @type event:    wx event 
366          """ 
367   
368           
369          if not hasattr(self, 'notebook'): 
370              return 
371   
372           
373          if status.exec_lock.locked(): 
374              return 
375   
376           
377          index = self.notebook.GetSelection() 
378   
379           
380          msg = "Are you sure you would like to close the current %s analysis tab?" % ds.relax_gui.analyses[index].analysis_type 
381          if status.show_gui and Question(msg, title="Close current analysis", size=(350, 140), default=False).ShowModal() == wx.ID_NO: 
382              return 
383   
384           
385          self.delete_analysis(index) 
 386   
387   
389          """Close all analyses. 
390   
391          @param event:   The wx event. 
392          @type event:    wx event 
393          """ 
394   
395           
396          if not hasattr(self, 'notebook'): 
397              return 
398   
399           
400          if status.exec_lock.locked(): 
401              return 
402   
403           
404          msg = "Are you sure you would like to close all analyses?  All data will be erased and the relax data store reset." 
405          if status.show_gui and Question(msg, title="Close all analyses", size=(350, 150), default=False).ShowModal() == wx.ID_NO: 
406              return 
407   
408           
409          self.delete_all() 
410   
411           
412          reset() 
 413   
414   
442   
443   
444 -    def new_analysis(self, analysis_type=None, analysis_name=None, pipe_name=None, pipe_bundle=None, uf_exec=[], index=None): 
 445          """Initialise a new analysis. 
446   
447          @keyword analysis_type: The type of analysis to initialise.  This can be one of 'noe', 'r1', 'r2', 'mf' or 'relax_disp'. 
448          @type analysis_type:    str 
449          @keyword analysis_name: The name of the analysis to initialise. 
450          @type analysis_name:    str 
451          @keyword pipe_name:     The name of the original data pipe to create for the analysis. 
452          @type pipe_name:        str 
453          @keyword pipe_bundle:   The name of the data pipe bundle to associate with this analysis. 
454          @type pipe_bundle:      str 
455          @keyword uf_exec:       The list of user function on_execute methods returned from the new analysis wizard. 
456          @type uf_exec:          list of methods 
457          @keyword index:         The index of the analysis in the relax data store (set to None if no data currently exists). 
458          @type index:            None or int 
459          """ 
460   
461           
462          if analysis_type in ['r1', 'r2'] and not dep_check.C_module_exp_fn: 
463              error_message("Relaxation curve fitting is not available.  Try compiling the C modules on your platform.") 
464              return 
465   
466           
467          if analysis_type == 'relax_disp' and not dep_check.C_module_exp_fn: 
468              error_message("Relaxation curve fitting will not available for this dispersion analysis.  Try compiling the C modules on your platform if you have measured full exponential curves.") 
469   
470           
471          wx.Yield() 
472          wx.BeginBusyCursor() 
473          self.gui.Freeze() 
474   
475           
476          if self.init_state: 
477               
478              sizer = wx.BoxSizer(wx.VERTICAL) 
479   
480               
481              self.notebook = wx.Notebook(self.gui, -1, style=wx.NB_TOP) 
482              sizer.Add(self.notebook, 1, wx.ALL|wx.EXPAND, 0) 
483   
484               
485              self.gui.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.on_page_changing) 
486              self.gui.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.on_page_changed) 
487   
488               
489              old_sizer = self.gui.GetSizer() 
490              old_sizer.DeleteWindows() 
491   
492               
493              self.gui.SetSizer(sizer) 
494              sizer.Layout() 
495   
496           
497          classes = { 
498              'noe': Auto_noe, 
499              'r1':  Auto_r1, 
500              'r2':  Auto_r2, 
501              'mf':  Auto_model_free, 
502              'relax_disp':  Auto_relax_disp 
503          } 
504   
505           
506          if analysis_type not in classes.keys(): 
507              raise RelaxError("The analysis '%s' is unknown." % analysis_type) 
508   
509           
510          analysis = classes[analysis_type](parent=self.notebook, id=-1, gui=self.gui, analysis_name=analysis_name, pipe_name=pipe_name, pipe_bundle=pipe_bundle, uf_exec=uf_exec, data_index=index) 
511   
512           
513          if not analysis.init_flag: 
514               
515              if self.init_state: 
516                  self.set_init_state() 
517   
518               
519              return 
520   
521           
522          self._analyses.append(analysis) 
523   
524           
525          self.notebook.AddPage(self._analyses[-1], analysis_name) 
526   
527           
528          self._num_analyses += 1 
529   
530           
531          if self._switch_flag: 
532              self.switch_page(self._num_analyses-1) 
533   
534           
535          self.init_state = False 
536   
537           
538          self.gui.Layout() 
539   
540           
541          self.gui.Thaw() 
542          if wx.IsBusy(): 
543              wx.EndBusyCursor() 
544   
545           
546          status.observers.gui_analysis.notify() 
 547   
548   
549 -    def on_page_changing(self, event): 
 550          """Block page changing if needed. 
551   
552          @param event:   The wx event. 
553          @type event:    wx event 
554          """ 
555   
556           
557          if status.exec_lock.locked(): 
558               
559              error_message("Cannot change analyses, relax is currently executing.", "relax execution lock") 
560   
561               
562              event.Veto() 
 563   
564   
565 -    def on_page_changed(self, event): 
 566          """Handle page changes. 
567   
568          @param event:   The wx event. 
569          @type event:    wx event 
570          """ 
571   
572           
573          self._current = event.GetSelection() 
574   
575           
576          if ds.is_empty(): 
577              return 
578   
579           
580          if self._switch_flag and pipes.cdp_name() != ds.relax_gui.analyses[self._current].pipe_name: 
581              self.gui.interpreter.apply('pipe.switch', ds.relax_gui.analyses[self._current].pipe_name) 
582   
583           
584          event.Skip() 
585   
586           
587          status.observers.gui_analysis.notify() 
 588   
589   
590 -    def page_index_from_bundle(self, bundle): 
 591          """Find the analysis associated with the data pipe bundle and return its page index. 
592   
593          @param bundle:  The data pipe bundle to find the page of. 
594          @type bundle:   str 
595          @return:        The page index. 
596          @rtype:         int or None 
597          """ 
598   
599           
600          index = None 
601          for i in range(self._num_analyses): 
602               
603              if ds.relax_gui.analyses[i].pipe_bundle == bundle: 
604                  index = i 
605                  break 
606   
607           
608          return index 
 609   
610   
611 -    def page_name_from_bundle(self, bundle): 
 612          """Find the analysis associated with the bundle and return its name. 
613   
614          @param bundle:  The data pipe bundle to find the page of. 
615          @type bundle:   str 
616          @return:        The page name. 
617          @rtype:         str or None 
618          """ 
619   
620           
621          index = self.page_index_from_bundle(bundle) 
622   
623           
624          if index == None: 
625              return 
626   
627           
628          return ds.relax_gui.analyses[index].analysis_name 
 629   
630   
646   
647   
649          """Switch the page to the given or current data pipe. 
650   
651          @keyword pipe:  The pipe associated with the page to switch to.  If not supplied, the current data pipe will be used. 
652          @type pipe:     str or None 
653          """ 
654   
655           
656          if pipe == None: 
657              pipe = pipes.cdp_name() 
658   
659           
660          if pipe == None: 
661              return 
662   
663           
664          index = self.page_index_from_bundle(pipes.get_bundle(pipe)) 
665   
666           
667          if index == None: 
668              return 
669   
670           
671          if self._current == index: 
672              return 
673   
674           
675          self.switch_page(index) 
676   
677           
678          status.observers.gui_analysis.notify() 
 679   
680   
681 -    def post_reset(self): 
 682          """Post relax data store reset event handler.""" 
683   
684           
685          if status.debug: 
686              fn_name = sys._getframe().f_code.co_name 
687              mod_name = inspect.getmodule(inspect.stack()[1][0]).__name__ 
688              class_name = self.__class__.__name__ 
689              full_name = "%s.%s.%s" % (mod_name, class_name, fn_name) 
690              print("\n\n") 
691              print("debug> %s:  Deleting all analyses." % full_name) 
692   
693           
694          if status.debug: 
695              print("debug> %s:  Unregistering all methods with the observer objects." % full_name) 
696          for i in range(self._num_analyses): 
697              self._analyses[i].observer_register(remove=True) 
698   
699           
700          while self._num_analyses: 
701               
702              index = self._num_analyses - 1 
703   
704               
705              if hasattr(self, 'notebook'): 
706                  self.notebook.DeletePage(index) 
707   
708               
709              self._analyses.pop(index) 
710   
711               
712              self._num_analyses -= 1 
713   
714           
715          self.set_init_state() 
 716   
717   
719          """Revert to the initial state.""" 
720   
721           
722          self.init_state = True 
723          self._current = None 
724   
725           
726          old_sizer = self.gui.GetSizer() 
727          old_sizer.DeleteWindows() 
728   
729           
730          if hasattr(self, 'notebook'): 
731              del self.notebook 
732   
733           
734          if not hasattr(ds, 'relax_gui'): 
735              ds.relax_gui = Gui() 
736   
737           
738          self.gui.add_start_screen() 
 739   
740   
741 -    def switch_page(self, index): 
 742          """Switch to the given page. 
743   
744          @param index:   The index of the page to switch to. 
745          @type index:    int 
746          """ 
747   
748           
749          self._current = index 
750   
751           
752          if pipes.cdp_name() != ds.relax_gui.analyses[self._current].pipe_name: 
753              self.gui.interpreter.apply('pipe.switch', ds.relax_gui.analyses[self._current].pipe_name) 
754   
755           
756          wx.CallAfter(self.notebook.SetSelection, self._current) 
757   
758           
759          wx.CallAfter(status.observers.gui_analysis.notify) 
  760