]>
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
.abortEvent 
= delayedresult
.AbortEvent() 
  61         self
.Bind(wx
.EVT_CLOSE
, self
.handleClose
) 
  63     def setLog(self
, log
): 
  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 
) 
  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() 
  82         self
.log( "Starting job %s in producer thread: GUI remains responsive" 
  84         delayedresult
.startWorker(self
._resultConsumer
, self
._resultProducer
,  
  85                                   wargs
=(self
.jobID
,self
.abortEvent
), jobID
=self
.jobID
) 
  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.""" 
  94         while not abortEvent() and count 
< 50: 
 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() 
 108     def _resultConsumer(self
, delayedResult
): 
 109         jobID 
= delayedResult
.getJobID() 
 110         assert jobID 
== self
.jobID
 
 112             result 
= delayedResult
.get() 
 113         except Exception, exc
: 
 114             self
.log( "Result for job %s raised exception: %s" % (jobID
, exc
) ) 
 118         self
.log( "Got result for job %s: %s" % (jobID
, result
) ) 
 119         self
.textCtrlResult
.SetValue(str(result
)) 
 121         # get ready for next job: 
 122         self
.buttonGet
.Enable(True) 
 123         self
.buttonAbort
.Enable(False) 
 126 class FrameSimpleDirect(FrameSimpleDelayedBase
): 
 127     """This does not use delayedresult so the GUI will freeze while 
 128     the GET is taking place.""" 
 130     def __init__(self
, *args
, **kwargs
): 
 132         FrameSimpleDelayedBase
.__init
__(self
, *args
, **kwargs
) 
 133         self
.checkboxUseDelayed
.SetValue(False) 
 135     def setLog(self
, log
): 
 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 
 142         self
.buttonGet
.Enable(False) 
 143         self
.buttonAbort
.Enable(True) 
 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 
) 
 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.""" 
 157     def handleAbort(self
, event
): 
 158         """can never be called""" 
 161     def _resultConsumer(self
, result
): 
 163         self
.log( "Got result for job %s: %s" % (self
.jobID
, result
) ) 
 164         self
.textCtrlResult
.SetValue(str(result
)) 
 166         # get ready for next job: 
 167         self
.buttonGet
.Enable(True) 
 168         self
.buttonAbort
.Enable(False) 
 172 #--------------------------------------------------------------------------- 
 173 #--------------------------------------------------------------------------- 
 175 class TestPanel(wx
.Panel
): 
 176     def __init__(self
, parent
, log
): 
 178         wx
.Panel
.__init
__(self
, parent
, -1) 
 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
) 
 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
) 
 190         bdr
.Add(vsizer
, 0, wx
.ALL
, 50) 
 194     def OnButton1(self
, evt
): 
 195         frame 
= FrameSimpleDelayed(self
, title
="Long-running function in separate thread") 
 196         frame
.setLog(self
.log
.WriteText
) 
 199     def OnButton2(self
, evt
): 
 200         frame 
= FrameSimpleDirect(self
, title
="Long-running function in GUI thread") 
 201         frame
.setLog(self
.log
.WriteText
) 
 205 #--------------------------------------------------------------------------- 
 208 def runTest(frame
, nb
, log
): 
 209     win 
= TestPanel(nb
, log
) 
 213 #--------------------------------------------------------------------------- 
 219 if __name__ 
== '__main__': 
 222    run
.main(['', os
.path
.basename(sys
.argv
[0])] + sys
.argv
[1:])