wxProgressDialog problems

Larry Bates larry.bates at websafe.com
Sat Feb 23 12:46:16 PST 2008


Robin Dunn wrote:
> Larry Bates wrote:
>> Robin Dunn wrote:
>>> Larry Bates wrote:
>>>> I am trying to implement a file upload dialog (see code below).  It 
>>>> almost works, except that I can't get the cancel button to respond 
>>>> as expect.  I'm certain it is something really small.
>>>
>>> Events (like the cancel button event) won't be delivered if program 
>>> flow is not in the main loop.  In real life that probably won't be a 
>>> problem for most apps, as long as you don't block some event handler 
>>> in a long running task.  In your sample it can be taken care of by 
>>> using a timer to update the progress dialog.  See 
>>> http://wiki.wxpython.org/LongRunningTasks for ideas on how to 
>>> approach other situations.
>>>
>>>     class MyApp(wx.App):
>>>         def OnInit(self):
>>>             self.callback = Callback("test file upload")
>>>             self.callback.start(100)
>>>             self.timer = wx.Timer(self)
>>>             self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
>>>             self.timer.Start(200)
>>>             self.count = 0
>>>             return True
>>>
>>>         def OnTimer(self, evt):
>>>             if self.count < 100:
>>>                 self.callback(100, self.count)
>>>                 self.count += 1
>>>             else:
>>>                 self.callback.complete()
>>>
>>>     app = MyApp(0)
>>>     app.MainLoop()
>>>
>>>
>>
>> Robin,
>>
>> My problem is that the thing that calls this Callback is WAY down deep in
>> other code.  I want to register the Callback instance with that and it
>> gets called as the file is streamed (once per (65K block) over a 
>> WEBDAV connection.  I don't see how to adapt what you wrote or the 
>> examples on
>> the wiki like you sent.  When the cancel button is pushed it needs to
>> return False so I can cancel the streaming upload and work my way back
>> through all the layers back to my main program.
>>
>> Thoughts?
> 
> Do the other code in a thread, and make the callback use wx.CallAfter or 
> post an event so the update of the progress dialog can be done in the 
> gui thread.  If the cancel button was pressed then set a flag that the 
> download thread will check periodically so it knows to stop (or send the 
> message in some other thread-safe way).
> 
> 
Real newbie here.  It sounds like this would be the best way:

 > or post an event so the update of the progress dialog can be done in
 > the gui thread.

I "really" tried on this one, but it exhibits the same behavior.  Can't ever 
click on the Cancel button.  See sample code below.

Thanks for all your help.

-Larry

import wx
import time
myEVT_UPDATE_PROGRESS = wx.NewEventType()
EVT_UPDATE_PROGRESS = wx.PyEventBinder(myEVT_UPDATE_PROGRESS, 1)

class MyProgressEvent(wx.PyCommandEvent):
     def __init(self, evtType, id):
         wxPYcommendEvent.__init(self, evType, id)
         self.myVal=None

     def SetVal(self, val):
         self.myVal=val

     def GetVal(self):
         return self.myVal

class GUIprogressCallback(object):
     def __init__(self, parent):
         self.uploadTotal=0
         self.uploadPosition=0

         #
         # Bind my custom event that gets posted by caller to update the progress
         #
         parent.Bind(EVT_UPDATE_PROGRESS, self.update)

     def setUploadTotal(self, uploadTotal):
         '''
         setUploadTotal - if user is doing a mult-file upload and would like to
                          set the total upload size for all files, use this
                          method.  Otherwise we will assume a single-file upload
                          and the total will be the size of the file being
                          uploaded.
         '''
         self.uploadTotal=uploadTotal

     def start(self, fileName, fileSize):
         '''
         start - This method is called to start the upload of a file
         '''
         #
         # Truncate the fileName if it is too long to display
         #
         if len(fileName) > 90:
             fileName=".."+fileName[-90:]
         #
         # Create the dialog unless I've already created it (e.g. previous file
         # in a multi-file upload).
         #
         if not hasattr(self, 'dlg'):
             self._createDialog(msg=fileName, maximum=self.uploadTotal)

         #
         # Handle the possibility that files changed sizes between the time that
         # I calculated uploadTotal and when I actually get to uploading them by
         # backing down from uploadTotal, but can't be less than 0.
         #
         if self.uploadPosition+fileSize > self.uploadTotal:
             self.uploadPosition=max(self.uploadTotal-fileSize, 0)
         #
         # Update the dialog with the info
         #
         self.dlg.Update(self.uploadPosition, newmsg=fileName)
         return True

     def update(self, event):
         '''
         update - Events are posted as file upload is progressing.  This method
                  is bound so it picks up those events (every block).  We
                  calculate the percentage complete and update the progessDialog
                  with this information and update the progress gauge.
         '''
         filePosition=event.GetVal()
         #
         # Handle both single- and mult-file upload percentage complete.
         #
         uploadPosition=self.uploadPosition+filePosition
         #
         # Calculate and display the percent complete and update the position
         # of the progress dialog gauge.
         #
         percentstr=int(100.0*float(uploadPosition)/float(self.uploadTotal))
         msg="WebSafe Upload-%s%%" % percentstr
         self.dlg.SetLabel(msg)
         wx.Yield()
         keepgoing, skip=self.dlg.Update(uploadPosition)
         #
         # See if user clicked the cancel button
         #
         if not keepgoing:
             self.close()

         return True

     def _createDialog(self, msg='', maximum=100):
         #
         # Create progress dialog and display it, but it must run in a different
         # thread or user won't be able to click cancel button.
         #
         self.dlg=wx.ProgressDialog(title="WebSafe Upload",
                                    message="",
                                    maximum=maximum,
                                    style=wx.PD_APP_MODAL |
                                          #wx.PD_AUTO_HIDE | \
                                          wx.PD_CAN_ABORT | \
                                          wx.PD_REMAINING_TIME)

         self.dlg.SetDimensions(-1, -1, width=500, height=-1,
                                sizeFlags=wx.SIZE_AUTO_HEIGHT)

         self.dlg.Show(True)

     def complete(self, fileSize):
         self.uploadPosition=self.uploadPosition+fileSize
         return True

     def close(self):
         self.isRunning=False
         self.dlg.Destroy()
         return False


if __name__ == "__main__":
     import time
     app=wx.PySimpleApp(0)
     wx.InitAllImageHandlers()
     evt=MyProgressEvent(myEVT_UPDATE_PROGRESS, -1)
     blockSize=1<<16
     #
     # Test multi-file upload
     #
     fileNames=['filename1.txt', 'filename2.txt', 'filename3.txt']
     fileSizes=[(1<<20), 2*(1<<20), 3*(1<<20)]
     CB=GUIprogressCallback(app)
     CB.setUploadTotal(sum(fileSizes))
     for fileName, fileSize in zip(fileNames, fileSizes):
         CB.start(fileName, fileSize)
         for i in xrange(blockSize, fileSize, blockSize):
             evt.SetVal(i)
             app.ProcessEvent(evt)
             time.sleep(0.5)

         CB.complete(fileSize)
     CB.close()







More information about the wxpython-users mailing list