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 from graphics import IMAGE_PATH, fetch_icon
33 from gui.components.menu import build_menu_item
34 from gui.fonts import font
35 from gui.icons import relax_icons
36 from gui.misc import add_border, bitmap_setup
37 from gui.string_conv import str_to_gui
38 from info import Info_box
39 from lib.compat import Queue
40 from lib.io import SplitIO
41 from pipe_control.pipes import cdp_name
42 from status import Status; status = Status()
43
44
46 """The relax controller window."""
47
49 """Set up the relax controller frame.
50
51 @param gui: The GUI object.
52 @type gui: wx.Frame instance
53 """
54
55
56 self.gui = gui
57
58
59 super(Controller, self).__init__(self.gui, -1, style=wx.DEFAULT_FRAME_STYLE)
60
61
62 self.size_x = 800
63 self.size_y = 700
64 self.border = 5
65 self.spacer = 10
66
67
68 sizer = self.setup_frame()
69
70
71 self.add_relax_logo(sizer)
72
73
74 sizer.AddSpacer(20)
75
76
77 self.name = self.add_text(self.main_panel, sizer, "Current GUI analysis:")
78
79
80 self.cdp = self.add_text(self.main_panel, sizer, "Current data pipe:")
81
82
83 self.create_rx(sizer)
84
85
86 self.create_mf(sizer)
87
88
89 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.")
90
91
92 self.log_queue = Queue()
93
94
95 self.log_panel = LogCtrl(self.main_panel, self, log_queue=self.log_queue, id=-1)
96 sizer.Add(self.log_panel, 1, wx.EXPAND|wx.ALL, 0)
97
98
99 out = Redirect_text(self.log_panel, self.log_queue, orig_io=sys.stdout, stream=0)
100 if sys.stdout == sys.__stdout__ or status.relax_mode in ['test suite', 'system tests', 'unit tests', 'GUI tests']:
101 sys.stdout = out
102 else:
103 split_stdout = SplitIO()
104 split_stdout.split(sys.stdout, out)
105 sys.stdout = split_stdout
106
107
108 err = Redirect_text(self.log_panel, self.log_queue, orig_io=sys.stderr, stream=1)
109 if sys.stderr == sys.__stderr__ or status.relax_mode in ['test suite', 'system tests', 'unit tests', 'GUI tests']:
110 sys.stderr = err
111 else:
112 split_stderr = SplitIO()
113 split_stderr.split(sys.stderr, err)
114 sys.stderr = split_stderr
115
116
117 self.update_controller()
118
119
120 self.timer = wx.Timer(self)
121 self.Bind(wx.EVT_TIMER, self.handler_timer, self.timer)
122
123
124 if not status.test_mode:
125 info = Info_box()
126 print(info.intro_text())
127
128
129 status.observers.pipe_alteration.register('controller', self.update_controller, method_name='update_controller')
130 status.observers.auto_analyses.register('controller', self.update_controller, method_name='update_controller')
131 status.observers.gui_analysis.register('controller', self.update_controller, method_name='update_controller')
132 status.observers.exec_lock.register('controller', self.update_gauge, method_name='update_gauge')
133
134
135 - def add_gauge(self, parent, sizer, desc, tooltip=None):
136 """Add a gauge to the sizer and return it.
137
138 @param parent: The parent GUI element.
139 @type parent: wx object
140 @param sizer: The sizer element to pack the element into.
141 @type sizer: wx.Sizer instance
142 @param desc: The description to display.
143 @type desc: str
144 @keyword tooltip: The tooltip which appears on hovering over the text and the gauge.
145 @type tooltip: str
146 @return: The gauge element.
147 @rtype: wx.Gauge instance
148 """
149
150
151 sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
152
153
154 text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT)
155 text.SetFont(font.normal)
156 sub_sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL, 0)
157
158
159 gauge = wx.Gauge(parent, id=-1, range=100, style=wx.GA_SMOOTH)
160 gauge.SetSize((-1, 20))
161 sub_sizer.Add(gauge, 3, wx.EXPAND|wx.ALL, 0)
162
163
164 sizer.Add(sub_sizer, 0, wx.ALL|wx.EXPAND, 0)
165
166
167 sizer.AddSpacer(self.spacer)
168
169
170 if tooltip:
171 text.SetToolTipString(tooltip)
172 gauge.SetToolTipString(tooltip)
173
174
175 return gauge
176
177
179 """Add the relax logo to the sizer.
180
181 @param sizer: The sizer element to pack the relax logo into.
182 @type sizer: wx.Sizer instance
183 """
184
185
186 logo = wx.StaticBitmap(self.main_panel, -1, bitmap_setup(IMAGE_PATH+'relax.gif'))
187
188
189 sizer.Add(logo, 0, wx.TOP|wx.ALIGN_CENTER_HORIZONTAL, 0)
190
191
192 sizer.AddSpacer(self.spacer)
193
194
195 - def add_text(self, parent, sizer, desc, tooltip=None):
196 """Add the current data pipe element.
197
198 @param parent: The parent GUI element.
199 @type parent: wx object
200 @param sizer: The sizer element to pack the element into.
201 @type sizer: wx.Sizer instance
202 @param desc: The description to display.
203 @type desc: str
204 @keyword tooltip: The tooltip which appears on hovering over the text and field.
205 @type tooltip: str
206 @return: The text control.
207 @rtype: wx.TextCtrl instance
208 """
209
210
211 sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
212
213
214 text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT)
215 text.SetFont(font.normal)
216 sub_sizer.Add(text, 1, wx.ALIGN_CENTER_VERTICAL, 0)
217
218
219 field = wx.TextCtrl(parent, -1, '', style=wx.ALIGN_LEFT)
220 field.SetEditable(False)
221 field.SetFont(font.normal)
222 colour = self.main_panel.GetBackgroundColour()
223 field.SetOwnBackgroundColour(colour)
224 sub_sizer.Add(field, 3, wx.ALIGN_CENTER_VERTICAL, 0)
225
226
227 sizer.Add(sub_sizer, 0, wx.ALL|wx.EXPAND, 0)
228
229
230 sizer.AddSpacer(self.spacer)
231
232
233 if tooltip:
234 text.SetToolTipString(tooltip)
235 field.SetToolTipString(tooltip)
236
237
238 return field
239
240
242 """Return the key for the current analysis' status object.
243
244 @return: The current analysis' status object key.
245 @rtype: str or None
246 """
247
248
249 data = self.gui.analysis.current_data()
250 if data == None:
251 return
252
253
254 if hasattr(data, 'pipe_bundle'):
255 return data.pipe_bundle
256
257
259 """Create the model-free specific panel.
260
261 @param sizer: The sizer element to pack the element into.
262 @type sizer: wx.Sizer instance
263 """
264
265
266 self.panel_mf = wx.Panel(self.main_panel, -1)
267 sizer.Add(self.panel_mf, 0, wx.ALL|wx.EXPAND, 0)
268
269
270 panel_sizer = wx.BoxSizer(wx.VERTICAL)
271 self.panel_mf.SetSizer(panel_sizer)
272
273
274 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'.")
275
276
277 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%")
278
279
280 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.")
281
282
284 """Create the relaxation curve-fitting specific panel.
285
286 @param sizer: The sizer element to pack the element into.
287 @type sizer: wx.Sizer instance
288 """
289
290
291 self.panel_rx = wx.Panel(self.main_panel, -1)
292 sizer.Add(self.panel_rx, 0, wx.ALL|wx.EXPAND, 0)
293
294
295 panel_sizer = wx.BoxSizer(wx.VERTICAL)
296 self.panel_rx.SetSizer(panel_sizer)
297
298
299 self.mc_gauge_rx = self.add_gauge(self.panel_rx, panel_sizer, "Monte Carlo simulations:", tooltip="The Monte Carlo simulation number.")
300
301
303 """Event handler for the close window action.
304
305 @param event: The wx event.
306 @type event: wx event
307 """
308
309
310 if self.gui.test_suite_flag:
311 return
312
313
314 self.Hide()
315
316
318 """Event handler for the timer.
319
320 @param event: The wx event.
321 @type event: wx event
322 """
323
324
325 wx.CallAfter(self.main_gauge.Pulse)
326
327
328 if not status.exec_lock.locked() and self.timer.IsRunning():
329 self.timer.Stop()
330 self.update_gauge()
331
332
334 """Reset the relax controller to its initial state."""
335
336
337 if self.timer.IsRunning():
338 self.timer.Stop()
339
340
341 if hasattr(self, 'mc_gauge_rx'):
342 wx.CallAfter(self.mc_gauge_rx.SetValue, 0)
343
344
345 if hasattr(self, 'mc_gauge_mf'):
346 wx.CallAfter(self.mc_gauge_mf.SetValue, 0)
347 if hasattr(self, 'progress_gauge_mf'):
348 wx.CallAfter(self.progress_gauge_mf.SetValue, 0)
349
350
351 wx.CallAfter(self.main_gauge.SetValue, 0)
352
353
355 """Set up the relax controller frame.
356 @return: The sizer object.
357 @rtype: wx.Sizer instance
358 """
359
360
361 self.SetTitle("The relax controller")
362
363
364 self.SetIcons(relax_icons)
365
366
367 self.main_panel = wx.Panel(self, -1)
368
369
370 main_sizer = wx.BoxSizer(wx.VERTICAL)
371 self.main_panel.SetSizer(main_sizer)
372
373
374 sizer = add_border(main_sizer, border=self.border, packing=wx.VERTICAL)
375
376
377 self.Bind(wx.EVT_CLOSE, self.handler_close)
378
379
380 self.SetSize((self.size_x, self.size_y))
381
382
383 self.Centre()
384
385
386 return sizer
387
388
430
431
433 """Update the main execution gauge."""
434
435
436 if status.exec_lock.locked():
437
438 if not self.timer.IsRunning():
439 wx.CallAfter(self.timer.Start, 100)
440
441
442 return
443
444
445 key = self.analysis_key()
446 if key and key in status.auto_analysis and status.auto_analysis[key].fin:
447
448 if self.timer.IsRunning():
449 self.timer.Stop()
450
451
452 if hasattr(self, 'mc_gauge_rx'):
453 wx.CallAfter(self.mc_gauge_rx.SetValue, 100)
454
455
456 if hasattr(self, 'mc_gauge_mf'):
457 wx.CallAfter(self.mc_gauge_mf.SetValue, 100)
458 if hasattr(self, 'progress_gauge_mf'):
459 wx.CallAfter(self.progress_gauge_mf.SetValue, 100)
460
461
462 wx.CallAfter(self.main_gauge.SetValue, 100)
463
464
465 if not self.main_gauge.GetValue():
466 return
467
468
469 if not key or not key in status.auto_analysis:
470 wx.CallAfter(self.main_gauge.SetValue, 0)
471
472
473 if key and key in status.auto_analysis and not status.auto_analysis[key].fin:
474
475 if hasattr(self, 'mc_gauge_rx'):
476 wx.CallAfter(self.mc_gauge_rx.SetValue, 0)
477
478
479 if hasattr(self, 'mc_gauge_mf'):
480 wx.CallAfter(self.mc_gauge_mf.SetValue, 0)
481 if hasattr(self, 'progress_gauge_mf'):
482 wx.CallAfter(self.progress_gauge_mf.SetValue, 0)
483
484
485 wx.CallAfter(self.main_gauge.SetValue, 0)
486
487
489 """Update the model-free specific elements."""
490
491
492 key = self.analysis_key()
493 if not key:
494 return
495
496
497 elif not key in status.auto_analysis and cdp_name() == 'final':
498 wx.CallAfter(self.mc_gauge_mf.SetValue, 100)
499 wx.CallAfter(self.progress_gauge_mf.SetValue, 100)
500 wx.CallAfter(self.main_gauge.SetValue, 100)
501 return
502
503
504 if not key in status.auto_analysis:
505 wx.CallAfter(self.mc_gauge_mf.SetValue, 0)
506 wx.CallAfter(self.progress_gauge_mf.SetValue, 0)
507 wx.CallAfter(self.main_gauge.SetValue, 0)
508 return
509
510
511 wx.CallAfter(self.global_model_mf.SetValue, str_to_gui(status.auto_analysis[key].diff_model))
512
513
514 if status.auto_analysis[key].diff_model == 'local_tm':
515 if status.auto_analysis[key].current_model:
516
517 no = int(status.auto_analysis[key].current_model[2:])
518
519
520 total_models = len(status.auto_analysis[key].local_tm_models)
521
522
523 percent = int(100 * no / float(total_models))
524 wx.CallAfter(self.progress_gauge_mf.SetValue, percent)
525
526
527 elif status.auto_analysis[key].diff_model in ['sphere', 'prolate', 'oblate', 'ellipsoid']:
528
529 if status.auto_analysis[key].round == None:
530 wx.CallAfter(self.progress_gauge_mf.SetValue, 0)
531 else:
532
533 percent = int(100 * (status.auto_analysis[key].round + 1) / (status.auto_analysis[key].max_iter + 1))
534
535
536 wx.CallAfter(self.progress_gauge_mf.SetValue, percent)
537
538
539 if status.auto_analysis[key].mc_number:
540
541 percent = int(100 * (status.auto_analysis[key].mc_number + 1) / cdp.sim_number)
542
543
544 wx.CallAfter(self.mc_gauge_mf.SetValue, percent)
545
546
548 """Update the Rx specific elements."""
549
550
551 key = self.analysis_key()
552 if not key:
553 return
554
555
556 if not key in status.auto_analysis:
557 wx.CallAfter(self.mc_gauge_rx.SetValue, 0)
558 wx.CallAfter(self.main_gauge.SetValue, 0)
559 return
560
561
562 if status.auto_analysis[key].mc_number:
563
564 percent = int(100 * (status.auto_analysis[key].mc_number + 1) / cdp.sim_number)
565
566
567 wx.CallAfter(self.mc_gauge_rx.SetValue, percent)
568
569
570
571 -class LogCtrl(wx.stc.StyledTextCtrl):
572 """A special control designed to display relax output messages."""
573
574 - 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):
575 """Set up the log control.
576
577 @param parent: The parent wx window object.
578 @type parent: Window
579 @param controller: The controller window.
580 @type controller: wx.Frame instance
581 @keyword log_queue: The queue of log messages.
582 @type log_queue: Queue.Queue instance
583 @keyword id: The wx ID.
584 @type id: int
585 @keyword pos: The window position.
586 @type pos: Point
587 @keyword size: The window size.
588 @type size: Size
589 @keyword style: The StyledTextCtrl to apply.
590 @type style: long
591 @keyword name: The window name.
592 @type name: str
593 """
594
595
596 self.controller = controller
597 self.log_queue = log_queue
598
599
600 super(LogCtrl, self).__init__(parent, id=id, pos=pos, size=size, style=style, name=name)
601
602
603 self.SetWrapMode(wx.stc.STC_WRAP_WORD)
604
605
606 self.StyleSetFont(0, font.modern_small)
607
608
609 self.StyleSetForeground(1, wx.NamedColour('red'))
610 self.StyleSetFont(1, font.modern_small)
611
612
613 self.StyleSetForeground(2, wx.NamedColour('blue'))
614 self.StyleSetFont(2, font.modern_small_bold)
615
616
617 self.StyleSetForeground(3, wx.NamedColour('orange red'))
618 self.StyleSetFont(3, font.modern_small)
619
620
621 self.StyleSetForeground(4, wx.NamedColour('dark green'))
622 self.StyleSetFont(4, font.modern_small)
623
624
625 self.find_dlg = None
626
627
628 self.find_data = wx.FindReplaceData()
629 self.find_data.SetFlags(wx.FR_DOWN)
630
631
632 self.UsePopUp(0)
633
634
635 self.menu_id_find = wx.NewId()
636 self.menu_id_copy = wx.NewId()
637 self.menu_id_select_all = wx.NewId()
638 self.menu_id_zoom_in = wx.NewId()
639 self.menu_id_zoom_out = wx.NewId()
640 self.menu_id_zoom_orig = wx.NewId()
641 self.menu_id_goto_start = wx.NewId()
642 self.menu_id_goto_end = wx.NewId()
643
644
645 self.SetReadOnly(True)
646
647
648 self.orig_zoom = self.GetZoom()
649
650
651 self.Bind(wx.EVT_FIND, self.find)
652 self.Bind(wx.EVT_FIND_NEXT, self.find)
653 self.Bind(wx.EVT_FIND_CLOSE, self.find_close)
654 self.Bind(wx.EVT_KEY_DOWN, self.capture_keys)
655 self.Bind(wx.EVT_RIGHT_DOWN, self.pop_up_menu)
656 self.Bind(wx.EVT_MENU, self.find_open, id=self.menu_id_find)
657 self.Bind(wx.EVT_MENU, self.on_copy, id=self.menu_id_copy)
658 self.Bind(wx.EVT_MENU, self.on_select_all, id=self.menu_id_select_all)
659 self.Bind(wx.EVT_MENU, self.on_zoom_in, id=self.menu_id_zoom_in)
660 self.Bind(wx.EVT_MENU, self.on_zoom_out, id=self.menu_id_zoom_out)
661 self.Bind(wx.EVT_MENU, self.on_zoom_orig, id=self.menu_id_zoom_orig)
662 self.Bind(wx.EVT_MENU, self.on_goto_start, id=self.menu_id_goto_start)
663 self.Bind(wx.EVT_MENU, self.on_goto_end, id=self.menu_id_goto_end)
664
665
667 """Control which key events are active, preventing text insertion and deletion.
668
669 @param event: The wx event.
670 @type event: wx event
671 """
672
673
674 if event.ControlDown() and event.GetKeyCode() == 67:
675 event.Skip()
676
677
678 if event.ControlDown() and event.GetKeyCode() == 70:
679 self.find_open(event)
680
681
682 if event.ControlDown() and event.GetKeyCode() == 65:
683 event.Skip()
684
685
686 if 'darwin' in sys.platform and event.ControlDown() and event.GetKeyCode() == 71:
687 self.find_next(event)
688 elif 'darwin' not in sys.platform and event.GetKeyCode() == 342:
689 self.find_next(event)
690
691
692 if event.GetKeyCode() in [312, 313, 314, 315, 316, 317]:
693 event.Skip()
694
695
696 if event.GetKeyCode() in [366, 367]:
697 event.Skip()
698
699
700 if event.ControlDown() and event.GetKeyCode() == 48:
701 self.on_zoom_orig(event)
702 if event.ControlDown() and event.GetKeyCode() == 45:
703 self.on_zoom_out(event)
704 if event.ControlDown() and event.GetKeyCode() == 61:
705 self.on_zoom_in(event)
706
707
708 if event.ControlDown() and event.GetKeyCode() == 316:
709 self.on_goto_start(event)
710 elif event.ControlDown() and event.GetKeyCode() == 317:
711 self.on_goto_end(event)
712
713
715 """Remove all text from the log."""
716
717
718 self.SetReadOnly(False)
719
720
721 self.ClearAll()
722
723
724 self.SetReadOnly(True)
725
726
727 - def find(self, event):
728 """Find the text in the log control.
729
730 @param event: The wx event.
731 @type event: wx event
732 """
733
734
735 sel = self.find_data.GetFindString()
736
737
738 flags = event.GetFlags()
739
740
741 pos = self.GetCurrentPos()
742 if pos != self.GetLength():
743 self.SetCurrentPos(pos+1)
744 self.SearchAnchor()
745
746
747 forwards = wx.FR_DOWN & flags
748
749
750 if forwards:
751 pos = self.SearchNext(flags, sel)
752
753
754 else:
755 pos = self.SearchPrev(flags, sel)
756
757
758 if pos == -1:
759
760 if forwards:
761 self.GotoPos(self.GetLength())
762 else:
763 self.GotoPos(pos)
764
765
766 text = "The string '%s' could not be found." % sel
767 nothing = wx.MessageDialog(self, text, caption="Not found", style=wx.ICON_INFORMATION|wx.OK)
768 nothing.SetSize((300, 200))
769 if status.show_gui:
770 nothing.ShowModal()
771 nothing.Destroy()
772
773
774 else:
775
776 self.EnsureCaretVisible()
777
778
780 """Close the find dialog.
781
782 @param event: The wx event.
783 @type event: wx event
784 """
785
786
787 self.find_dlg.Destroy()
788
789
790 self.find_dlg = None
791
792
794 """Display the text finding dialog.
795
796 @param event: The wx event.
797 @type event: wx event
798 """
799
800
801 if self.find_dlg == None:
802 self.find_dlg = wx.FindReplaceDialog(self, self.find_data, "Find")
803 if status.show_gui:
804 self.find_dlg.Show(True)
805
806
807 else:
808 self.find_dlg.Show()
809
810
812 """Find the next instance of the text.
813
814 @param event: The wx event.
815 @type event: wx event
816 """
817
818
819 if self.find_data.GetFindString():
820 self.find(event)
821
822
823 else:
824 self.find_open(event)
825
826
827 - def get_text(self):
828 """Concatenate all of the text from the log queue and return it as a string.
829
830 @return: A list of the text from the log queue and a list of the streams these correspond to.
831 @rtype: list of str, list of int
832 """
833
834
835 string_list = ['']
836 stream_list = [0]
837
838
839 while True:
840
841 if self.log_queue.empty():
842 break
843
844
845 msg, stream = self.log_queue.get()
846
847
848 if msg[1:7] == 'relax>':
849
850 string_list[-1] += '\n'
851
852
853 string_list.append('relax>')
854 stream_list.append(2)
855
856
857 msg = msg[7:]
858
859
860 string_list.append('')
861 stream_list.append(stream)
862
863
864 elif msg[0:13] == 'RelaxWarning:':
865
866 string_list.append(msg)
867 stream_list.append(3)
868 continue
869
870
871 elif msg[0:6] == 'debug>':
872
873 string_list.append(msg)
874 stream_list.append(4)
875 continue
876
877
878 if stream_list[-1] != stream:
879 string_list.append('')
880 stream_list.append(stream)
881
882
883 string_list[-1] = string_list[-1] + msg
884
885
886 return string_list, stream_list
887
888
919
920
922 """Copy the selected text.
923
924 @param event: The wx event.
925 @type event: wx event
926 """
927
928
929 self.Copy()
930
931
933 """Move to the end of the text.
934
935 @param event: The wx event.
936 @type event: wx event
937 """
938
939
940 self.GotoPos(self.GetLength())
941
942
944 """Move to the start of the text.
945
946 @param event: The wx event.
947 @type event: wx event
948 """
949
950
951 self.GotoPos(-1)
952
953
955 """Select all text in the control.
956
957 @param event: The wx event.
958 @type event: wx event
959 """
960
961
962 self.SelectAll()
963
964
966 """Zoom in by increase the font by 1 point size.
967
968 @param event: The wx event.
969 @type event: wx event
970 """
971
972
973 self.ZoomIn()
974
975
977 """Zoom to the original zoom level.
978
979 @param event: The wx event.
980 @type event: wx event
981 """
982
983
984 self.SetZoom(self.orig_zoom)
985
986
988 """Zoom out by decreasing the font by 1 point size.
989
990 @param event: The wx event.
991 @type event: wx event
992 """
993
994
995 self.ZoomOut()
996
997
999 """Override the StyledTextCtrl pop up menu.
1000
1001 @param event: The wx event.
1002 @type event: wx event
1003 """
1004
1005
1006 menu = wx.Menu()
1007
1008
1009 menu.AppendItem(build_menu_item(menu, id=self.menu_id_find, text="&Find", icon=fetch_icon('oxygen.actions.edit-find', "16x16")))
1010 menu.AppendSeparator()
1011 menu.AppendItem(build_menu_item(menu, id=self.menu_id_copy, text="&Copy", icon=fetch_icon('oxygen.actions.edit-copy', "16x16")))
1012 menu.AppendItem(build_menu_item(menu, id=self.menu_id_select_all, text="&Select all", icon=fetch_icon('oxygen.actions.edit-select-all', "16x16")))
1013 menu.AppendSeparator()
1014 menu.AppendItem(build_menu_item(menu, id=self.menu_id_zoom_in, text="Zoom &in", icon=fetch_icon('oxygen.actions.zoom-in', "16x16")))
1015 menu.AppendItem(build_menu_item(menu, id=self.menu_id_zoom_out, text="Zoom &out", icon=fetch_icon('oxygen.actions.zoom-out', "16x16")))
1016 menu.AppendItem(build_menu_item(menu, id=self.menu_id_zoom_orig, text="Original &zoom", icon=fetch_icon('oxygen.actions.zoom-original', "16x16")))
1017 menu.AppendSeparator()
1018 menu.AppendItem(build_menu_item(menu, id=self.menu_id_goto_start, text="&Go to start", icon=fetch_icon('oxygen.actions.go-top', "16x16")))
1019 menu.AppendItem(build_menu_item(menu, id=self.menu_id_goto_end, text="&Go to end", icon=fetch_icon('oxygen.actions.go-bottom', "16x16")))
1020
1021
1022 if status.show_gui:
1023 self.PopupMenu(menu)
1024 menu.Destroy()
1025
1026
1028 """Write the text in the log queue to the log control."""
1029
1030
1031 string_list, stream_list = self.get_text()
1032
1033
1034 if len(string_list) == 1 and string_list[0] == '':
1035 return
1036
1037
1038 at_end = False
1039 if self.GetScrollPos(wx.VERTICAL) == self.GetScrollRange(wx.VERTICAL) - self.LinesOnScreen():
1040 at_end = True
1041
1042
1043 self.SetReadOnly(False)
1044
1045
1046 for i in range(len(string_list)):
1047
1048 self.AppendText(string_list[i])
1049
1050
1051 if stream_list[i] != 0:
1052
1053 len_string = len(string_list[i].encode('utf8'))
1054 end = self.GetLength()
1055
1056
1057 self.StartStyling(end - len_string, 31)
1058 self.SetStyling(len_string, stream_list[i])
1059
1060
1061 if stream_list[i] in [1, 3] and status.show_gui:
1062
1063 if self.controller.IsShown():
1064 self.controller.Raise()
1065
1066
1067 else:
1068
1069 self.controller.Show()
1070 self.GotoPos(self.GetLength())
1071
1072
1073 self.limit_scrollback()
1074
1075
1076 if at_end:
1077 self.ScrollToLine(self.GetLineCount())
1078
1079
1080 self.SetReadOnly(True)
1081
1082
1083
1084 -class Redirect_text(object):
1085 """The IO redirection to text control object."""
1086
1087 - def __init__(self, control, log_queue, orig_io, stream=0):
1088 """Set up the text redirection object.
1089
1090 @param control: The text control object to redirect IO to.
1091 @type control: wx.TextCtrl instance
1092 @param log_queue: The queue of log messages.
1093 @type log_queue: Queue.Queue instance
1094 @param orig_io: The original IO stream, used for debugging and the test suite.
1095 @type orig_io: file
1096 @keyword stream: The type of steam (0 for STDOUT and 1 for STDERR).
1097 @type stream: int
1098 """
1099
1100
1101 self.control = control
1102 self.log_queue = log_queue
1103 self.orig_io = orig_io
1104 self.stream = stream
1105
1106
1108 """Simulate the file object flush method."""
1109
1110
1111 wx.CallAfter(self.control.write)
1112
1113
1115 """Answer that this is not a TTY.
1116
1117 @return: False, as this is not a TTY.
1118 @rtype: bool
1119 """
1120
1121 return False
1122
1123
1124 - def write(self, string):
1125 """Simulate the file object write method.
1126
1127 @param string: The text to write.
1128 @type string: str
1129 """
1130
1131
1132 if status.debug or status.test_mode:
1133 self.orig_io.write(string)
1134
1135
1136 self.log_queue.put([string, self.stream])
1137
1138
1139 wx.CallAfter(self.control.write)
1140