]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/DelayedResult.py
2 This demonstrates a simple use of delayedresult: get/compute
3 something that takes a long time, without hanging the GUI while this
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().
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.
19 import wx
.lib
.delayedresult
as delayedresult
22 class FrameSimpleDelayedBase(wx
.Frame
):
23 def __init__(self
, *args
, **kwds
):
24 wx
.Frame
.__init
__(self
, *args
, **kwds
)
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
)
33 self
.checkboxUseDelayed
.SetValue(1)
34 self
.checkboxUseDelayed
.Enable(False)
35 self
.buttonAbort
.Enable(False)
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)
46 vsizer
.SetSizeHints(self
)
48 self
.Bind(wx
.EVT_BUTTON
, self
.handleGet
, self
.buttonGet
)
49 self
.Bind(wx
.EVT_BUTTON
, self
.handleAbort
, self
.buttonAbort
)
54 class FrameSimpleDelayed(FrameSimpleDelayedBase
):
55 """This demos simplistic use of delayedresult module."""
57 def __init__(self
, *args
, **kwargs
):
58 FrameSimpleDelayedBase
.__init
__(self
, *args
, **kwargs
)
60 self
.Bind(wx
.EVT_CLOSE
, self
.handleClose
)
62 def setLog(self
, log
):
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():
71 wx
.FutureCall(5000, self
.Destroy
)
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)
80 self
.log( "Starting job %s in producer thread: GUI remains responsive"
82 delayedresult
.startWorker(self
._resultConsumer
, self
._resultProducer
,
83 wargs
=(self
.jobID
,), jobID
=self
.jobID
)
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."""
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)
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
)
114 result
= delayedResult
.get()
115 except Exception, exc
:
116 self
.log( "Result for job %s raised exception: %s" % (jobID
, exc
) )
121 self
.log( "Got result for job %s: %s" % (jobID
, result
) )
122 self
.textCtrlResult
.SetValue(str(result
))
124 # get ready for next job:
125 self
.buttonGet
.Enable(True)
126 self
.buttonAbort
.Enable(False)
130 class FrameSimpleDirect(FrameSimpleDelayedBase
):
131 """This does not use delayedresult so the GUI will freeze while
132 the GET is taking place."""
134 def __init__(self
, *args
, **kwargs
):
136 FrameSimpleDelayedBase
.__init
__(self
, *args
, **kwargs
)
137 self
.checkboxUseDelayed
.SetValue(False)
139 def setLog(self
, log
):
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
146 self
.buttonGet
.Enable(False)
147 self
.buttonAbort
.Enable(True)
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
)
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."""
161 def handleAbort(self
, event
):
162 """can never be called"""
165 def _resultConsumer(self
, result
):
167 self
.log( "Got result for job %s: %s" % (self
.jobID
, result
) )
168 self
.textCtrlResult
.SetValue(str(result
))
170 # get ready for next job:
171 self
.buttonGet
.Enable(True)
172 self
.buttonAbort
.Enable(False)
176 #---------------------------------------------------------------------------
177 #---------------------------------------------------------------------------
179 class TestPanel(wx
.Panel
):
180 def __init__(self
, parent
, log
):
182 wx
.Panel
.__init
__(self
, parent
, -1)
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
)
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
)
194 bdr
.Add(vsizer
, 0, wx
.ALL
, 50)
198 def OnButton1(self
, evt
):
199 frame
= FrameSimpleDelayed(self
, title
="Long-running function in separate thread")
200 frame
.setLog(self
.log
.WriteText
)
203 def OnButton2(self
, evt
):
204 frame
= FrameSimpleDirect(self
, title
="Long-running function in GUI thread")
205 frame
.setLog(self
.log
.WriteText
)
209 #---------------------------------------------------------------------------
212 def runTest(frame
, nb
, log
):
213 win
= TestPanel(nb
, log
)
217 #---------------------------------------------------------------------------
223 if __name__
== '__main__':
226 run
.main(['', os
.path
.basename(sys
.argv
[0])] + sys
.argv
[1:])