]> git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/DelayedResult.py
Tweaks and cleanup
[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 = 1
60 self.Bind(wx.EVT_CLOSE, self.handleClose)
61
62 def setLog(self, log):
63 self.log = log
64
65 def handleClose(self, event):
66 """Only needed because in demo, closing the window does not kill the
67 app, so worker thread continues and sends result to dead frame; normally
68 your app would exit so this would not happen."""
69 if self.buttonAbort.IsEnabled():
70 self.Hide()
71 wx.FutureCall(5000, self.Destroy)
72 else:
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
80 self.log( "Starting job %s in producer thread: GUI remains responsive"
81 % self.jobID )
82 delayedresult.startWorker(self._resultConsumer, self._resultProducer,
83 wargs=(self.jobID,), jobID=self.jobID)
84
85
86 def _resultProducer(self, jobID):
87 """Pretend to be a complex worker function or something that takes
88 long time to run due to network access etc. GUI will freeze if this
89 method is not called in separate thread."""
90 import time
91 time.sleep(5)
92 return jobID
93
94
95 def handleAbort(self, event):
96 """Abort actually just means 'ignore the result when it gets to
97 handler, it is no longer relevant'. We just increase the job ID,
98 this will let handler know that the result has been cancelled."""
99 self.log( "Aborting result for job %s" % self.jobID )
100 self.buttonGet.Enable(True)
101 self.buttonAbort.Enable(False)
102 self.jobID += 1
103
104
105 def _resultConsumer(self, delayedResult):
106 # See if we still want the result for last job started
107 jobID = delayedResult.getJobID()
108 if jobID != self.jobID:
109 self.log( "Got obsolete result for job %s, ignored" % jobID )
110 return
111
112 # we do, get result:
113 try:
114 result = delayedResult.get()
115 except Exception, exc:
116 self.log( "Result for job %s raised exception: %s" % (jobID, exc) )
117 self.jobID += 1
118 return
119
120 # output result
121 self.log( "Got result for job %s: %s" % (jobID, result) )
122 self.textCtrlResult.SetValue(str(result))
123
124 # get ready for next job:
125 self.buttonGet.Enable(True)
126 self.buttonAbort.Enable(False)
127 self.jobID += 1
128
129
130 class FrameSimpleDirect(FrameSimpleDelayedBase):
131 """This does not use delayedresult so the GUI will freeze while
132 the GET is taking place."""
133
134 def __init__(self, *args, **kwargs):
135 self.jobID = 1
136 FrameSimpleDelayedBase.__init__(self, *args, **kwargs)
137 self.checkboxUseDelayed.SetValue(False)
138
139 def setLog(self, log):
140 self.log = log
141
142 def handleGet(self, event):
143 """Use delayedresult, this will compute result in separate
144 thread, and will affect GUI response because a thread is not
145 used."""
146 self.buttonGet.Enable(False)
147 self.buttonAbort.Enable(True)
148
149 self.log( "Doing job %s without delayedresult (same as GUI thread): GUI hangs (for a while)" % self.jobID )
150 result = self._resultProducer(self.jobID)
151 self._resultConsumer( result )
152
153 def _resultProducer(self, jobID):
154 """Pretend to be a complex worker function or something that takes
155 long time to run due to network access etc. GUI will freeze if this
156 method is not called in separate thread."""
157 import time
158 time.sleep(5)
159 return jobID
160
161 def handleAbort(self, event):
162 """can never be called"""
163 pass
164
165 def _resultConsumer(self, result):
166 # output result
167 self.log( "Got result for job %s: %s" % (self.jobID, result) )
168 self.textCtrlResult.SetValue(str(result))
169
170 # get ready for next job:
171 self.buttonGet.Enable(True)
172 self.buttonAbort.Enable(False)
173 self.jobID += 1
174
175
176 #---------------------------------------------------------------------------
177 #---------------------------------------------------------------------------
178
179 class TestPanel(wx.Panel):
180 def __init__(self, parent, log):
181 self.log = log
182 wx.Panel.__init__(self, parent, -1)
183
184 vsizer = wx.BoxSizer(wx.VERTICAL)
185 b = wx.Button(self, -1, "Long-running function in separate thread")
186 vsizer.Add(b, 0, wx.ALL, 5)
187 self.Bind(wx.EVT_BUTTON, self.OnButton1, b)
188
189 b = wx.Button(self, -1, "Long-running function in GUI thread")
190 vsizer.Add(b, 0, wx.ALL, 5)
191 self.Bind(wx.EVT_BUTTON, self.OnButton2, b)
192
193 bdr = wx.BoxSizer()
194 bdr.Add(vsizer, 0, wx.ALL, 50)
195 self.SetSizer(bdr)
196 self.Layout()
197
198 def OnButton1(self, evt):
199 frame = FrameSimpleDelayed(self, title="Long-running function in separate thread")
200 frame.setLog(self.log.WriteText)
201 frame.Show()
202
203 def OnButton2(self, evt):
204 frame = FrameSimpleDirect(self, title="Long-running function in GUI thread")
205 frame.setLog(self.log.WriteText)
206 frame.Show()
207
208
209 #---------------------------------------------------------------------------
210
211
212 def runTest(frame, nb, log):
213 win = TestPanel(nb, log)
214 return win
215
216
217 #---------------------------------------------------------------------------
218
219
220 overview = __doc__
221
222
223 if __name__ == '__main__':
224 import sys,os
225 import run
226 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
227
228