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 compat import Queue
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.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