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