【问题标题】:Displaying Progress with wxPython使用 wxPython 显示进度
【发布时间】:2014-03-26 13:28:14
【问题描述】:

我希望有人能够帮助我显示使用 wxPython 的长期运行任务的进度。

我目前有一个函数,它由一个按钮调用,并执行 5 个其他函数,例如

def sum(self, event):
    self.processSents()
    self.processWords()
    self.repSents()
    self.measSim()
    self.selCont()

我想在执行这些功能时显示一个进度条,因为程序有时会挂起,这并不理想。

我看过的很多解决方案都建议使用线程,但我对 Python 中的线程非常缺乏经验,而且我的尝试也没有让我快速取得进展。例如,我不确定对话框是否应该在线程中、正在执行的代码中,或者两者兼而有之。

我目前的尝试如下:

def sum(self, event):
    progress = wx.ProgressDialog("sum in progress", "please wait", maximum=100, parent=self, style=wx.PD_SMOOTH|wx.PD_AUTO_HIDE)
    self.Start(self.DoWork, progress)
    progress.ShowModal()

def Start(func, *args):
    thread = threading.Thread(target=func, args=args)
    thread.setDaemon(True)
    thread.start()

def DoWork(self, progress):
    for i in range(101):
        ex.CallAfter(progress.Update, i)
        time.sleep(0.2)
    self.processSents()
    self.processWords()
    self.repSents()
    self.measSim()
    self.selCont()
    wx.CallAfter(progress.Destroy)

到目前为止我看到的解决方案是:

Updating a wxPython progress bar after calling app.MainLoop()

How to thread wxPython progress bar

How Can I Safely Manage wxPython Progress Dialog Threading?

http://wiki.wxpython.org/LongRunningTasks

