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.wizard import Analysis_wizard
40 from gui.message import error_message, Question
41 from lib.errors import RelaxError
42 from pipe_control import pipes
43 from pipe_control.reset import reset
44 from status import Status; status = Status()
45
46
47
48 __all__ = ['auto_model_free',
49 'auto_noe',
50 'auto_r1',
51 'auto_r2',
52 'auto_rx_base',
53 'base',
54 'elements',
55 'relax_control',
56 'results_analysis',
57 'results']
58
59
61 """Class for controlling all aspects of analyses."""
62
64 """Initialise the analysis controller.
65
66 @param gui: The gui object.
67 @type gui: wx object
68 """
69
70
71 self.gui = gui
72
73
74 self.init_state = True
75 self._current = None
76 self._num_analyses = 0
77 self._switch_flag = True
78
79
80 self._analyses = []
81
82
83 self.name = 'notebook page switcher'
84 status.observers.pipe_alteration.register(self.name, self.pipe_switch, method_name='pipe_switch')
85
86
87 status.observers.pipe_alteration.register('notebook pipe deletion', self.pipe_deletion, method_name='pipe_deletion')
88
89
90 status.observers.reset.register('gui analyses', self.post_reset, method_name='post_reset')
91
92
93 status.observers.state_load.register('gui analyses', self.load_from_store, method_name='load_from_store')
94
95
97 """Loop over the analyses, yielding the data objects.
98
99 @return: The analysis data object from the relax data store.
100 @rtype: data.gui.Analyses instance
101 """
102
103
104 for i in range(self._num_analyses):
105 yield ds.relax_gui.analyses[i]
106
107
109 """Loop over the analyses, yielding the page objects.
110
111 @return: The page object.
112 @rtype: wx.Frame object
113 """
114
115
116 for i in range(self._num_analyses):
117 yield self._analyses[i]
118
119
121 """Return the data container of the current analysis from the relax data store.
122
123 @return: The data container of the current analysis.
124 @rtype: str
125 """
126
127
128 if self._current == None:
129 return
130
131
132 return ds.relax_gui.analyses[self._current]
133
134
136 """Return the name of the current analysis.
137
138 @return: The name of the current analysis.
139 @rtype: str
140 """
141
142
143 if self._current == None:
144 return
145
146
147 return ds.relax_gui.analyses[self._current].analysis_name
148
149
151 """Return the type of the current analysis.
152
153 @return: The type of the current analysis.
154 @rtype: str
155 """
156
157
158 if self._current == None:
159 return
160
161
162 return ds.relax_gui.analyses[self._current].analysis_type
163
164
166 """Remove all analyses."""
167
168
169 if status.debug:
170 fn_name = sys._getframe().f_code.co_name
171 mod_name = inspect.getmodule(inspect.stack()[1][0]).__name__
172 class_name = self.__class__.__name__
173 full_name = "%s.%s.%s" % (mod_name, class_name, fn_name)
174 print("\n\n")
175 print("debug> %s: Deleting all analyses." % full_name)
176
177
178 if status.debug:
179 print("debug> %s: Unregistering all methods with the observer objects." % full_name)
180 for i in range(self._num_analyses):
181 self._analyses[i].observer_register(remove=True)
182
183
184 while self._num_analyses:
185
186
187
188
189 if status.debug:
190 print("debug> %s: Deleting the analysis at index %s." % (full_name, self._num_analyses-1))
191 self.delete_analysis(self._num_analyses-1)
192
193
194 if status.debug:
195 print("debug> %s: All analyses now deleted." % full_name)
196 status.observers.gui_analysis.notify()
197
198
200 """Delete the analysis tab and data store corresponding to the index.
201
202 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.
203
204
205 @param index: The index of the analysis to delete.
206 @type index: int
207 """
208
209
210 if status.debug:
211 fn_name = sys._getframe().f_code.co_name
212 mod_name = inspect.getmodule(inspect.stack()[1][0]).__name__
213 class_name = self.__class__.__name__
214 full_name = "%s.%s.%s" % (mod_name, class_name, fn_name)
215 print("\n\n")
216 print("debug> %s: Deleting the analysis at index %s." % (full_name, index))
217
218
219 self._num_analyses -= 1
220
221
222 if self._current > index:
223 self._current -= 1
224 if status.debug:
225 print("debug> %s: Switching the current analysis to index %s." % (full_name, self._current))
226
227
228 if hasattr(self._analyses[index], 'delete'):
229 if status.debug:
230 print("debug> %s: Executing the analysis specific delete() method." % full_name)
231 self._analyses[index].delete()
232
233
234 if status.debug:
235 print("debug> %s: Deleting the notebook page." % full_name)
236 self.notebook.DeletePage(index)
237
238
239 if status.debug:
240 print("debug> %s: Deleting the analysis GUI object." % full_name)
241 self._analyses.pop(index)
242
243
244 if index == self._current and self._current != 0:
245 if status.debug:
246 print("debug> %s: Switching to page %s." % (full_name, self._current-1))
247 self.switch_page(self._current-1)
248
249
250 if self._num_analyses == 0:
251 if status.debug:
252 print("debug> %s: Setting the initial state." % full_name)
253 self.set_init_state()
254
255
256 status.observers.gui_analysis.notify()
257
258
259 pipe_bundle = ds.relax_gui.analyses[index].pipe_bundle
260
261
262 if status.debug:
263 print("debug> %s: Deleting the data store object." % full_name)
264 ds.relax_gui.analyses.pop(index)
265
266
267 for pipe in pipes.pipe_names():
268 if pipes.get_bundle(pipe) == pipe_bundle:
269 if status.debug:
270 print("debug> %s: Deleting the data pipe '%s' from the '%s' bundle." % (full_name, pipe, pipe_bundle))
271 pipes.delete(pipe)
272
273
274 - def get_page_from_name(self, name):
275 """Return the page corresponding to the given name.
276
277 @return: The page which matches the given name, or nothing otherwise.
278 @rtype: wx.Frame object or None
279 """
280
281
282 found = False
283 for index in range(self._num_analyses):
284
285 if name == ds.relax_gui.analyses[index].analysis_name:
286 found = True
287 break
288
289
290 if not found:
291 return
292
293
294 return self._analyses[index]
295
296
298 """Recreate the analyses from the relax data store."""
299
300
301 if not hasattr(ds, 'relax_gui'):
302 return
303
304
305 map = {'NOE': 'noe',
306 'R1': 'r1',
307 'R2': 'r2',
308 'model-free': 'mf'}
309
310
311 for i in range(len(ds.relax_gui.analyses)):
312
313 if hasattr(ds.relax_gui.analyses[i], 'analysis_name'):
314 analysis_name = ds.relax_gui.analyses[i].analysis_name
315 elif ds.relax_gui.analyses[i].analysis_type == 'NOE':
316 analysis_name = 'Steady-state NOE'
317 elif ds.relax_gui.analyses[i].analysis_type == 'R1':
318 analysis_name = 'R1 relaxation'
319 elif ds.relax_gui.analyses[i].analysis_type == 'R2':
320 analysis_name = 'R2 relaxation'
321 elif ds.relax_gui.analyses[i].analysis_type == 'model-free':
322 analysis_name = 'Model-free'
323
324
325 if not hasattr(ds.relax_gui.analyses[i], 'pipe_bundle'):
326
327 ds.relax_gui.analyses[i].pipe_bundle = ds.relax_gui.analyses[i].pipe_name
328
329
330 self.gui.interpreter.apply('pipe.bundle', pipe=ds.relax_gui.analyses[i].pipe_name, bundle=ds.relax_gui.analyses[i].pipe_name)
331
332
333 self._switch_flag = False
334 self.new_analysis(map[ds.relax_gui.analyses[i].analysis_type], analysis_name, index=i)
335
336
337 self.pipe_switch()
338
339
340 self._switch_flag = True
341
342
343 status.observers.gui_analysis.notify()
344
345
347 """Close the currently opened analysis.
348
349 @param event: The wx event.
350 @type event: wx event
351 """
352
353
354 if not hasattr(self, 'notebook'):
355 return
356
357
358 if status.exec_lock.locked():
359 return
360
361
362 index = self.notebook.GetSelection()
363
364
365 msg = "Are you sure you would like to close the current %s analysis tab?" % ds.relax_gui.analyses[index].analysis_type
366 if status.show_gui and Question(msg, title="Close current analysis", size=(350, 140), default=False).ShowModal() == wx.ID_NO:
367 return
368
369
370 self.delete_analysis(index)
371
372
374 """Close all analyses.
375
376 @param event: The wx event.
377 @type event: wx event
378 """
379
380
381 if not hasattr(self, 'notebook'):
382 return
383
384
385 if status.exec_lock.locked():
386 return
387
388
389 msg = "Are you sure you would like to close all analyses? All data will be erased and the relax data store reset."
390 if status.show_gui and Question(msg, title="Close all analyses", size=(350, 150), default=False).ShowModal() == wx.ID_NO:
391 return
392
393
394 self.delete_all()
395
396
397 reset()
398
399
427
428
429 - def new_analysis(self, analysis_type=None, analysis_name=None, pipe_name=None, pipe_bundle=None, uf_exec=[], index=None):
430 """Initialise a new analysis.
431
432 @keyword analysis_type: The type of analysis to initialise. This can be one of 'noe', 'r1', 'r2', or 'mf'.
433 @type analysis_type: str
434 @keyword analysis_name: The name of the analysis to initialise.
435 @type analysis_name: str
436 @keyword pipe_name: The name of the original data pipe to create for the analysis.
437 @type pipe_name: str
438 @keyword pipe_bundle: The name of the data pipe bundle to associate with this analysis.
439 @type pipe_bundle: str
440 @keyword uf_exec: The list of user function on_execute methods returned from the new analysis wizard.
441 @type uf_exec: list of methods
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, uf_exec=uf_exec, 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