How Can I Safely Manage wxPython Progress Dialog Threading?

喜欢而已 提交于 2019-12-08 06:58:44

问题


I have several calculations to run without any user input and without the user worrying about the program having frozen, so I am trying to show a progress bar popup to keep them aware of what's happening.

Looking around has lead me to believe that I need to use a separate thread to do this, and I came up with this example.

import threading, wx, time

MAX_INT = 10
TEST_TUPLE = [[11, 22],[33,44]]

class mainFrame(wx.Frame):
    def __init__(self, parent, ID, title):
        wx.Frame.__init__(self, parent, ID, title)
        bt = wx.Button(self, wx.ID_OK)
        self.Bind(wx.EVT_BUTTON, self.onBt, bt)

    def onBt(self,event):
        self.dlg = wx.ProgressDialog("title", "message", maximum=MAX_INT)
        workThread = threading.Thread(target=self.doWork, args=(TEST_TUPLE,) )
        workThread.start()
        self.dlg.ShowModal()

    def doWork(self, testArg):
        # time consuming stuff that affects main GUI
        print testArg
        for i in range(1, MAX_INT+1):
            self.SetPosition((i*4*MAX_INT, i*2*MAX_INT))
            time.sleep(1)
            print str(i)+" of "+str(MAX_INT)
            wx.CallAfter(self.dlg.Update, i, "%i of %i"%(i, MAX_INT))
        self.dlg.Destroy()


app = wx.App(False)
fr = mainFrame(None, -1, "Title")
fr.Show()
app.MainLoop()

It seems to work as intended, but is there some housekeeping that I am skipping here?

EDIT: I replaced the dialog with a miniframe so the main window would not freeze, disabled the button so there wouldn't be several frames spawned, and added a crude cancelling method.

import threading, wx, time

MAX_INT = 10
TEST_TUPLE = [[11, 22],[33,44]]

class GaugeFrame(wx.MiniFrame):
    def __init__(self, parent, title, maximum):
        wx.MiniFrame.__init__(self, parent, title=title, size=(200, 60) )

        self.bar = wx.Gauge(self, range=maximum)
        self.buCancel = wx.Button(self, label="Cancel")
        self.SetBackgroundColour("LTGRAY")

        siMainV = wx.BoxSizer(wx.VERTICAL)
        siMainV.Add(self.bar)
        siMainV.Add(self.buCancel, flag=wx.CENTER)
        self.SetSizer(siMainV)
        self.Fit()

        self.Bind(wx.EVT_BUTTON, self.onCancel, self.buCancel)

    def updateGauge(self, value, message=""):
        self.bar.SetValue(value)
        if message!="":
            self.SetTitle(message)

    def onCancel(self, e):
        self.SetTitle("Cancelling...")

class MainFrame(wx.Frame):
    def __init__(self, parent, ID, title):
        wx.Frame.__init__(self, parent, ID, title)
        self.bt = wx.Button(self, wx.ID_OK)
        self.Bind(wx.EVT_BUTTON, self.onBt, self.bt)

    def onBt(self, event):
        self.gFr = GaugeFrame(self, title="0 of "+str(MAX_INT), maximum=MAX_INT)
        self.gFr.Show()
        self.gFr.Center()
        self.bt.Disable()
        workThread = threading.Thread(target=self.doWork, args=(TEST_TUPLE,) )
        workThread.start()

    def doWork(self, testArg):
        # time consuming stuff that affects main GUI
        print testArg
        for i in range(1, MAX_INT+1):
            time.sleep(1)
            if self.gFr.GetTitle()=="Cancelling...":
                break
            print str(i)+" of "+str(MAX_INT)
            wx.CallAfter(self.gFr.updateGauge, i, "%i of %i"%(i, MAX_INT))
        wx.CallAfter(self.gFr.Destroy)
        wx.CallAfter(self.bt.Enable)


app = wx.App(False)
fr = MainFrame(None, -1, "Title")
fr.Show()
app.MainLoop()

回答1:


Looks pretty good, just a couple of observations.

  • You should not call ANY window functions on the worker thread. This includes SetPosition and Destroy. You can use wx.CallAfter to invoke these on the main thread just like you are for Update.
  • You probably should allow the user to cancel the processing.


来源:https://stackoverflow.com/questions/20012542/how-can-i-safely-manage-wxpython-progress-dialog-threading

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!