Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Displaying Progress with wxPython

I'm hoping someone will be able to give me a hand displaying the progress for a long running task using wxPython.

I currently have a function, which is invoked by a button, and executes 5 other functions e.g.

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

I would like to display a progress bar while these functions are being executed, as the program sometimes hangs which isn't ideal.

A lot of the solutions I've looked at suggest threading, but I'm pretty inexperienced with threads in Python, and my attempts are getting me nowhere fast. I'm not sure for example, if the dialog should be in the thread, the executing code, or both.

My current attempt is as follows:

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)

The solutions I've looked at so far are:

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

Any help or advice would be appreciated as I'm pretty lost :(

Thanks

Chris

Updated to working version (combination of Jerry's response combined with wx.Yield() as recommended by Corley

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() is called from within each function e.g.

def processSents(self):
    // some long running process
    wx.Yield()
    // end of long running process
like image 284
Chrisrs2292 Avatar asked Jun 09 '26 06:06

Chrisrs2292


2 Answers

1) Thread DoWork would be created when you click the button.

2) In DoWork, another thread showProgress would be created to display the progress dialog

3)In DoWork, doSomething simulate some time-consuming thing

4) Aonther thread updateProgress would be created before each doSomething in this sample to avoid the progress bar freeze, but actually you should call self.progress.Update to update the progress bar while your 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()
like image 144
Jerry_Y Avatar answered Jun 11 '26 20:06

Jerry_Y


A possibly simpler option is just to call wx.Yield() periodically in your code; this allows the GUI to refresh/process inputs. Of course, nothing else will run, but it does allow your progress bar to update properly.

The progress bar should probably be a global, or passed to the subfunctions, so that it can update the progress as it goes.

like image 36
Corley Brigman Avatar answered Jun 11 '26 21:06

Corley Brigman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!