]> git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/DelayedResult.py
fixed wxVsnprintf() to write as much as it can if the output buffer is too short
[wxWidgets.git] / wxPython / demo / DelayedResult.py
1 """
2 This demonstrates a simple use of delayedresult: get/compute
3 something that takes a long time, without hanging the GUI while this
4 is taking place.
5
6 The top button runs a small GUI that uses wx.lib.delayedresult.startWorker
7 to wrap a long-running function into a separate thread. Just click
8 Get, and move the slider, and click Get and Abort a few times, and
9 observe that GUI responds. The key functions to look for in the code
10 are startWorker() and __handleResult().
11
12 The second button runs the same GUI, but without delayedresult. Click
13 Get: now the get/compute is taking place in main thread, so the GUI
14 does not respond to user actions until worker function returns, it's
15 not even possible to Abort.
16 """
17
18 import wx
19 import wx.lib.delayedresult as delayedresult
20
21
22 class FrameSimpleDelayedBase(wx.Frame):
23 def __init__(self, *args, **kwds):
24 wx.Frame.__init__(self, *args, **kwds)
25 pnl = wx.Panel(self)
26 self.checkboxUseDelayed = wx.CheckBox(pnl, -1, "Using delayedresult")
27 self.buttonGet = wx.Button(pnl, -1, "Get")
28 self.buttonAbort = wx.Button(pnl, -1, "Abort")
29 self.slider = wx.Slider(pnl, -1, 0, 0, 10, size=(100,-1),
30 style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS)
31 self.textCtrlResult = wx.TextCtrl(pnl, -1, "", style=wx.TE_READONLY)
32
33 self.checkboxUseDelayed.SetValue(1)
34 self.checkboxUseDelayed.Enable(False)
35 self.buttonAbort.Enable(False)
36
37 vsizer = wx.BoxSizer(wx.VERTICAL)
38 hsizer = wx.BoxSizer(wx.HORIZONTAL)
39 vsizer.Add(self.checkboxUseDelayed, 0, wx.ALL, 10)
40 hsizer.Add(self.buttonGet, 0, wx.ALL, 5)
41 hsizer.Add(self.buttonAbort, 0, wx.ALL, 5)
42 hsizer.Add(self.slider, 0, wx.ALL, 5)
43 hsizer.Add(self.textCtrlResult, 0, wx.ALL, 5)
44 vsizer.Add(hsizer, 0, wx.ALL, 5)
45 pnl.SetSizer(vsizer)
46 vsizer.SetSizeHints(self)
47
48 self.Bind(wx.EVT_BUTTON, self.handleGet, self.buttonGet)
49 self.Bind(wx.EVT_BUTTON, self.handleAbort, self.buttonAbort)
50
51
52
53
54 class FrameSimpleDelayed(FrameSimpleDelayedBase):
55 """This demos simplistic use of delayedresult module."""
56
57 def __init__(self, *args, **kwargs):
58 FrameSimpleDelayedBase.__init__(self, *args, **kwargs)
59 self.jobID = 0
60 self.abortEvent = delayedresult.AbortEvent()
61 self.Bind(wx.EVT_CLOSE, self.handleClose)
62
63 def setLog(self, log):
64 self.log = log
65
66 def handleClose(self, event):
67 """Only needed because in demo, closing the window does not kill the
68 app, so worker thread continues and sends result to dead frame; normally
69 your app would exit so this would not happen."""
70 if self.buttonAbort.IsEnabled():
71 self.log( "Exiting: Aborting job %s" % self.jobID )
72 self.abortEvent.set()
73 self.Destroy()
74
75 def handleGet(self, event):
76 """Compute result in separate thread, doesn't affect GUI response."""
77 self.buttonGet.Enable(False)
78 self.buttonAbort.Enable(True)
79 self.abortEvent.clear()
80 self.jobID += 1
81
82 self.log( "Starting job %s in producer thread: GUI remains responsive"
83 % self.jobID )
84 delayedresult.startWorker(self._resultConsumer, self._resultProducer,
85 wargs=(self.jobID,self.abortEvent), jobID=self.jobID)
86
87
88 def _resultProducer(self, jobID, abortEvent):
89 """Pretend to be a complex worker function or something that takes
90 long time to run due to network access etc. GUI will freeze if this
91 method is not called in separate thread."""
92 import time
93 count = 0
94 while not abortEvent() and count < 50:
95 time.sleep(0.1)
96 count += 1
97 return jobID
98
99
100 def handleAbort(self, event):
101 """Abort the result computation."""
102 self.log( "Aborting result for job %s" % self.jobID )
103 self.buttonGet.Enable(True)
104 self.buttonAbort.Enable(False)
105 self.abortEvent.set()
106
107
108 def _resultConsumer(self, delayedResult):
109 jobID = delayedResult.getJobID()
110 assert jobID == self.jobID
111 try:
112 result = delayedResult.get()
113 except Exception, exc:
114 self.log( "Result for job %s raised exception: %s" % (jobID, exc) )
115 return
116
117 # output result
118 self.log( "Got result for job %s: %s" % (jobID, result) )
119 self.textCtrlResult.SetValue(str(result))
120
121 # get ready for next job:
122 self.buttonGet.Enable(True)
123 self.buttonAbort.Enable(False)
124
125
126 class FrameSimpleDirect(FrameSimpleDelayedBase):
127 """This does not use delayedresult so the GUI will freeze while
128 the GET is taking place."""
129
130 def __init__(self, *args, **kwargs):
131 self.jobID = 1
132 FrameSimpleDelayedBase.__init__(self, *args, **kwargs)
133 self.checkboxUseDelayed.SetValue(False)
134
135 def setLog(self, log):
136 self.log = log
137
138 def handleGet(self, event):
139 """Use delayedresult, this will compute result in separate
140 thread, and will affect GUI response because a thread is not
141 used."""
142 self.buttonGet.Enable(False)
143 self.buttonAbort.Enable(True)
144
145 self.log( "Doing job %s without delayedresult (same as GUI thread): GUI hangs (for a while)" % self.jobID )
146 result = self._resultProducer(self.jobID)
147 self._resultConsumer( result )
148
149 def _resultProducer(self, jobID):
150 """Pretend to be a complex worker function or something that takes
151 long time to run due to network access etc. GUI will freeze if this
152 method is not called in separate thread."""
153 import time
154 time.sleep(5)
155 return jobID
156
157 def handleAbort(self, event):
158 """can never be called"""
159 pass
160
161 def _resultConsumer(self, result):
162 # output result
163 self.log( "Got result for job %s: %s" % (self.jobID, result) )
164 self.textCtrlResult.SetValue(str(result))
165
166 # get ready for next job:
167 self.buttonGet.Enable(True)
168 self.buttonAbort.Enable(False)
169 self.jobID += 1
170
171
172 #---------------------------------------------------------------------------
173 #---------------------------------------------------------------------------
174
175 class TestPanel(wx.Panel):
176 def __init__(self, parent, log):
177 self.log = log
178 wx.Panel.__init__(self, parent, -1)
179
180 vsizer = wx.BoxSizer(wx.VERTICAL)
181 b = wx.Button(self, -1, "Long-running function in separate thread")
182 vsizer.Add(b, 0, wx.ALL, 5)
183 self.Bind(wx.EVT_BUTTON, self.OnButton1, b)
184
185 b = wx.Button(self, -1, "Long-running function in GUI thread")
186 vsizer.Add(b, 0, wx.ALL, 5)
187 self.Bind(wx.EVT_BUTTON, self.OnButton2, b)
188
189 bdr = wx.BoxSizer()
190 bdr.Add(vsizer, 0, wx.ALL, 50)
191 self.SetSizer(bdr)
192 self.Layout()
193
194 def OnButton1(self, evt):
195 frame = FrameSimpleDelayed(self, title="Long-running function in separate thread")
196 frame.setLog(self.log.WriteText)
197 frame.Show()
198
199 def OnButton2(self, evt):
200 frame = FrameSimpleDirect(self, title="Long-running function in GUI thread")
201 frame.setLog(self.log.WriteText)
202 frame.Show()
203
204
205 #---------------------------------------------------------------------------
206
207
208 def runTest(frame, nb, log):
209 win = TestPanel(nb, log)
210 return win
211
212
213 #---------------------------------------------------------------------------
214
215
216 overview = __doc__
217
218
219 if __name__ == '__main__':
220 import sys,os
221 import run
222 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
223
224