1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """Log window of relax GUI controlling all calculations."""
25
26
27 import sys
28 import wx
29 import wx.stc
30
31
32 import dep_check
33 from graphics import IMAGE_PATH, fetch_icon
34 from gui.components.menu import build_menu_item
35 from gui.fonts import font
36 from gui.icons import Relax_icons
37 from gui.misc import add_border, bitmap_setup
38 from gui.string_conv import str_to_gui
39 from info import Info_box
40 from lib.compat import Queue
41 from lib.io import SplitIO
42 from pipe_control.pipes import cdp_name
43 from status import Status; status = Status()
44
45
46
47 MENU_ID_FIND = wx.NewId()
48 MENU_ID_COPY = wx.NewId()
49 MENU_ID_SELECT_ALL = wx.NewId()
50 MENU_ID_ZOOM_IN = wx.NewId()
51 MENU_ID_ZOOM_OUT = wx.NewId()
52 MENU_ID_ZOOM_ORIG = wx.NewId()
53 MENU_ID_GOTO_START = wx.NewId()
54 MENU_ID_GOTO_END = wx.NewId()
55
56
57
59 """The relax controller window."""
60
62 """Set up the relax controller frame.
63
64 @param gui: The GUI object.
65 @type gui: wx.Frame instance
66 """
67
68
69 self.gui = gui
70
71
72 super(Controller, self).__init__(self.gui, -1, style=wx.DEFAULT_FRAME_STYLE)
73
74
75 self.size_x = 800
76 self.size_y = 700
77 self.border = 5
78 self.spacer = 10
79
80
81 sizer = self.setup_frame()
82
83
84 self.add_relax_logo(sizer)
85
86
87 sizer.AddSpacer(20)
88
89
90 self.name = self.add_text(self.main_panel, sizer, "Current GUI analysis:")
91
92
93 self.cdp = self.add_text(self.main_panel, sizer, "Current data pipe:")
94
95
96 self.create_rx(sizer)
97
98
99 self.create_mf(sizer)
100
101
102 self.main_gauge = self.add_gauge(self.main_panel, sizer, "Execution progress:", tooltip="This gauge will pulse while relax is executing an auto-analysis (when the execution lock is turned on) and will be set to 100% once the analysis is complete.")
103
104
105 self.log_queue = Queue()
106
107
108 self.log_panel = LogCtrl(self.main_panel, self, log_queue=self.log_queue, id=-1)
109 sizer.Add(self.log_panel, 1, wx.EXPAND|wx.ALL, 0)
110
111
112 out = Redirect_text(self.log_panel, self.log_queue, orig_io=sys.stdout, stream=0)
113 if sys.stdout == sys.__stdout__ or status.relax_mode in ['test suite', 'system tests', 'unit tests', 'GUI tests']:
114 sys.stdout = out
115 else:
116 split_stdout = SplitIO()
117 split_stdout.split(sys.stdout, out)
118 sys.stdout = split_stdout
119
120
121 err = Redirect_text(self.log_panel, self.log_queue, orig_io=sys.stderr, stream=1)
122 if sys.stderr == sys.__stderr__ or status.relax_mode in ['test suite', 'system tests', 'unit tests', 'GUI tests']:
123 sys.stderr = err
124 else:
125 split_stderr = SplitIO()
126 split_stderr.split(sys.stderr, err)
127 sys.stderr = split_stderr
128
129
130 self.update_controller()
131
132
133 self.timer = wx.Timer(self)
134 self.Bind(wx.EVT_TIMER, self.handler_timer, self.timer)
135
136
137 if not status.test_mode:
138 info = Info_box()
139 sys.stdout.write(info.intro_text())
140 sys.stdout.write("\n")
141 sys.stdout.flush()
142
143
144 self.log_panel.SetFocus()
145
146
147 status.observers.pipe_alteration.register('controller', self.update_controller, method_name='update_controller')
148 status.observers.auto_analyses.register('controller', self.update_controller, method_name='update_controller')
149 status.observers.gui_analysis.register('controller', self.update_controller, method_name='update_controller')
150 status.observers.exec_lock.register('controller', self.update_gauge, method_name='update_gauge')
151
152
153 - def add_gauge(self, parent, sizer, desc, tooltip=None):
154 """Add a gauge to the sizer and return it.
155
156 @param parent: The parent GUI element.
157 @type parent: wx object
158 @param sizer: The sizer element to pack the element into.
159 @type sizer: wx.Sizer instance
160 @param desc: The description to display.
161 @type desc: str
162 @keyword tooltip: The tooltip which appears on hovering over the text and the gauge.
163 @type tooltip: str
164 @return: The gauge element.
165 @rtype: wx.Gauge instance
166 """
167
168
169 sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
170
171
172 text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT)
173 text.SetFont(font.normal)
174 sub_sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL, 0)
175
176
177 gauge = wx.Gauge(parent, id=-1, range=100, style=wx.GA_SMOOTH)
178 gauge.SetSize((-1, 20))
179 sub_sizer.Add(gauge, 3, wx.EXPAND|wx.ALL, 0)
180
181
182 sizer.Add(sub_sizer, 0, wx.ALL|wx.EXPAND, 0)
183
184
185 sizer.AddSpacer(self.spacer)
186
187
188 if tooltip:
189 text.SetToolTip(wx.ToolTip(tooltip))
190 gauge.SetToolTip(wx.ToolTip(tooltip))
191
192
193 return gauge
194
195
197 """Add the relax logo to the sizer.
198
199 @param sizer: The sizer element to pack the relax logo into.
200 @type sizer: wx.Sizer instance
201 """
202
203
204 logo = wx.StaticBitmap(self.main_panel, -1, bitmap_setup(IMAGE_PATH+'relax.gif'))
205
206
207 sizer.Add(logo, 0, wx.TOP|wx.ALIGN_CENTER_HORIZONTAL, 0)
208
209
210 sizer.AddSpacer(self.spacer)
211
212
213 - def add_text(self, parent, sizer, desc, tooltip=None):
214 """Add the current data pipe element.
215
216 @param parent: The parent GUI element.
217 @type parent: wx object
218 @param sizer: The sizer element to pack the element into.
219 @type sizer: wx.Sizer instance
220 @param desc: The description to display.
221 @type desc: str
222 @keyword tooltip: The tooltip which appears on hovering over the text and field.
223 @type tooltip: str
224 @return: The text control.
225 @rtype: wx.TextCtrl instance
226 """
227
228
229 sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
230
231
232 text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT)
233 text.SetFont(font.normal)
234 sub_sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL, 0)
235
236
237 field = wx.TextCtrl(parent, -1, '', style=wx.ALIGN_LEFT)
238 field.SetEditable(False)
239 field.SetFont(font.normal)
240 colour = self.main_panel.GetBackgroundColour()
241 field.SetOwnBackgroundColour(colour)
242 sub_sizer.Add(field, 3, wx.ALIGN_CENTER_VERTICAL, 0)
243
244
245 sizer.Add(sub_sizer, 0, wx.ALL|wx.EXPAND, 0)
246
247
248 sizer.AddSpacer(self.spacer)
249
250
251 if tooltip:
252 text.SetToolTip(wx.ToolTip(tooltip))
253 field.SetToolTip(wx.ToolTip(tooltip))
254
255
256 field.Bind(wx.EVT_KEY_DOWN, self.handler_key_down)
257
258
259 return field
260
261
263 """Return the key for the current analysis' status object.
264
265 @return: The current analysis' status object key.
266 @rtype: str or None
267 """
268
269
270 data = self.gui.analysis.current_data()
271 if data == None:
272 return
273
274
275 if hasattr(data, 'pipe_bundle'):
276 return data.pipe_bundle
277
278
280 """Create the model-free specific panel.
281
282 @param sizer: The sizer element to pack the element into.
283 @type sizer: wx.Sizer instance
284 """
285
286
287 self.panel_mf = wx.Panel(self.main_panel, -1)
288 sizer.Add(self.panel_mf, 0, wx.ALL|wx.EXPAND, 0)
289
290
291 panel_sizer = wx.BoxSizer(wx.VERTICAL)
292 self.panel_mf.SetSizer(panel_sizer)
293
294
295 self.global_model_mf = self.add_text(self.panel_mf, panel_sizer, "Global model:", tooltip="This shows the global diffusion model of the dauvergne_protocol auto-analysis currently being optimised. It will be one of 'local_tm', 'sphere', 'prolate', 'oblate', 'ellipsoid' or 'final'.")
296
297
298 self.progress_gauge_mf = self.add_gauge(self.panel_mf, panel_sizer, "Incremental progress:", tooltip="This shows the global iteration round of the dauvergne_protocol auto-analysis. Optimisation of the global model may require between 5 to 15 iterations. The maximum number of iterations should not be reached. Once the global diffusion model has converged, this gauge will be set to 100%")
299
300
301 self.mc_gauge_mf = self.add_gauge(self.panel_mf, panel_sizer, "Monte Carlo simulations:", tooltip="The Monte Carlo simulation number. Simulations are only performed at the very end of the analysis in the 'final' global model.")
302
303
305 """Create the relaxation curve-fitting specific panel.
306
307 @param sizer: The sizer element to pack the element into.
308 @type sizer: wx.Sizer instance
309 """
310
311
312 self.panel_rx = wx.Panel(self.main_panel, -1)
313 sizer.Add(self.panel_rx, 0, wx.ALL|wx.EXPAND, 0)
314
315
316 panel_sizer = wx.BoxSizer(wx.VERTICAL)
317 self.panel_rx.SetSizer(panel_sizer)
318
319
320 self.mc_gauge_rx = self.add_gauge(self.panel_rx, panel_sizer, "Monte Carlo simulations:", tooltip="The Monte Carlo simulation number.")
321
322
324 """Event handler for the close window action.
325
326 @param event: The wx event.
327 @type event: wx event
328 """
329
330
331 if self.gui.test_suite_flag:
332 return
333
334
335 self.Hide()
336
337
339 """Event handler for key strokes.
340
341 @keyword event: The wx event.
342 @type event: wx event
343 """
344
345
346 if event.GetKeyCode() == wx.WXK_ESCAPE:
347 self.handler_close(event)
348
349
351 """Event handler for the timer.
352
353 @param event: The wx event.
354 @type event: wx event
355 """
356
357
358 wx.CallAfter(self.log_panel.write)
359
360
361 wx.CallAfter(self.main_gauge.Pulse)
362
363
364 if not status.exec_lock.locked() and self.timer.IsRunning():
365 self.timer.Stop()
366 self.update_gauge()
367
368
370 """Reset the relax controller to its initial state."""
371
372
373 if self.timer.IsRunning():
374 self.timer.Stop()
375
376
377 if hasattr(self, 'mc_gauge_rx'):
378 wx.CallAfter(self.mc_gauge_rx.SetValue, 0)
379
380
381 if hasattr(self, 'mc_gauge_mf'):
382 wx.CallAfter(self.mc_gauge_mf.SetValue, 0)
383 if hasattr(self, 'progress_gauge_mf'):
384 wx.CallAfter(self.progress_gauge_mf.SetValue, 0)
385
386
387 wx.CallAfter(self.main_gauge.SetValue, 0)
388
389
391 """Set up the relax controller frame.
392 @return: The sizer object.
393 @rtype: wx.Sizer instance
394 """
395
396
397 self.SetTitle("The relax controller")
398
399
400 self.SetIcons(Relax_icons())
401
402
403 self.main_panel = wx.Panel(self, -1)
404
405
406 main_sizer = wx.BoxSizer(wx.VERTICAL)
407 self.main_panel.SetSizer(main_sizer)
408
409
410 sizer = add_border(main_sizer, border=self.border, packing=wx.VERTICAL)
411
412
413 self.Bind(wx.EVT_CLOSE, self.handler_close)
414
415
416 self.SetSize((self.size_x, self.size_y))
417
418
419 self.Centre()
420
421
422 return sizer
423
424
466
467
469 """Update the main execution gauge."""
470
471
472 if status.exec_lock.locked():
473
474 if not self.timer.IsRunning():
475 wx.CallAfter(self.timer.Start, 100)
476
477
478 return
479
480
481 key = self.analysis_key()
482 if key and key in status.auto_analysis and status.auto_analysis[key].fin:
483
484 if self.timer.IsRunning():
485 self.timer.Stop()
486
487
488 if hasattr(self, 'mc_gauge_rx'):
489 wx.CallAfter(self.mc_gauge_rx.SetValue, 100)
490
491
492 if hasattr(self, 'mc_gauge_mf'):
493 wx.CallAfter(self.mc_gauge_mf.SetValue, 100)
494 if hasattr(self, 'progress_gauge_mf'):
495 wx.CallAfter(self.progress_gauge_mf.SetValue, 100)
496
497
498 wx.CallAfter(self.main_gauge.SetValue, 100)
499
500
501 if not self.main_gauge.GetValue():
502 return
503
504
505 if not key or not key in status.auto_analysis:
506 wx.CallAfter(self.main_gauge.SetValue, 0)
507
508
509 if key and key in status.auto_analysis and not status.auto_analysis[key].fin:
510
511 if hasattr(self, 'mc_gauge_rx'):
512 wx.CallAfter(self.mc_gauge_rx.SetValue, 0)
513
514
515 if hasattr(self, 'mc_gauge_mf'):
516 wx.CallAfter(self.mc_gauge_mf.SetValue, 0)
517 if hasattr(self, 'progress_gauge_mf'):
518 wx.CallAfter(self.progress_gauge_mf.SetValue, 0)
519
520
521 wx.CallAfter(self.main_gauge.SetValue, 0)
522
523
525 """Update the model-free specific elements."""
526
527
528 key = self.analysis_key()
529 if not key:
530 return
531
532
533 elif not key in status.auto_analysis and cdp_name() == 'final':
534 wx.CallAfter(self.mc_gauge_mf.SetValue, 100)
535 wx.CallAfter(self.progress_gauge_mf.SetValue, 100)
536 wx.CallAfter(self.main_gauge.SetValue, 100)
537 return
538
539
540 if not key in status.auto_analysis:
541 wx.CallAfter(self.mc_gauge_mf.SetValue, 0)
542 wx.CallAfter(self.progress_gauge_mf.SetValue, 0)
543 wx.CallAfter(self.main_gauge.SetValue, 0)
544 return
545
546
547 wx.CallAfter(self.global_model_mf.SetValue, str_to_gui(status.auto_analysis[key].diff_model))
548
549
550 if status.auto_analysis[key].diff_model == 'local_tm':
551 if status.auto_analysis[key].current_model:
552
553 no = int(status.auto_analysis[key].current_model[2:])
554
555
556 total_models = len(status.auto_analysis[key].local_tm_models)
557
558
559 percent = int(100 * no / float(total_models))
560 wx.CallAfter(self.progress_gauge_mf.SetValue, percent)
561
562
563 elif status.auto_analysis[key].diff_model in ['sphere', 'prolate', 'oblate', 'ellipsoid']:
564
565 if status.auto_analysis[key].round == None:
566 wx.CallAfter(self.progress_gauge_mf.SetValue, 0)
567 else:
568
569 percent = int(100 * (status.auto_analysis[key].round + 1) / (status.auto_analysis[key].max_iter + 1))
570
571
572 wx.CallAfter(self.progress_gauge_mf.SetValue, percent)
573
574
575 if status.auto_analysis[key].mc_number:
576
577 percent = int(100 * (status.auto_analysis[key].mc_number + 1) / cdp.sim_number)
578
579
580 wx.CallAfter(self.mc_gauge_mf.SetValue, percent)
581
582
584 """Update the Rx specific elements."""
585
586
587 key = self.analysis_key()
588 if not key:
589 return
590
591
592 if not key in status.auto_analysis:
593 wx.CallAfter(self.mc_gauge_rx.SetValue, 0)
594 wx.CallAfter(self.main_gauge.SetValue, 0)
595 return
596
597
598 if status.auto_analysis[key].mc_number:
599
600 percent = int(100 * (status.auto_analysis[key].mc_number + 1) / cdp.sim_number)
601
602
603 wx.CallAfter(self.mc_gauge_rx.SetValue, percent)
604
605
606
607 -class LogCtrl(wx.stc.StyledTextCtrl):
608 """A special control designed to display relax output messages."""
609
610 - def __init__(self, parent, controller, log_queue=None, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.BORDER_SUNKEN, name=wx.stc.STCNameStr):
611 """Set up the log control.
612
613 @param parent: The parent wx window object.
614 @type parent: Window
615 @param controller: The controller window.
616 @type controller: wx.Frame instance
617 @keyword log_queue: The queue of log messages.
618 @type log_queue: Queue.Queue instance
619 @keyword id: The wx ID.
620 @type id: int
621 @keyword pos: The window position.
622 @type pos: Point
623 @keyword size: The window size.
624 @type size: Size
625 @keyword style: The StyledTextCtrl to apply.
626 @type style: long
627 @keyword name: The window name.
628 @type name: str
629 """
630
631
632 self.controller = controller
633 self.log_queue = log_queue
634
635
636 super(LogCtrl, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)
637
638
639 self.at_end = True
640
641
642 self.SetWrapMode(wx.stc.STC_WRAP_WORD)
643
644
645 self.StyleSetFont(0, font.modern_small)
646
647
648 if dep_check.wx_classic:
649 colour = wx.NamedColour('red')
650 else:
651 colour = wx.Colour('red')
652 self.StyleSetForeground(1, colour)
653 self.StyleSetFont(1, font.modern_small)
654
655
656 if dep_check.wx_classic:
657 colour = wx.NamedColour('blue')
658 else:
659 colour = wx.Colour('blue')
660 self.StyleSetForeground(2, colour)
661 self.StyleSetFont(2, font.modern_small_bold)
662
663
664 if dep_check.wx_classic:
665 colour = wx.NamedColour('orange red')
666 else:
667 colour = wx.Colour('orange red')
668 self.StyleSetForeground(3, colour)
669 self.StyleSetFont(3, font.modern_small)
670
671
672 if dep_check.wx_classic:
673 colour = wx.NamedColour('dark green')
674 else:
675 colour = wx.Colour('dark green')
676 self.StyleSetForeground(4, colour)
677 self.StyleSetFont(4, font.modern_small)
678
679
680 self.find_dlg = None
681
682
683 self.find_data = wx.FindReplaceData()
684 self.find_data.SetFlags(wx.FR_DOWN)
685
686
687 self.UsePopUp(0)
688
689
690 self.SetReadOnly(True)
691
692
693 self.orig_zoom = self.GetZoom()
694
695
696 self.Bind(wx.EVT_KEY_DOWN, self.capture_keys)
697 self.Bind(wx.EVT_MOUSE_EVENTS, self.capture_mouse)
698 self.Bind(wx.EVT_MOUSEWHEEL, self.capture_mouse_wheel)
699 self.Bind(wx.EVT_RIGHT_DOWN, self.pop_up_menu)
700 self.Bind(wx.EVT_SCROLLWIN_THUMBTRACK, self.capture_scroll)
701 self.Bind(wx.EVT_MENU, self.find_open, id=MENU_ID_FIND)
702 self.Bind(wx.EVT_MENU, self.on_copy, id=MENU_ID_COPY)
703 self.Bind(wx.EVT_MENU, self.on_select_all, id=MENU_ID_SELECT_ALL)
704 self.Bind(wx.EVT_MENU, self.on_zoom_in, id=MENU_ID_ZOOM_IN)
705 self.Bind(wx.EVT_MENU, self.on_zoom_out, id=MENU_ID_ZOOM_OUT)
706 self.Bind(wx.EVT_MENU, self.on_zoom_orig, id=MENU_ID_ZOOM_ORIG)
707 self.Bind(wx.EVT_MENU, self.on_goto_start, id=MENU_ID_GOTO_START)
708 self.Bind(wx.EVT_MENU, self.on_goto_end, id=MENU_ID_GOTO_END)
709
710
712 """Control which key events are active, preventing text insertion and deletion.
713
714 @param event: The wx event.
715 @type event: wx event
716 """
717
718
719 if event.ControlDown() and event.GetKeyCode() == 67:
720 event.Skip()
721
722
723 if event.ControlDown() and event.GetKeyCode() == 70:
724 self.find_open(event)
725
726
727 if event.ControlDown() and event.GetKeyCode() == 65:
728 self.on_select_all(event)
729
730
731 if 'darwin' in sys.platform and event.ControlDown() and event.GetKeyCode() == 71:
732 self.find_next(event)
733 elif 'darwin' not in sys.platform and event.GetKeyCode() == wx.WXK_F3:
734 self.find_next(event)
735
736
737 if event.GetKeyCode() in [wx.WXK_END, wx.WXK_HOME, wx.WXK_LEFT, wx.WXK_UP, wx.WXK_RIGHT, wx.WXK_DOWN]:
738 self.at_end = False
739 event.Skip()
740
741
742 if event.GetKeyCode() in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]:
743 self.at_end = False
744 event.Skip()
745
746
747 if event.ControlDown() and event.GetKeyCode() == 48:
748 self.on_zoom_orig(event)
749 if event.ControlDown() and event.GetKeyCode() == 45:
750 self.on_zoom_out(event)
751 if event.ControlDown() and event.GetKeyCode() == 61:
752 self.on_zoom_in(event)
753
754
755 if event.ControlDown() and event.GetKeyCode() == wx.WXK_HOME:
756 self.on_goto_start(event)
757 elif event.ControlDown() and event.GetKeyCode() == wx.WXK_END:
758 self.on_goto_end(event)
759
760
761 if event.GetKeyCode() == wx.WXK_ESCAPE:
762 self.controller.handler_close(event)
763
764
766 """Control the mouse events.
767
768 @param event: The wx event.
769 @type event: wx event
770 """
771
772
773 if event.ButtonDown():
774 self.at_end = False
775
776
777 event.Skip()
778
779
781 """Control the mouse wheel events.
782
783 @param event: The wx event.
784 @type event: wx event
785 """
786
787
788 self.at_end = False
789
790
791 scroll = event.GetLinesPerAction()
792 if event.GetWheelRotation() > 0.0:
793 scroll *= -1
794 self.GotoLine(self.GetCurrentLine() + scroll)
795
796
797 event.Skip()
798
799
815
816
818 """Remove all text from the log."""
819
820
821 self.SetReadOnly(False)
822
823
824 self.ClearAll()
825
826
827 self.SetReadOnly(True)
828
829
830 - def find(self, event):
831 """Find the text in the log control.
832
833 @param event: The wx event.
834 @type event: wx event
835 """
836
837
838 sel = self.find_data.GetFindString()
839
840
841 flags = self.find_data.GetFlags()
842
843
844 pos = self.GetCurrentPos()
845 if pos != self.GetLength():
846 self.SetCurrentPos(pos+1)
847 self.SearchAnchor()
848
849
850 forwards = wx.FR_DOWN & flags
851
852
853 if forwards:
854 pos = self.SearchNext(flags, sel)
855
856
857 else:
858 pos = self.SearchPrev(flags, sel)
859
860
861 if pos == -1:
862
863 if forwards:
864 self.GotoPos(self.GetLength())
865 else:
866 self.GotoPos(pos)
867
868
869 text = "The string '%s' could not be found." % sel
870 nothing = wx.MessageDialog(self, text, caption="Not found", style=wx.ICON_INFORMATION|wx.OK)
871 nothing.SetSize((300, 200))
872 if status.show_gui:
873 nothing.ShowModal()
874 nothing.Destroy()
875
876
877 else:
878
879 self.EnsureCaretVisible()
880
881
882 self.at_end = False
883
884
886 """Close the find dialog.
887
888 @param event: The wx event.
889 @type event: wx event
890 """
891
892
893 self.find_dlg.Destroy()
894
895
896 self.find_dlg = None
897
898
900 """Display the text finding dialog.
901
902 @param event: The wx event.
903 @type event: wx event
904 """
905
906
907 self.at_end = False
908
909
910 if self.find_dlg == None:
911
912 self.find_dlg = wx.FindReplaceDialog(self, self.find_data, "Find")
913
914
915 self.find_dlg.Bind(wx.EVT_FIND, self.find)
916 self.find_dlg.Bind(wx.EVT_FIND_NEXT, self.find)
917 self.find_dlg.Bind(wx.EVT_FIND_CLOSE, self.find_close)
918
919
920 if status.show_gui:
921 self.find_dlg.Show(True)
922
923
924 else:
925 self.find_dlg.Show()
926
927
929 """Find the next instance of the text.
930
931 @param event: The wx event.
932 @type event: wx event
933 """
934
935
936 self.at_end = False
937
938
939 if self.find_data.GetFindString():
940 self.find(event)
941
942
943 else:
944 self.find_open(event)
945
946
947 - def get_text(self):
948 """Concatenate all of the text from the log queue and return it as a string.
949
950 @return: A list of the text from the log queue and a list of the streams these correspond to.
951 @rtype: list of str, list of int
952 """
953
954
955 string_list = ['']
956 stream_list = [0]
957
958
959 while True:
960
961 if self.log_queue.empty():
962 break
963
964
965 msg, stream = self.log_queue.get()
966
967
968 if msg[1:7] == 'relax>':
969
970 string_list[-1] += '\n'
971
972
973 string_list.append('relax>')
974 stream_list.append(2)
975
976
977 msg = msg[7:]
978
979
980 string_list.append('')
981 stream_list.append(stream)
982
983
984 elif msg[0:13] == 'RelaxWarning:':
985
986 string_list.append(msg)
987 stream_list.append(3)
988 continue
989
990
991 elif msg[0:6] == 'debug>':
992
993 string_list.append(msg)
994 stream_list.append(4)
995 continue
996
997
998 if stream_list[-1] != stream:
999 string_list.append('')
1000 stream_list.append(stream)
1001
1002
1003 string_list[-1] = string_list[-1] + msg
1004
1005
1006 return string_list, stream_list
1007
1008
1039
1040
1042 """Copy the selected text.
1043
1044 @param event: The wx event.
1045 @type event: wx event
1046 """
1047
1048
1049 self.Copy()
1050
1051
1053 """Move to the end of the text.
1054
1055 @param event: The wx event.
1056 @type event: wx event
1057 """
1058
1059
1060 self.at_end = True
1061
1062
1063 self.GotoPos(self.GetLength())
1064
1065
1067 """Move to the start of the text.
1068
1069 @param event: The wx event.
1070 @type event: wx event
1071 """
1072
1073
1074 self.at_end = False
1075
1076
1077 self.GotoPos(-1)
1078
1079
1081 """Select all text in the control.
1082
1083 @param event: The wx event.
1084 @type event: wx event
1085 """
1086
1087
1088 self.at_end = False
1089
1090
1091 self.GotoPos(1)
1092
1093
1094 self.SelectAll()
1095
1096
1098 """Zoom in by increase the font by 1 point size.
1099
1100 @param event: The wx event.
1101 @type event: wx event
1102 """
1103
1104
1105 self.ZoomIn()
1106
1107
1109 """Zoom to the original zoom level.
1110
1111 @param event: The wx event.
1112 @type event: wx event
1113 """
1114
1115
1116 self.SetZoom(self.orig_zoom)
1117
1118
1120 """Zoom out by decreasing the font by 1 point size.
1121
1122 @param event: The wx event.
1123 @type event: wx event
1124 """
1125
1126
1127 self.ZoomOut()
1128
1129
1131 """Override the StyledTextCtrl pop up menu.
1132
1133 @param event: The wx event.
1134 @type event: wx event
1135 """
1136
1137
1138 menu = wx.Menu()
1139
1140
1141 build_menu_item(menu, id=MENU_ID_FIND, text="&Find", icon=fetch_icon('oxygen.actions.edit-find', "16x16"))
1142 menu.AppendSeparator()
1143 build_menu_item(menu, id=MENU_ID_COPY, text="&Copy", icon=fetch_icon('oxygen.actions.edit-copy', "16x16"))
1144 build_menu_item(menu, id=MENU_ID_SELECT_ALL, text="&Select all", icon=fetch_icon('oxygen.actions.edit-select-all', "16x16"))
1145 menu.AppendSeparator()
1146 build_menu_item(menu, id=MENU_ID_ZOOM_IN, text="Zoom &in", icon=fetch_icon('oxygen.actions.zoom-in', "16x16"))
1147 build_menu_item(menu, id=MENU_ID_ZOOM_OUT, text="Zoom &out", icon=fetch_icon('oxygen.actions.zoom-out', "16x16"))
1148 build_menu_item(menu, id=MENU_ID_ZOOM_ORIG, text="Original &zoom", icon=fetch_icon('oxygen.actions.zoom-original', "16x16"))
1149 menu.AppendSeparator()
1150 build_menu_item(menu, id=MENU_ID_GOTO_START, text="&Go to start", icon=fetch_icon('oxygen.actions.go-top', "16x16"))
1151 build_menu_item(menu, id=MENU_ID_GOTO_END, text="&Go to end", icon=fetch_icon('oxygen.actions.go-bottom', "16x16"))
1152
1153
1154 if status.show_gui:
1155 self.PopupMenu(menu)
1156
1157
1158 menu.Destroy()
1159
1160
1162 """Write the text in the log queue to the log control."""
1163
1164
1165 if not self.at_end and self.GetScrollRange(wx.VERTICAL) - self.GetCurrentLine() <= 1:
1166 self.at_end = True
1167
1168
1169 string_list, stream_list = self.get_text()
1170
1171
1172 if len(string_list) == 1 and string_list[0] == '':
1173 return
1174
1175
1176 self.SetReadOnly(False)
1177
1178
1179 for i in range(len(string_list)):
1180
1181 self.AppendText(string_list[i])
1182
1183
1184 if stream_list[i] != 0:
1185
1186 len_string = len(string_list[i].encode('utf8'))
1187 end = self.GetLength()
1188
1189
1190 self.StartStyling(end - len_string)
1191 self.SetStyling(len_string, stream_list[i])
1192
1193
1194 if stream_list[i] in [1, 3] and status.show_gui:
1195
1196 if self.controller.IsShown():
1197 self.controller.Raise()
1198
1199
1200 else:
1201
1202 self.controller.Show()
1203 self.GotoPos(self.GetLength())
1204
1205
1206 self.limit_scrollback()
1207
1208
1209 if self.at_end:
1210 self.DocumentEnd()
1211
1212
1213 self.SetReadOnly(True)
1214
1215
1216
1217 -class Redirect_text(object):
1218 """The IO redirection to text control object."""
1219
1220 - def __init__(self, control, log_queue, orig_io, stream=0):
1221 """Set up the text redirection object.
1222
1223 @param control: The text control object to redirect IO to.
1224 @type control: wx.TextCtrl instance
1225 @param log_queue: The queue of log messages.
1226 @type log_queue: Queue.Queue instance
1227 @param orig_io: The original IO stream, used for debugging and the test suite.
1228 @type orig_io: file
1229 @keyword stream: The type of steam (0 for STDOUT and 1 for STDERR).
1230 @type stream: int
1231 """
1232
1233
1234 self.control = control
1235 self.log_queue = log_queue
1236 self.orig_io = orig_io
1237 self.stream = stream
1238
1239
1241 """Simulate the file object flush method."""
1242
1243
1244 wx.CallAfter(self.control.write)
1245
1246
1248 """Answer that this is not a TTY.
1249
1250 @return: False, as this is not a TTY.
1251 @rtype: bool
1252 """
1253
1254 return False
1255
1256
1257 - def write(self, string):
1258 """Simulate the file object write method.
1259
1260 @param string: The text to write.
1261 @type string: str
1262 """
1263
1264
1265 if status.debug or status.test_mode:
1266 self.orig_io.write(string)
1267
1268
1269 self.log_queue.put([string, self.stream])
1270