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 import Relax_data_store; ds = Relax_data_store()
33 from data.gui import Gui
34 import dep_check
35 from generic_fns import pipes
36 from generic_fns.reset import reset
37 from relax_errors import RelaxError
38 from status import Status; status = Status()
39
40
41 from gui.analyses.auto_model_free import Auto_model_free
42 from gui.analyses.auto_noe import Auto_noe
43 from gui.analyses.auto_r1 import Auto_r1
44 from gui.analyses.auto_r2 import Auto_r2
45 from gui.analyses.wizard import Analysis_wizard
46 from gui.message import error_message, Question
47
48
49
50 __all__ = ['auto_model_free',
51 'auto_noe',
52 'auto_r1',
53 'auto_r2',
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 if index == self._current and self._current != 0:
247 if status.debug:
248 print("debug> %s: Switching to page %s." % (full_name, self._current-1))
249 self.switch_page(self._current-1)
250
251
252 if self._num_analyses == 0:
253 if status.debug:
254 print("debug> %s: Setting the initial state." % full_name)
255 self.set_init_state()
256
257
258 status.observers.gui_analysis.notify()
259
260
261 pipe_bundle = ds.relax_gui.analyses[index].pipe_bundle
262
263
264 if status.debug:
265 print("debug> %s: Deleting the data store object." % full_name)
266 ds.relax_gui.analyses.pop(index)
267
268
269 for pipe in pipes.pipe_names():
270 if pipes.get_bundle(pipe) == pipe_bundle:
271 if status.debug:
272 print("debug> %s: Deleting the data pipe '%s' from the '%s' bundle." % (full_name, pipe, pipe_bundle))
273 pipes.delete(pipe)
274
275
276 - def get_page_from_name(self, name):
277 """Return the page corresponding to the given name.
278
279 @return: The page which matches the given name, or nothing otherwise.
280 @rtype: wx.Frame object or None
281 """
282
283
284 found = False
285 for index in range(self._num_analyses):
286
287 if name == ds.relax_gui.analyses[index].analysis_name:
288 found = True
289 break
290
291
292 if not found:
293 return
294
295
296 return self._analyses[index]
297
298
300 """Recreate the analyses from the relax data store."""
301
302
303 if not hasattr(ds, 'relax_gui'):
304 return
305
306
307 map = {'NOE': 'noe',
308 'R1': 'r1',
309 'R2': 'r2',
310 'model-free': 'mf'}
311
312
313 for i in range(len(ds.relax_gui.analyses)):
314
315 if hasattr(ds.relax_gui.analyses[i], 'analysis_name'):
316 analysis_name = ds.relax_gui.analyses[i].analysis_name
317 elif ds.relax_gui.analyses[i].analysis_type == 'NOE':
318 analysis_name = 'Steady-state NOE'
319 elif ds.relax_gui.analyses[i].analysis_type == 'R1':
320 analysis_name = 'R1 relaxation'
321 elif ds.relax_gui.analyses[i].analysis_type == 'R2':
322 analysis_name = 'R2 relaxation'
323 elif ds.relax_gui.analyses[i].analysis_type == 'model-free':
324 analysis_name = 'Model-free'
325
326
327 if not hasattr(ds.relax_gui.analyses[i], 'pipe_bundle'):
328
329 ds.relax_gui.analyses[i].pipe_bundle = ds.relax_gui.analyses[i].pipe_name
330
331
332 self.gui.interpreter.apply('pipe.bundle', pipe=ds.relax_gui.analyses[i].pipe_name, bundle=ds.relax_gui.analyses[i].pipe_name)
333
334
335 self._switch_flag = False
336 self.new_analysis(map[ds.relax_gui.analyses[i].analysis_type], analysis_name, index=i)
337
338
339 self.pipe_switch()
340
341
342 self._switch_flag = True
343
344
345 status.observers.gui_analysis.notify()
346
347
349 """Close the currently opened analysis.
350
351 @param event: The wx event.
352 @type event: wx event
353 """
354
355
356 if not hasattr(self, 'notebook'):
357 return
358
359
360 if status.exec_lock.locked():
361 return
362
363
364 index = self.notebook.GetSelection()
365
366
367 msg = "Are you sure you would like to close the current %s analysis tab?" % ds.relax_gui.analyses[index].analysis_type
368 if status.show_gui and Question(msg, title="Close current analysis", size=(350, 140), default=False).ShowModal() == wx.ID_NO:
369 return
370
371
372 self.delete_analysis(index)
373
374
376 """Close all analyses.
377
378 @param event: The wx event.
379 @type event: wx event
380 """
381
382
383 if not hasattr(self, 'notebook'):
384 return
385
386
387 if status.exec_lock.locked():
388 return
389
390
391 msg = "Are you sure you would like to close all analyses? All data will be erased and the relax data store reset."
392 if status.show_gui and Question(msg, title="Close all analyses", size=(350, 150), default=False).ShowModal() == wx.ID_NO:
393 return
394
395
396 self.delete_all()
397
398
399 reset()
400
401
429
430
431 - def new_analysis(self, analysis_type=None, analysis_name=None, pipe_name=None, pipe_bundle=None, index=None):
432 """Initialise a new analysis.
433
434 @keyword analysis_type: The type of analysis to initialise. This can be one of 'noe', 'r1', 'r2', or 'mf'.
435 @type analysis_type: str
436 @keyword analysis_name: The name of the analysis to initialise.
437 @type analysis_name: str
438 @keyword pipe_name: The name of the original data pipe to create for the analysis.
439 @type pipe_name: str
440 @keyword pipe_bundle: The name of the data pipe bundle to associate with this analysis.
441 @type pipe_bundle: str
442 @keyword index: The index of the analysis in the relax data store (set to None if no data currently exists).
443 @type index: None or int
444 """
445
446
447 if analysis_type in ['r1', 'r2'] and not dep_check.C_module_exp_fn:
448 error_message("Relaxation curve fitting is not available. Try compiling the C modules on your platform.")
449 return
450
451
452 wx.Yield()
453 wx.BeginBusyCursor()
454 self.gui.Freeze()
455
456
457 if self.init_state:
458
459 sizer = wx.BoxSizer(wx.VERTICAL)
460
461
462 self.notebook = wx.Notebook(self.gui, -1, style=wx.NB_TOP)
463 sizer.Add(self.notebook, 1, wx.ALL|wx.EXPAND, 0)
464
465
466 self.gui.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.on_page_changing)
467 self.gui.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.on_page_changed)
468
469
470 old_sizer = self.gui.GetSizer()
471 old_sizer.DeleteWindows()
472
473
474 self.gui.SetSizer(sizer)
475 sizer.Layout()
476
477
478 classes = {'noe': Auto_noe,
479 'r1': Auto_r1,
480 'r2': Auto_r2,
481 'mf': Auto_model_free}
482
483
484 if analysis_type not in classes.keys():
485 raise RelaxError("The analysis '%s' is unknown." % analysis_type)
486
487
488 analysis = classes[analysis_type](parent=self.notebook, id=-1, gui=self.gui, analysis_name=analysis_name, pipe_name=pipe_name, pipe_bundle=pipe_bundle, data_index=index)
489
490
491 if not analysis.init_flag:
492
493 if self.init_state:
494 self.set_init_state()
495
496
497 return
498
499
500 self._analyses.append(analysis)
501
502
503 self.notebook.AddPage(self._analyses[-1], analysis_name)
504
505
506 self._num_analyses += 1
507
508
509 if self._switch_flag:
510 self.switch_page(self._num_analyses-1)
511
512
513 self.init_state = False
514
515
516 self.gui.Layout()
517
518
519 self.gui.Thaw()
520 if wx.IsBusy():
521 wx.EndBusyCursor()
522
523
524 status.observers.gui_analysis.notify()
525
526
527 - def on_page_changing(self, event):
528 """Block page changing if needed.
529
530 @param event: The wx event.
531 @type event: wx event
532 """
533
534
535 if status.exec_lock.locked():
536
537 error_message("Cannot change analyses, relax is currently executing.", "relax execution lock")
538
539
540 event.Veto()
541
542
543 - def on_page_changed(self, event):
544 """Handle page changes.
545
546 @param event: The wx event.
547 @type event: wx event
548 """
549
550
551 self._current = event.GetSelection()
552
553
554 if ds.is_empty():
555 return
556
557
558 if self._switch_flag and pipes.cdp_name() != ds.relax_gui.analyses[self._current].pipe_name:
559 self.gui.interpreter.apply('pipe.switch', ds.relax_gui.analyses[self._current].pipe_name)
560
561
562 event.Skip()
563
564
565 status.observers.gui_analysis.notify()
566
567
568 - def page_index_from_bundle(self, bundle):
569 """Find the analysis associated with the data pipe bundle and return its page index.
570
571 @param bundle: The data pipe bundle to find the page of.
572 @type bundle: str
573 @return: The page index.
574 @rtype: int or None
575 """
576
577
578 index = None
579 for i in range(self._num_analyses):
580
581 if ds.relax_gui.analyses[i].pipe_bundle == bundle:
582 index = i
583 break
584
585
586 return index
587
588
589 - def page_name_from_bundle(self, bundle):
590 """Find the analysis associated with the bundle and return its name.
591
592 @param bundle: The data pipe bundle to find the page of.
593 @type bundle: str
594 @return: The page name.
595 @rtype: str or None
596 """
597
598
599 index = self.page_index_from_bundle(bundle)
600
601
602 if index == None:
603 return
604
605
606 return ds.relax_gui.analyses[index].analysis_name
607
608
624
625
627 """Switch the page to the given or current data pipe.
628
629 @keyword pipe: The pipe associated with the page to switch to. If not supplied, the current data pipe will be used.
630 @type pipe: str or None
631 """
632
633
634 if pipe == None:
635 pipe = pipes.cdp_name()
636
637
638 if pipe == None:
639 return
640
641
642 index = self.page_index_from_bundle(pipes.get_bundle(pipe))
643
644
645 if index == None:
646 return
647
648
649 if self._current == index:
650 return
651
652
653 self.switch_page(index)
654
655
656 status.observers.gui_analysis.notify()
657
658
659 - def post_reset(self):
660 """Post relax data store reset event handler."""
661
662
663 if status.debug:
664 fn_name = sys._getframe().f_code.co_name
665 mod_name = inspect.getmodule(inspect.stack()[1][0]).__name__
666 class_name = self.__class__.__name__
667 full_name = "%s.%s.%s" % (mod_name, class_name, fn_name)
668 print("\n\n")
669 print("debug> %s: Deleting all analyses." % full_name)
670
671
672 if status.debug:
673 print("debug> %s: Unregistering all methods with the observer objects." % full_name)
674 for i in range(self._num_analyses):
675 self._analyses[i].observer_register(remove=True)
676
677
678 while self._num_analyses:
679
680 index = self._num_analyses - 1
681
682
683 if hasattr(self, 'notebook'):
684 self.notebook.DeletePage(index)
685
686
687 self._analyses.pop(index)
688
689
690 self._num_analyses -= 1
691
692
693 self.set_init_state()
694
695
697 """Revert to the initial state."""
698
699
700 self.init_state = True
701 self._current = None
702
703
704 old_sizer = self.gui.GetSizer()
705 old_sizer.DeleteWindows()
706
707
708 if hasattr(self, 'notebook'):
709 del self.notebook
710
711
712 if not hasattr(ds, 'relax_gui'):
713 ds.relax_gui = Gui()
714
715
716 self.gui.add_start_screen()
717
718
719 - def switch_page(self, index):
720 """Switch to the given page.
721
722 @param index: The index of the page to switch to.
723 @type index: int
724 """
725
726
727 self._current = index
728
729
730 wx.CallAfter(self.notebook.SetSelection, self._current)
731
732
733 wx.CallAfter(status.observers.gui_analysis.notify)
734