1
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
84
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 """
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
112 event = MonitorEvent(message=message)
113 wx.PostEvent(self.win, event)
114 time.sleep(SLEEP_TIME)
115
117 """
118 Dispatch the event from the asynchronous monitor. This is running
119 in the GUI thread.
120 """
121 message = event.message
122
123
124
125 fn = getattr(self.win, 'onMonitor'+message.__class__.__name__, None)
126
127 if fn is None:
128 fn = getattr(self, 'onMonitor'+message.__class__.__name__, None)
129
130 if fn is None:
131 fn = getattr(self.win, 'onMonitorMessage', self.onMonitorMessage)
132
133 fn(message)
134
136 """
137 Generic message handler: do nothing.
138 """
139 print ">",str(message)
140
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
160
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