]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/DelayedResult.py
minor demo tweak
[wxWidgets.git] / wxPython / demo / DelayedResult.py
CommitLineData
8bbd1bbf
RD
1"""
2This demonstrates a simple use of delayedresult: get/compute
3something that takes a long time, without hanging the GUI while this
4is taking place.
5
6The top button runs a small GUI that uses wx.lib.delayedresult.startWorker
7to wrap a long-running function into a separate thread. Just click
8Get, and move the slider, and click Get and Abort a few times, and
9observe that GUI responds. The key functions to look for in the code
10are startWorker() and __handleResult().
11
12The second button runs the same GUI, but without delayedresult. Click
13Get: now the get/compute is taking place in main thread, so the GUI
14does not respond to user actions until worker function returns, it's
15not even possible to Abort.
16"""
17
18import wx
24648f55 19import wx.lib.delayedresult as delayedresult
8bbd1bbf 20
24648f55
RD
21
22class FrameSimpleDelayedBase(wx.Frame):
8bbd1bbf 23 def __init__(self, *args, **kwds):
8bbd1bbf 24 wx.Frame.__init__(self, *args, **kwds)
24648f55
RD
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)
8bbd1bbf 32
24648f55
RD
33 self.checkboxUseDelayed.SetValue(1)
34 self.checkboxUseDelayed.Enable(False)
35 self.buttonAbort.Enable(False)
8bbd1bbf 36
24648f55
RD
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
8bbd1bbf
RD
48 self.Bind(wx.EVT_BUTTON, self.handleGet, self.buttonGet)
49 self.Bind(wx.EVT_BUTTON, self.handleAbort, self.buttonAbort)
8bbd1bbf 50
24648f55
RD
51
52
53
54class FrameSimpleDelayed(FrameSimpleDelayedBase):
8bbd1bbf
RD
55 """This demos simplistic use of delayedresult module."""
56
57 def __init__(self, *args, **kwargs):
24648f55 58 FrameSimpleDelayedBase.__init__(self, *args, **kwargs)
8bbd1bbf 59 self.jobID = 1
8bbd1bbf 60 self.Bind(wx.EVT_CLOSE, self.handleClose)
24648f55 61
8bbd1bbf
RD
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()
24648f55
RD
71 wx.FutureCall(5000, self.Destroy)
72 else:
73 self.Destroy()
8bbd1bbf
RD
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
24648f55
RD
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
8bbd1bbf 85
24648f55 86 def _resultProducer(self, jobID):
8bbd1bbf
RD
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
24648f55 94
8bbd1bbf
RD
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
24648f55 103
8bbd1bbf 104
24648f55 105 def _resultConsumer(self, delayedResult):
8bbd1bbf
RD
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
24648f55 130class FrameSimpleDirect(FrameSimpleDelayedBase):
8bbd1bbf
RD
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
24648f55 136 FrameSimpleDelayedBase.__init__(self, *args, **kwargs)
8bbd1bbf
RD
137 self.checkboxUseDelayed.SetValue(False)
138
139 def setLog(self, log):
140 self.log = log
141
142 def handleGet(self, event):
24648f55
RD
143 """Use delayedresult, this will compute result in separate
144 thread, and will affect GUI response because a thread is not
145 used."""
8bbd1bbf
RD
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 )
24648f55
RD
150 result = self._resultProducer(self.jobID)
151 self._resultConsumer( result )
8bbd1bbf 152
24648f55 153 def _resultProducer(self, jobID):
8bbd1bbf
RD
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
24648f55 165 def _resultConsumer(self, result):
8bbd1bbf
RD
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
179class 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
212def runTest(frame, nb, log):
213 win = TestPanel(nb, log)
214 return win
215
216
217#---------------------------------------------------------------------------
218
219
220overview = __doc__
221
222
223if __name__ == '__main__':
224 import sys,os
225 import run
226 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
227
228