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