1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 """Main module for the relax graphical user interface."""
26
27
28 from os import F_OK, access, getcwd, sep
29 import platform
30 from re import search
31 from string import split
32 import sys
33 from textwrap import wrap
34 from time import sleep
35 import webbrowser
36 import wx
37
38
39 import ansi
40 from data import Relax_data_store; ds = Relax_data_store()
41 from data.gui import Gui
42 from info import Info_box
43 from generic_fns import state
44 from generic_fns.pipes import cdp_name
45 from generic_fns.reset import reset
46 from relax_errors import RelaxNoPipeError
47 from relax_io import io_streams_restore
48 from status import Status; status = Status()
49 import test_suite.test_suite_runner
50 from version import version
51
52
53 from gui.about import About_relax
54 from gui.analyses import Analysis_controller
55 from gui.spin_viewer.frame import Spin_view_window
56 from gui.controller import Controller
57 from gui.export_bmrb import Export_bmrb_window
58 from gui.filedialog import RelaxFileDialog
59 from gui.fonts import font
60 from gui.icons import Relax_task_bar_icon, relax_icons
61 from gui.interpreter import Interpreter
62 from gui.menu import Menu
63 from gui.message import error_message, Question
64 from gui.misc import gui_raise, open_file, protected_exec
65 from gui import paths
66 from gui.pipe_editor import Pipe_editor
67 from gui.references import References
68 from gui.relax_prompt import Prompt
69 from gui.results_viewer import Results_viewer
70 from gui.components.free_file_format import Free_file_format_window
71 from gui.string_conv import gui_to_str
72 from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
73
74
75 -class Main(wx.Frame):
76 """The main GUI class."""
77
78
79 min_width = 1000
80 min_height = 600
81
82 - def __init__(self, parent=None, id=-1, title=""):
83 """Initialise the main relax GUI frame."""
84
85
86 status.wx_info = {}
87 status.wx_info["version"] = split(wx.__version__, '.')
88 status.wx_info["minor"] = "%s.%s" % (status.wx_info["version"][0], status.wx_info["version"][1])
89 status.wx_info["os"] = sys.platform
90 status.wx_info["build"] = None
91 if search('gtk2', wx.version()):
92 status.wx_info["build"] = 'gtk'
93 elif search('cocoa', wx.version()):
94 status.wx_info["build"] = 'cocoa'
95 elif search('mac-unicode', wx.version()):
96 status.wx_info["build"] = 'carbon'
97 status.wx_info["full"] = None
98 if status.wx_info["build"]:
99 status.wx_info["full"] = "%s-%s" % (status.wx_info["os"], status.wx_info["build"])
100
101
102 self.test_suite_flag = False
103
104
105 style = wx.DEFAULT_FRAME_STYLE
106 if not status.debug and status.wx_info["os"] != 'darwin':
107 style = style | wx.MAXIMIZE
108
109
110 super(Main, self).__init__(parent=parent, id=id, title=title, style=style)
111
112
113 if not status.debug and status.wx_info["os"] != 'darwin':
114 self.Maximize()
115
116
117 font.setup()
118
119
120 relax_icons.setup()
121 self.SetIcons(relax_icons)
122
123
124
125
126
127
128 self.launch_dir = getcwd()
129
130
131 self.Layout()
132 self.SetSize((self.min_width, self.min_height))
133 self.SetMinSize((self.min_width, self.min_height))
134 self.Centre()
135
136
137 self.analysis = Analysis_controller(self)
138
139
140 self.calc_threads = []
141
142
143 self.init_data()
144
145
146 self.menu = Menu(self)
147
148
149 self.build_toolbar()
150
151
152 self.controller = Controller(self)
153
154
155 self.SetTitle("relax " + version)
156
157
158 self.status_bar = self.CreateStatusBar(3, 0)
159 self.status_bar.SetStatusWidths([-4, -1, -2])
160 self.update_status_bar()
161
162
163 self.add_start_screen()
164
165
166 self.Bind(wx.EVT_CLOSE, self.exit_gui)
167
168
169 self.interpreter = Interpreter()
170
171
172 status.observers.pipe_alteration.register('status bar', self.update_status_bar)
173 status.observers.result_file.register('gui', self.show_results_viewer_no_warn)
174 status.observers.exec_lock.register('gui', self.enable)
175
176
177 self.analysis.load_from_store()
178
179
180 - def about_relax(self, event=None):
181 """The about message for relax.
182
183 @keyword event: The wx event.
184 @type event: wx event
185 """
186
187
188 dialog = About_relax(None, -1)
189
190
191 if status.show_gui:
192 dialog.Show()
193
194
195 - def action_export_bmrb(self, event=None):
196 """Export the contents of the current data pipe for BMRB deposition.
197
198 @keyword event: The wx event.
199 @type event: wx event
200 """
201
202
203 if not cdp_name():
204 gui_raise(RelaxNoPipeError())
205 return
206
207
208 Export_bmrb_window(self)
209
210
211 - def action_state_save(self, event=None):
212 """Save the program state.
213
214 @keyword event: The wx event.
215 @type event: wx event
216 """
217
218
219 if not self.save_file:
220 self.action_state_save_as(event)
221 return
222
223
224 self.state_save()
225
226
227 - def action_state_save_as(self, event=None):
228 """Save the program state with file name selection.
229
230 @keyword event: The wx event.
231 @type event: wx event
232 """
233
234
235 dialog = RelaxFileDialog(parent=self, message='Select the relax save state file', defaultFile='state.bz2', wildcard='relax save file (*.bz2)|*.bz2', style=wx.FD_SAVE)
236
237
238 if status.show_gui and dialog.ShowModal() != wx.ID_OK:
239
240 return
241
242
243 file_name = dialog.get_file()
244
245
246 self.save_file = file_name
247
248
249 self.state_save()
250
251
253 """Create a start screen for the main window when no analyses exist."""
254
255
256 sizer = wx.BoxSizer(wx.VERTICAL)
257 self.SetSizer(sizer)
258
259
260 image = wx.StaticBitmap(self, -1, wx.Bitmap(paths.IMAGE_PATH+'ulysses_shadowless_400x168.png', wx.BITMAP_TYPE_ANY))
261
262
263 sizer.AddStretchSpacer()
264 sizer.Add(image, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
265 sizer.AddStretchSpacer()
266
267
268 self.Layout()
269 self.Refresh()
270
271
273 """Create the toolbar."""
274
275
276 self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL|wx.TB_FLAT)
277
278
279 self.TB_FILE_NEW = wx.NewId()
280 self.toolbar.AddLabelTool(self.TB_FILE_NEW, "New analysis", wx.Bitmap(paths.icon_22x22.new, wx.BITMAP_TYPE_ANY), shortHelp="New analysis")
281 self.Bind(wx.EVT_TOOL, self.analysis.menu_new, id=self.TB_FILE_NEW)
282
283
284 self.TB_FILE_CLOSE = wx.NewId()
285 self.toolbar.AddLabelTool(self.TB_FILE_CLOSE, "Close analysis", wx.Bitmap(paths.icon_22x22.document_close, wx.BITMAP_TYPE_ANY), shortHelp="Close analysis")
286 self.Bind(wx.EVT_TOOL, self.analysis.menu_close, id=self.TB_FILE_CLOSE)
287
288
289 self.TB_FILE_CLOSE_ALL = wx.NewId()
290 self.toolbar.AddLabelTool(self.TB_FILE_CLOSE_ALL, "Close all analyses", wx.Bitmap(paths.icon_22x22.dialog_close, wx.BITMAP_TYPE_ANY), shortHelp="Close all analyses")
291 self.Bind(wx.EVT_TOOL, self.analysis.menu_close_all, id=self.TB_FILE_CLOSE_ALL)
292
293
294 self.toolbar.AddSeparator()
295
296
297 self.TB_FILE_OPEN = wx.NewId()
298 self.toolbar.AddLabelTool(self.TB_FILE_OPEN, "Open relax state", wx.Bitmap(paths.icon_22x22.document_open, wx.BITMAP_TYPE_ANY), shortHelp="Open relax state")
299 self.Bind(wx.EVT_TOOL, self.state_load, id=self.TB_FILE_OPEN)
300
301
302 self.TB_FILE_SAVE = wx.NewId()
303 self.toolbar.AddLabelTool(self.TB_FILE_SAVE, "Save relax state", wx.Bitmap(paths.icon_22x22.document_save, wx.BITMAP_TYPE_ANY), shortHelp="Save relax state")
304 self.Bind(wx.EVT_TOOL, self.action_state_save, id=self.TB_FILE_SAVE)
305
306
307 self.TB_FILE_SAVE_AS = wx.NewId()
308 self.toolbar.AddLabelTool(self.TB_FILE_SAVE_AS, "Save as", wx.Bitmap(paths.icon_22x22.document_save_as, wx.BITMAP_TYPE_ANY), shortHelp="Save as")
309 self.Bind(wx.EVT_TOOL, self.action_state_save_as, id=self.TB_FILE_SAVE_AS)
310
311
312 self.toolbar.AddSeparator()
313
314
315 self.TB_VIEW_CONTROLLER = wx.NewId()
316 self.toolbar.AddLabelTool(self.TB_VIEW_CONTROLLER, "Controller", wx.Bitmap(paths.icon_22x22.preferences_system_performance, wx.BITMAP_TYPE_ANY), shortHelp="relax controller")
317 self.Bind(wx.EVT_TOOL, self.show_controller, id=self.TB_VIEW_CONTROLLER)
318
319
320 self.TB_VIEW_SPIN_VIEW = wx.NewId()
321 self.toolbar.AddLabelTool(self.TB_VIEW_SPIN_VIEW, "Spin viewer", wx.Bitmap(paths.icon_22x22.spin, wx.BITMAP_TYPE_ANY), shortHelp="Spin viewer window")
322 self.Bind(wx.EVT_TOOL, self.show_tree, id=self.TB_VIEW_SPIN_VIEW)
323
324
325 self.TB_VIEW_RESULTS = wx.NewId()
326 self.toolbar.AddLabelTool(self.TB_VIEW_RESULTS, "Results viewer", wx.Bitmap(paths.icon_22x22.view_statistics, wx.BITMAP_TYPE_ANY), shortHelp="Results viewer window")
327 self.Bind(wx.EVT_TOOL, self.show_results_viewer, id=self.TB_VIEW_RESULTS)
328
329
330 self.TB_VIEW_PIPE_EDIT = wx.NewId()
331 self.toolbar.AddLabelTool(self.TB_VIEW_PIPE_EDIT, "Data pipe editor", wx.Bitmap(paths.icon_22x22.pipe, wx.BITMAP_TYPE_ANY), shortHelp="Data pipe editor")
332 self.Bind(wx.EVT_TOOL, self.show_pipe_editor, id=self.TB_VIEW_PIPE_EDIT)
333
334
335 self.TB_VIEW_PROMPT = wx.NewId()
336 self.toolbar.AddLabelTool(self.TB_VIEW_PROMPT, "relax prompt", wx.Bitmap(paths.icon_22x22.relax_prompt, wx.BITMAP_TYPE_ANY), shortHelp="The relax prompt GUI window")
337 self.Bind(wx.EVT_TOOL, self.show_prompt, id=self.TB_VIEW_PROMPT)
338
339
340 self.toolbar.Realize()
341
342
343 - def close_windows(self):
344 """Throw a warning to close all of the non-essential windows when execution is locked.
345
346 This is to speed up the calculations by avoiding window updates.
347 """
348
349
350 win_list = []
351
352
353 if hasattr(self, 'spin_viewer') and self.spin_viewer.IsShown():
354 win_list.append('The spin viewer window')
355
356
357 if hasattr(self, 'pipe_editor') and self.pipe_editor.IsShown():
358 win_list.append('The data pipe editor window')
359
360
361 if hasattr(self, 'results_viewer') and self.results_viewer.IsShown():
362 win_list.append('The results viewer window')
363
364
365 if not len(win_list):
366 return
367
368
369 text = "The following windows are currently open:\n\n"
370 for win in win_list:
371 text = "%s\t%s.\n" % (text, win)
372 text = text + "\nClosing these will significantly speed up the calculations."
373
374
375 dlg = wx.MessageDialog(self, text, caption="Close windows", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP)
376 if status.show_gui:
377 dlg.ShowModal()
378
379
380 else:
381 sys.stderr.write(text)
382
383
385 """Write an email to the relax mailing-list using the standard mailing program.
386
387 @keyword event: The wx event.
388 @type event: wx event
389 """
390
391 webbrowser.open_new('mailto:relax-users@gna.org')
392
393
395 """Enable and disable certain parts of the main window with the execution lock."""
396
397
398 enable = False
399 if not status.exec_lock.locked():
400 enable = True
401
402
403 wx.CallAfter(self.toolbar.EnableTool, self.TB_FILE_NEW, enable)
404 wx.CallAfter(self.toolbar.EnableTool, self.TB_FILE_CLOSE, enable)
405 wx.CallAfter(self.toolbar.EnableTool, self.TB_FILE_CLOSE_ALL, enable)
406 wx.CallAfter(self.toolbar.EnableTool, self.TB_FILE_OPEN, enable)
407 wx.CallAfter(self.toolbar.EnableTool, self.TB_FILE_SAVE, enable)
408 wx.CallAfter(self.toolbar.EnableTool, self.TB_FILE_SAVE_AS, enable)
409
410
411 - def exit_gui(self, event=None):
412 """Catch the main window closure and perform the exit procedure.
413
414 @keyword event: The wx event.
415 @type event: wx event
416 """
417
418
419 doexit = wx.ID_YES
420 if status.show_gui and not ds.is_empty():
421 doexit = Question('Are you sure you would like to quit relax? All unsaved data will be lost.', title='Exit relax', default=True).ShowModal()
422
423
424 if doexit == wx.ID_YES:
425
426 io_streams_restore(verbosity=0)
427
428
429 info = Info_box()
430
431
432 if platform.uname()[0] in ['Windows', 'Microsoft']:
433 width = 80
434 else:
435 width = 100
436
437
438 text = "\n\nThank you for citing:\n"
439 text += "\n\n%srelaxGUI%s\n\n" % (ansi.relax_prompt, ansi.end)
440 for line in wrap(info.bib['Bieri11'].cite_short(), width):
441 text += line + '\n'
442 text += "\n\n\n%srelax%s\n\n" % (ansi.relax_prompt, ansi.end)
443 for line in wrap(info.bib['dAuvergneGooley08a'].cite_short(), width):
444 text += line + '\n'
445 text += '\n'
446 for line in wrap(info.bib['dAuvergneGooley08b'].cite_short(), width):
447 text += line + '\n'
448 text += '\n'
449 text += '\n'
450 sys.stdout.write(text)
451
452
453 if hasattr(self, 'taskbar_icon'):
454 self.taskbar_icon.Destroy()
455
456
457 wx.Exit()
458
459
460 - def init_data(self):
461 """Initialise the data used by the GUI interface."""
462
463
464 self.save_file = None
465
466
467 if not hasattr(ds, 'relax_gui'):
468 ds.relax_gui = Gui()
469
470
472 """Open the free file format settings window.
473
474 @keyword event: The wx event.
475 @type event: wx event
476 """
477
478
479 win = Free_file_format_window()
480
481
482 if status.show_gui:
483 win.Show()
484
485
486 - def references(self, event=None):
487 """Display the references relevant for relax.
488
489 @keyword event: The wx event.
490 @type event: wx event
491 """
492
493
494 self.references = References(self)
495 if status.show_gui:
496 self.references.Show()
497
498
499 - def relax_manual(self, event=None):
500 """Display the relax manual.
501
502 @keyword event: The wx event.
503 @type event: wx event
504 """
505
506
507 file = status.install_path + sep+"docs"+sep+"relax.pdf"
508
509
510 if not access(file, F_OK):
511 error_message("The relax manual '%s' cannot be found. Please compile using the scons program." % file)
512 return
513
514
515 open_file(file)
516
517
518 - def run_test_suite(self, event=None, categories=['system', 'unit', 'gui']):
519 """Execute the full test suite.
520
521 @keyword event: The wx event.
522 @type event: wx event
523 @keyword categories: The list of test categories to run, for example ['system', 'unit', 'gui'] for all tests.
524 @type categories: list of str
525 """
526
527
528 msg = "In running the test suite, relax will be reset and all data lost. Are you sure you would like to run the test suite?"
529 if Question(msg, parent=self, default=False).ShowModal() == wx.ID_NO:
530 return
531
532
533 self.test_suite_flag = True
534
535
536 wx.BeginBusyCursor()
537
538
539 orig_style = self.controller.GetWindowStyle()
540 self.controller.SetWindowStyle(orig_style | wx.STAY_ON_TOP)
541 self.controller.Refresh()
542
543
544 self.controller.MakeModal(True)
545
546
547 if hasattr(self, 'spin_viewer'):
548 self.spin_viewer.Close()
549 if hasattr(self, 'pipe_editor'):
550 self.pipe_editor.Close()
551 if hasattr(self, 'results_viewer'):
552 self.results_viewer.Close()
553 if hasattr(self, 'relax_prompt'):
554 self.relax_prompt.Close()
555
556
557 reset()
558
559
560 self.show_controller(event)
561
562
563 wx.GetApp().Yield(True)
564
565
566 status.show_gui = False
567
568
569 runner = test_suite.test_suite_runner.Test_suite_runner([], from_gui=True, categories=categories)
570 runner.run_all_tests()
571
572
573 status.show_gui = True
574
575
576 if wx.IsBusy():
577 wx.EndBusyCursor()
578
579
580 self.controller.SetWindowStyle(orig_style)
581 self.controller.MakeModal(False)
582 self.controller.Refresh()
583
584
585 self.test_suite_flag = False
586
587
588 wx.CallAfter(self.controller.main_gauge.SetValue, 100)
589
590
591 - def run_test_suite_gui(self, event=None):
592 """Execute the GUI tests.
593
594 @keyword event: The wx event.
595 @type event: wx event
596 """
597
598
599 self.run_test_suite(event, categories=['gui'])
600
601
602 - def run_test_suite_sys(self, event=None):
603 """Execute the system tests.
604
605 @keyword event: The wx event.
606 @type event: wx event
607 """
608
609
610 self.run_test_suite(event, categories=['system'])
611
612
613 - def run_test_suite_unit(self, event=None):
614 """Execute the unit tests.
615
616 @keyword event: The wx event.
617 @type event: wx event
618 """
619
620
621 self.run_test_suite(event, categories=['unit'])
622
623
624 - def show_controller(self, event=None):
625 """Display the relax controller window.
626
627 @keyword event: The wx event.
628 @type event: wx event
629 """
630
631
632 if self.controller.IsShown():
633 self.controller.Raise()
634 return
635
636
637 if status.show_gui:
638 self.controller.Show()
639
640
641 - def show_pipe_editor(self, event=None):
642 """Display the data pipe editor window.
643
644 @keyword event: The wx event.
645 @type event: wx event
646 """
647
648
649 if status.exec_lock.locked():
650 dlg = wx.MessageDialog(self, "Leaving the pipe editor window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP)
651 if status.show_gui:
652 dlg.ShowModal()
653
654
655 if not hasattr(self, 'pipe_editor'):
656 self.pipe_editor = Pipe_editor(gui=self)
657
658
659 if self.pipe_editor.IsShown():
660 self.pipe_editor.Raise()
661 return
662
663
664 if status.show_gui and not self.pipe_editor.IsShown():
665 self.pipe_editor.Show()
666
667
668 - def show_prompt(self, event=None):
669 """Display the relax prompt window.
670
671 @keyword event: The wx event.
672 @type event: wx event
673 """
674
675
676 if not hasattr(self, 'relax_prompt'):
677 self.relax_prompt = Prompt(None, -1, "", parent=self)
678
679
680 if self.relax_prompt.IsShown():
681 self.relax_prompt.Raise()
682 return
683
684
685 if status.show_gui:
686 self.relax_prompt.Show()
687
688
689 - def show_results_viewer(self, event=None):
690 """Display the analysis results.
691
692 @keyword event: The wx event.
693 @type event: wx event
694 """
695
696
697 wx.CallAfter(self.show_results_viewer_safe, warn=True)
698
699
700 - def show_results_viewer_safe(self, warn=False):
701 """Display the analysis results in a thread safe wx.CallAfter call.
702
703 @keyword warn: A flag which if True will cause a message dialog to appear warning about keeping the window open with the execution lock.
704 @type warn: bool
705 """
706
707
708 if warn and status.exec_lock.locked():
709 dlg = wx.MessageDialog(self, "Leaving the results viewer window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP)
710 if status.show_gui:
711 wx.CallAfter(dlg.ShowModal)
712
713
714 if not hasattr(self, 'results_viewer'):
715 self.results_viewer = Results_viewer(self)
716
717
718 if self.results_viewer.IsShown():
719 self.results_viewer.Raise()
720 return
721
722
723 if status.show_gui and not self.results_viewer.IsShown():
724 self.results_viewer.Show()
725
726
728 """Display the analysis results."""
729
730
731 wx.CallAfter(self.show_results_viewer_safe, warn=False)
732
733
734 - def show_tree(self, event=None):
735 """Display the molecule, residue, and spin tree window.
736
737 @keyword event: The wx event.
738 @type event: wx event
739 """
740
741
742 if status.exec_lock.locked():
743 dlg = wx.MessageDialog(self, "Leaving the spin viewer window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP)
744 if status.show_gui:
745 dlg.ShowModal()
746
747
748 if not hasattr(self, 'spin_viewer'):
749 self.spin_viewer = Spin_view_window(None, -1, "", parent=self)
750
751
752 if self.spin_viewer.IsShown():
753 self.spin_viewer.Raise()
754 return
755
756
757 if status.show_gui and not self.spin_viewer.IsShown():
758 self.spin_viewer.Show()
759
760
761 - def state_load(self, event=None, file_name=None):
762 """Load the program state.
763
764 @keyword event: The wx event.
765 @type event: wx event
766 @keyword file_name: The name of the file to load (for dialogless operation).
767 @type file_name: str
768 """
769
770
771 if status.exec_lock.locked():
772 return
773
774
775 if not self.analysis.init_state or not ds.is_empty():
776
777 msg = "Loading a saved relax state file will cause all unsaved data to be lost. Are you sure you would to open a save file?"
778
779
780 if status.show_gui and Question(msg, default=True, size=(400, 150)).ShowModal() == wx.ID_NO:
781 return
782
783
784 if not file_name:
785 dialog = RelaxFileDialog(parent=self, message='Select the relax save state file', defaultFile='state.bz2', wildcard='relax save files (*.bz2;*.gz)|*.bz2;*.gz|All files (*)|*', style=wx.FD_OPEN)
786
787
788 if status.show_gui and dialog.ShowModal() != wx.ID_OK:
789
790 return
791
792
793 file_name = gui_to_str(dialog.get_file())
794
795
796 wx.Yield()
797
798
799 wx.BeginBusyCursor()
800 self.Freeze()
801
802
803 try:
804
805 self.analysis.delete_all()
806
807
808 reset()
809
810
811 self.save_file = file_name
812
813
814 if protected_exec(state.load_state, file_name, verbosity=0):
815
816 self.sync_ds(upload=False)
817
818
819 else:
820
821 self.init_data()
822
823
824 finally:
825 self.Thaw()
826
827
828 if wx.IsBusy():
829 wx.EndBusyCursor()
830
831
832 - def state_save(self):
833 """Save the program state."""
834
835
836 self.sync_ds(upload=True)
837
838
839 try:
840 wx.BeginBusyCursor()
841 state.save_state(self.save_file, verbosity=0, force=True)
842
843
844 sleep(1)
845
846
847 finally:
848 if wx.IsBusy():
849 wx.EndBusyCursor()
850
851
852 - def sync_ds(self, upload=False):
853 """Synchronise the GUI and the relax data store, both ways.
854
855 This method allows the GUI information to be uploaded into the relax data store, or for the information in the relax data store to be downloaded by the GUI.
856
857 @keyword upload: A flag which if True will cause the GUI to send data to the relax data store. If False, data will be downloaded from the relax data store to update the GUI.
858 @type upload: bool
859 """
860
861
862 for page in self.analysis.analysis_loop():
863
864 if hasattr(page, 'sync_ds'):
865 page.sync_ds(upload)
866
867
869 """Update the status bar info."""
870
871
872 pipe = cdp_name()
873
874
875 if pipe == None:
876 pipe = ''
877
878
879 wx.CallAfter(self.status_bar.SetStatusText, "(C) 2001-2012 the relax development team", 0)
880 wx.CallAfter(self.status_bar.SetStatusText, "Current data pipe:", 1)
881 wx.CallAfter(self.status_bar.SetStatusText, pipe, 2)
882