Package park :: Package core :: Module wxmonitor

Source Code for Module park.core.wxmonitor

  1  # This program is public domain 
  2  """ 
  3  Asynchronous monitoring service for wx applications. 
  4   
  5  Define a monitor using park.wxmonitor.wxMonitor(panel) where panel is 
  6  the window which will receive the monitor updates. 
  7   
  8  In panel, be sure to have methods for onMonitorStart(message), 
  9  onMonitorProgress(message), etc., for the kinds of monitor messages 
 10  the application will send.  The catch-all method is onMonitorMessage, 
 11  which by default will print the messages on the console.  If you 
 12  don't catch onMonitorLog messages then the log messages will be 
 13  sent to the standard python logger. 
 14   
 15  See `park.monitor` for details on the message types. 
 16   
 17  Example 
 18  ======= 
 19   
 20  The following defines a panel which responds to monitor messages:: 
 21   
 22      import wx 
 23   
 24      class Panel(wx.Panel): 
 25          def __init__(self, *args, **kw): 
 26              wx.Panel.__init__(self, *args, **kw) 
 27              self.text = wx.TextCtrl(self, size=(200,100), style=wx.TE_MULTILINE) 
 28              self.gauge = wx.Gauge(self, range=100) 
 29              sizer = wx.BoxSizer(wx.VERTICAL) 
 30              sizer.Add(self.text, 0, wx.LEFT | wx.EXPAND) 
 31              sizer.Add(self.gauge, 0, wx.LEFT | wx.EXPAND) 
 32              self.SetSizer(sizer) 
 33              self.text.SetValue('starting value') 
 34          def onMonitorMessage(self, message): 
 35              self.text.SetValue(str(message)) 
 36          def onMonitorStart(self, message): 
 37              self.text.SetValue(str(message)) 
 38              self.gauge.SetValue(0) 
 39          def onMonitorProgress(self, message): 
 40              self.text.SetValue(str(message)) 
 41              self.gauge.SetValue(int(100*message.complete/message.total)) 
 42          def onMonitorComplete(self, message): 
 43              self.text.SetValue(str(message)) 
 44              self.gauge.SetValue(100) 
 45   
 46  We can put this panel in a simple app:: 
 47   
 48      app = wx.PySimpleApp() 
 49      frame = wx.Frame(None, -1, 'Test Monitor') 
 50      panel = Panel(frame) 
 51      frame.Show() 
 52   
 53  Next we attach attach the monitor to this panel and feed some messages from 
 54  another thread:: 
 55   
 56      import time,thread 
 57      import park.wxmonitor, park.monitor 
 58      from park.monitor import Start, Progress, Improvement, Complete 
 59      monitor = park.wxmonitor.wxMonitor(panel) 
 60      msgs = [Start(), Progress(1,10), Progress(3,10), 
 61              Improvement('Better!'), Progerss(6,10), Complete('Best!')]: 
 62      def message_stream(monitor,msgs): 
 63          time.sleep(1) 
 64          for message in msgs: 
 65            monitor.put(message) 
 66            time.sleep(1) 
 67      thread.start_new_thread(message_stream, (monitor,msgs)) 
 68      app.MainLoop() 
 69   
 70  You should see the progress bar jump from 10% to 30% to 60% then all the way 
 71  to the end. 
 72  """ 
 73  import logging 
 74  import time 
 75   
 76  import wx 
 77  import wx.lib.newevent 
 78   
 79  import park.core.monitor 
 80   
 81  (MonitorEvent, EVT_MONITOR) = wx.lib.newevent.NewEvent() 
 82   
 83  # For wx on Mac OS X we need to sleep after posting a message from 
 84  # a thread in order to give the GUI a chance to update itself. 
 85  SLEEP_TIME = 0.01 
86 -class Monitor(park.core.monitor.Monitor):
87 """ 88 Attach a job monitor to a panel. 89 90 The monitor will perform callbacks to onMonitorStart(message), 91 onMonitorProgress(message), etc. if the associated method is 92 defined. If the type specific method is not defined, then the 93 monitor will call onMonitorMessage(message). Otherwise the 94 message is dropped. 95 96 See `park.monitor` for a description of the usual messages. 97 """
98 - def __init__(self, win):
99 """ 100 Window to receive the monitoring events. This is running in the 101 GUI thread. 102 """ 103 self.win = win 104 win.Bind(EVT_MONITOR, self.dispatch)
105
106 - def put(self, message):
107 """ 108 Intercept an event received from an asynchronous monitor. This is 109 running in the asynchronous thread. 110 """ 111 #print "dispatch",message 112 event = MonitorEvent(message=message) 113 wx.PostEvent(self.win, event) 114 time.sleep(SLEEP_TIME)
115
116 - def dispatch(self, event):
117 """ 118 Dispatch the event from the asynchronous monitor. This is running 119 in the GUI thread. 120 """ 121 message = event.message 122 #print "window dispatch",message 123 124 # First check for a handler in the monitor window 125 fn = getattr(self.win, 'onMonitor'+message.__class__.__name__, None) 126 # If none, then check in our class (we have a default onMonitorLog) 127 if fn is None: 128 fn = getattr(self, 'onMonitor'+message.__class__.__name__, None) 129 # If still none, then look for the generic handler 130 if fn is None: 131 fn = getattr(self.win, 'onMonitorMessage', self.onMonitorMessage) 132 # Process the message 133 fn(message)
134
135 - def onMonitorMessage(self, message):
136 """ 137 Generic message handler: do nothing. 138 """ 139 print ">",str(message)
140
141 - def onMonitorLog(self, message):
142 """ 143 Called when the job sends a logging record. 144 145 The logging record contains a normal python logging record. 146 147 The default behaviour is to tie into the application logging 148 system using:: 149 150 logger = logging.getLogger(message.record.name) 151 logger.handle(message.record) 152 153 Logging levels are set in the job controller. 154 """ 155 logging.basicConfig() 156 logger = logging.getLogger(message.record.name) 157 logger.handle(message.record)
158 159 # === Demonstration code === 160
161 -def demo(rate=0):
162 import time 163 import sys 164 import logging 165 166 class Panel(wx.Panel): 167 def __init__(self, *args, **kw): 168 wx.Panel.__init__(self, *args, **kw) 169 self.text = wx.TextCtrl(self, size=(200,100), style=wx.TE_MULTILINE) 170 self.gauge = wx.Gauge(self, range=100) 171 sizer = wx.BoxSizer(wx.VERTICAL) 172 sizer.Add(self.text, 0, wx.LEFT | wx.EXPAND) 173 sizer.Add(self.gauge, 0, wx.LEFT | wx.EXPAND) 174 self.SetSizer(sizer) 175 self.text.SetValue('starting value')
176 def onMonitorMessage(self, message): 177 self.text.SetValue(str(message)) 178 def onMonitorStarted(self, message): 179 self.text.SetValue(str(message)) 180 self.gauge.SetValue(0) 181 def onMonitorProgress(self, message): 182 self.text.SetValue(str(message)) 183 self.gauge.SetValue(int(100*message.complete/message.total)) 184 def onMonitorCompleted(self, message): 185 self.text.SetValue(str(message)) 186 self.gauge.SetValue(100) 187 188 app = wx.PySimpleApp() 189 frame = wx.Frame(None, -1, 'Test Monitor') 190 panel = Panel(frame) 191 frame.Show() 192 thread = park.core.monitor._demo_stream(rate=rate, monitor=Monitor(panel)) 193 thread.after(lambda result: frame.Close()) 194 app.MainLoop() 195 196 if __name__ == "__main__": 197 demo(rate=0.1) 198