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