]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/DelayedResult.py
always use wxPyPanel
[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)
45c0ea45
RD
59 self.jobID = 0
60 self.abortEvent = delayedresult.AbortEvent()
8bbd1bbf 61 self.Bind(wx.EVT_CLOSE, self.handleClose)
24648f55 62
8bbd1bbf
RD
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():
45c0ea45
RD
71 self.log( "Exiting: Aborting job %s" % self.jobID )
72 self.abortEvent.set()
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)
45c0ea45
RD
79 self.abortEvent.clear()
80 self.jobID += 1
81
24648f55
RD
82 self.log( "Starting job %s in producer thread: GUI remains responsive"
83 % self.jobID )
84 delayedresult.startWorker(self._resultConsumer, self._resultProducer,
45c0ea45 85 wargs=(self.jobID,self.abortEvent), jobID=self.jobID)
24648f55 86
8bbd1bbf 87
45c0ea45 88 def _resultProducer(self, jobID, abortEvent):
8bbd1bbf
RD
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
45c0ea45
RD
93 count = 0
94 while not abortEvent() and count < 50:
95 time.sleep(0.1)
96 count += 1
8bbd1bbf
RD
97 return jobID
98
24648f55 99
8bbd1bbf 100 def handleAbort(self, event):
45c0ea45 101 """Abort the result computation."""
8bbd1bbf
RD
102 self.log( "Aborting result for job %s" % self.jobID )
103 self.buttonGet.Enable(True)
104 self.buttonAbort.Enable(False)
45c0ea45 105 self.abortEvent.set()
24648f55 106
8bbd1bbf 107
24648f55 108 def _resultConsumer(self, delayedResult):
8bbd1bbf 109 jobID = delayedResult.getJobID()
45c0ea45 110 assert jobID == self.jobID
8bbd1bbf
RD
111 try:
112 result = delayedResult.get()
113 except Exception, exc:
114 self.log( "Result for job %s raised exception: %s" % (jobID, exc) )
8bbd1bbf
RD
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)
8bbd1bbf
RD
124
125
24648f55 126class FrameSimpleDirect(FrameSimpleDelayedBase):
8bbd1bbf
RD
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
24648f55 132 FrameSimpleDelayedBase.__init__(self, *args, **kwargs)
8bbd1bbf
RD
133 self.checkboxUseDelayed.SetValue(False)
134
135 def setLog(self, log):
136 self.log = log
137
138 def handleGet(self, event):
24648f55
RD
139 """Use delayedresult, this will compute result in separate
140 thread, and will affect GUI response because a thread is not
141 used."""
8bbd1bbf
RD
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 )
24648f55
RD
146 result = self._resultProducer(self.jobID)
147 self._resultConsumer( result )
8bbd1bbf 148
24648f55 149 def _resultProducer(self, jobID):
8bbd1bbf
RD
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
24648f55 161 def _resultConsumer(self, result):
8bbd1bbf
RD
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
175class 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
208def runTest(frame, nb, log):
209 win = TestPanel(nb, log)
210 return win
211
212
213#---------------------------------------------------------------------------
214
215
216overview = __doc__
217
218
219if __name__ == '__main__':
220 import sys,os
221 import run
222 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
223
224