任何帮助或建议将不胜感激,因为我很迷茫:(

谢谢

克里斯

更新到工作版本(根据 Corley 的建议结合了 Jerry 的响应与 wx.Yield() 的组合

def sum(self, event):
    progress = wx.ProgressDialog("sum in progress", "please wait", maximum=100, parent=self, style=wx.PD_SMOOTH|wx.PD_AUTO_HIDE)
    self.processSents()
    percent = 20
    progress.Update(percent)
    self.processWords()
    percent += 20
    progress.Update(percent)

    // repSends, measSim and selCont omitted to save space
    progress.Destroy()

wx.Yield() 从每个函数中调用,例如

def processSents(self):
    // some long running process
    wx.Yield()
    // end of long running process

【问题讨论】:

    标签: python python-2.7 wxpython


    【解决方案1】:

    1) 单击按钮时将创建线程DoWork

    2) 在DoWork 中,将创建另一个线程showProgress 来显示进度对话框

    3)在DoWork,doSomething模拟一些耗时的事情

    4) 在此示例中,将在每个 doSomething 之前创建另一个线程 updateProgress 以避免进度条冻结,但实际上您应该调用 self.progress.Update 来更新进度条,而您的 sum progress

    import wx
    import threading
    import  time
    
    class MyFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self,None)
            panel = wx.Panel(self)
            btn1 = wx.Button(panel, label="test1")
            btn1.Bind(wx.EVT_BUTTON, self.onButton1)
    
            btn2 = wx.Button(panel, label="test2")
            btn2.Bind(wx.EVT_BUTTON, self.onButton2)
    
            sizer = wx.BoxSizer(wx.VERTICAL)
    
            sizer.Add(btn1)
            sizer.Add(btn2)
    
            panel.SetSizer(sizer)
    
    
            self.maxPercent = 100
            self.percent = 0
    
        def onButton1(self, evt):
            self.StartThread(self.DoWork1)
    
        def onButton2(self, evt):
            self.StartThread(self.DoWork2)
    
        def StartThread(self, func, *args):
            thread = threading.Thread(target=func, args=args)
            thread.setDaemon(True)
            thread.start()
    
        def showProgress(self):
            self.progress = wx.ProgressDialog("sum in progress", "please wait", maximum=self.maxPercent, parent=self, style=wx.PD_SMOOTH|wx.PD_AUTO_HIDE)
    
        def destoryProgress(self):
            self.progress.Destroy()
    
        def updateProgress(self, percent):
            keepGoing = True
            time.sleep(1)
            while keepGoing and self.percent < percent:
                self.percent += 1
                (keepGoing, skip) = self.progress.Update(self.percent)
                time.sleep(0.1)
    
    
        def doSomething(self, take_time, taskPercent, say_something):
            time.sleep(take_time)
            (keepGoing, skip) = self.progress.Update(taskPercent, say_something+" done!")
    
    
        def DoWork1(self):
            self.StartThread(self.showProgress)
    
            taskPercent = 25
            self.StartThread(self.updateProgress, taskPercent)
            self.doSomething(5, taskPercent, "1st")
    
            taskPercent +=25
            self.StartThread(self.updateProgress, taskPercent)
            self.doSomething(5, taskPercent, "2nd")
    
            taskPercent +=25
            self.StartThread(self.updateProgress, taskPercent)
            self.doSomething(5, taskPercent, "3rd")
    
            taskPercent +=25
            self.StartThread(self.updateProgress, taskPercent)
            self.doSomething(5, taskPercent, "4th")
    
            self.destoryProgress()
    
        def DoWork2(self):
            self.StartThread(self.showProgress)
    
            taskPercent = 25
            self.doSomething(5, taskPercent, "1st")
    
            taskPercent +=25
            self.doSomething(5, taskPercent, "2nd")
    
            taskPercent +=25
            self.doSomething(5, taskPercent, "3rd")
    
            taskPercent +=25
            self.doSomething(5, taskPercent, "4th")
    
            self.destoryProgress()
    
    
    if __name__ == '__main__':
    
        app = wx.App(0)
        frame = MyFrame()
        frame.Show()
        app.MainLoop()
    

    【讨论】:

    • 感谢杰瑞的回复。我尝试了您的代码(没有更改),但在进度对话框关闭后,程序挂起,我不再能够选择主窗口。我是否正确理解了第 4 点,认为我的代码基本上应该如下所示:def DoWork(self) self.StartThread(self.showProgress) taskPercent=25 self.progress.Update(taskPercent) self.processSents() 即删除 updateProgress 和 doSomething 方法?
    • @chrisrs2292 抱歉,应用程序挂起,因为我忘记破坏对话框。在代码without updateProgress 中添加了另一个案例,您可以检查差异。 self.progress.Update 将在每个 doSomething 的末尾执行
    • 啊,谢谢,应用程序不再挂起,但由于某种原因,progressdialog 现在在代码完成执行之前关闭。我正在用一些更新的代码更新我原来的帖子。
    • @chrisrs2292 请尝试添加一些标志,例如wx.PD_APP_MODAL,我现在无法运行代码,但我认为这是您需要的。
    • 嘿,杰瑞,我尝试使用 wx.PD_APP_MODAL,但在长时间运行的代码完成之前,对话框仍然会自行关闭。所以我遇到的问题是,在 selCont() 函数中,我在文本控件中显示了一些输出。但是 progressDialog 会关闭,然后应用程序在我的测试中只停留 4-5 秒,然后输出实际显示。真的不知道为什么直到 selCont() 完成执行后才调用 destroy 时会发生这种情况=/
    【解决方案2】:

    一个可能更简单的选择是在您的代码中定期调用wx.Yield();这允许 GUI 刷新/处理输入。当然,不会运行其他任何东西,但它确实可以让您的进度条正确更新。

    进度条应该是全局的,或者传递给子函数,以便它可以随时更新进度。

    【讨论】:

    • 嗨 Corley,是否可以从单独的类中调用 wx.Yield()?即我的 GUI 在一个类中,而长时间运行的代码在另一个类中,因此需要从长时间运行的代码中调用 wx.Yield()。这是可行的还是需要从 GUI 类中调用 wx.Yield() ?谢谢
    • wx.Yield() 可以从任何地方调用,并且应该从长时间运行的代码中调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多