Displaying Progress with wxPython - python

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

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()

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.

Related

Call method to the main thread 'from a sub thread'

I am making a data acquisition program that communicates with a measurement device. The status of the device needs to be checked periodically (e.g., every 0.1 sec) to see if acquisition is done. Furthermore, the program must have the 'abort' method because acquisition sometime takes longer than few minutes. Thus I need to use multi-threading.
I attached the flow-chart and the example code. But I have no idea how I call the main-thread to execute a method from the sub-thread.
python 3.7.2
wxpython 4.0.6
Flow Chart
import wx
import time
from threading import Thread
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="Test Frame")
panel = wx.Panel(self)
self.Btn1 = wx.Button(panel, label="Start Measurement")
self.Btn1.Bind(wx.EVT_BUTTON, self.OnStart)
self.Btn2 = wx.Button(panel, label="Abort Measurement")
self.Btn2.Bind(wx.EVT_BUTTON, self.OnAbort)
self.Btn2.Enable(False)
self.DoneFlag = False
self.SubThread = Thread(target=self.Check, daemon=True)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.Btn1, 0, wx.EXPAND)
sizer.Add(self.Btn2, 0, wx.EXPAND)
panel.SetSizer(sizer)
def OnStart(self, event):
# self.N is the number of data points
self.N = 0
# self.N_max is the number of data points that is going to be acquired
self.N_max = int(input("How many data points do yo want? (greater than 1) : "))
self.DoneFlag = False
self.Btn1.Enable(False)
self.Btn2.Enable(True)
self.Start()
def OnAbort(self, event):
self.DoneFlag = True
def Start(self):
self.SubThread.start()
def Done(self):
if self.DoneFlag is True:
self.Finish()
elif self.DoneFlag is False:
self.Start()
def Finish(self):
print("Measurement done (N = {})\n".format(self.N))
self.Btn1.Enable(True)
self.Btn2.Enable(False)
def Check(self):
# In the actual program, this method communicates with a data acquisition device to check its status
# For example,
# "RunningStatus" is True when the device is still running (acquisition has not been done yet),
# is False when the device is in idle state (acquisition has done)
#
# [Structure of the actual program]
# while True:
# RunningStatus = GetStatusFromDevice()
# if RunningStatus is False or self.DoneFlag is True:
# break
# else:
# time.sleep(0.1)
# In below code, it just waits 3 seconds then assumes the acqusition is done
t = time.time()
time.sleep(1)
for i in range(3):
if self.DoneFlag is True:
break
print("{} sec left".format(int(5-time.time()+t)))
time.sleep(1)
# Proceed to the next steps after the acquisition is done.
if self.DoneFlag is False:
self.N += 1
print("Data acquired (N = {})\n".format(self.N))
if self.N == self.N_max:
self.DoneFlag = True
self.Done() # This method should be excuted in the main thread
if __name__ == "__main__":
app = wx.App()
frame = TestFrame()
frame.Show()
app.MainLoop()
When using a GUI it is not recommended to call GUI functions from another thread, see:
https://docs.wxwidgets.org/trunk/overview_thread.html
One of your options, is to use events to keep track of what is going on.
One function creates and dispatches an event when something happens or to denote progress for example, whilst another function listens for and reacts to a specific event.
So, just like pubsub but native.
Here, I use one event to post information about progress and another for results but with different targets.
It certainly will not be an exact fit for your scenario but should give enough information to craft a solution of your own.
import time
import wx
from threading import Thread
import wx.lib.newevent
progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
results_event, EVT_RESULTS_EVENT = wx.lib.newevent.NewEvent()
class ThreadFrame(wx.Frame):
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
panel = wx.Panel(self)
self.parent = parent
self.btn = wx.Button(panel,label='Stop Measurements', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.progress = wx.Gauge(panel,size=(240,10), pos=(10,50), range=30)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Show()
self.mythread = TestThread(self)
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
if event.result != 0:
evt = results_event(result=event.result)
#Send back result to main frame
try:
wx.PostEvent(self.parent, evt)
except:
pass
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
self.mythread.join() # Wait for it to finish
self.Destroy()
class TestThread(Thread):
def __init__(self,parent_target):
Thread.__init__(self)
self.parent = parent_target
self.stopthread = False
self.start() # start the thread
def run(self):
curr_loop = 0
while self.stopthread == False:
curr_loop += 1
# Send a result every 3 seconds for test purposes
if curr_loop < 30:
time.sleep(0.1)
evt = progress_event(count=curr_loop,result=0)
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
else:
curr_loop = 0
evt = progress_event(count=curr_loop,result=time.time())
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
def terminate(self):
evt = progress_event(count=0,result="Measurements Ended")
try:
wx.PostEvent(self.parent, evt)
except:
pass
self.stopthread = True
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.text_count = 0
self.thread_count = 0
self.parent=parent
btn = wx.Button(self, wx.ID_ANY, label='Start Measurements', size=(200,30), pos=(10,10))
btn.Bind(wx.EVT_BUTTON, self.Thread_Frame)
btn2 = wx.Button(self, wx.ID_ANY, label='Is the GUI still active?', size=(200,30), pos=(10,50))
btn2.Bind(wx.EVT_BUTTON, self.AddText)
self.txt = wx.TextCtrl(self, wx.ID_ANY, style= wx.TE_MULTILINE, pos=(10,90),size=(400,100))
#Bind to the result event issued by the thread
self.Bind(EVT_RESULTS_EVENT, self.OnResult)
def Thread_Frame(self, event):
self.thread_count += 1
frame = ThreadFrame(title='Measurement Task '+str(self.thread_count), parent=self)
def AddText(self,event):
self.text_count += 1
txt = "Gui is still active " + str(self.text_count)+"\n"
self.txt.write(txt)
def OnResult(self,event):
txt = "Result received " + str(event.result)+"\n"
self.txt.write(txt)
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame', size=(600,400))
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()

Python: Does PubSub and WxPython with Threading require wx.CallAfter?

I am using:
wxPython 4.0.7.post2
Pypubsub 4.0.3
Python 3.8.1
I have the following example program I have written:
import wx
import time
from threading import Thread
from pubsub import pub
TIME_UPDATED = "time.updated"
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="Example")
self.text = wx.StaticText(self, label="I will display seconds elapsed!")
self.othertext = wx.StaticText(self, label="I will Update")
sizer = wx.BoxSizer(orient=wx.VERTICAL)
sizer.Add(self.text)
sizer.Add(self.othertext)
self.SetSizer(sizer)
self.timer = wx.Timer(self)
pub.subscribe(self.UpdateTime, TIME_UPDATED)
self.Bind(wx.EVT_TIMER, self.OnTime, self.timer)
self.Show()
self.i = 0
self.timer.Start(500)
def OnTime(self, _):
self.i += 1
self.othertext.SetLabel(str(self.i))
def UpdateTime(self, seconds):
self.text.SetLabel("{seconds} seconds have elapsed".format(seconds=seconds))
self.text.Refresh()
class BackgroundThread(Thread):
def run(self):
time_elapsed = 0
while True:
# Lets sleep 1 second
time.sleep(1)
time_elapsed += 1
# <<<<---- This line is what I am worried about.
pub.sendMessage(TIME_UPDATED, seconds=time_elapsed)
if __name__ == '__main__':
app = wx.App()
frame = MyFrame()
background = BackgroundThread(daemon=True)
background.start()
app.MainLoop()
I am performing a pub.sendMessage(TIME_UPDATED, seconds=time_elapsed) without a wx.CallAfter and it seems to be working fine. I am not sure why.
Could someone please explain if wx.CallAfter is necessary anymore?
If it is can you explain why that is? Is it that some wx methods put something onto the dispatch queue whereas others do not?
Yes, you should still ensure that UI operations occur on the UI thread. Just because something is not safe to do does not mean that it doesn't happen to work okay (or apprear to work okay) in some cases.

python script runs after x seconds but when tkinter code inserted it runs only once

What is the best way to repeatedly execute a function every x seconds in Python?
i have tried the solutions posted on above links but none helped me to achieve the desired result.
the code above prints "Doing stuff..." on console many times as per seconds mentioned i.e. 5 but when i add the line of window() which is a tkinter code for displaying a message the code runs just once and not anytime again .
please help . i want to run the tkinter code again and again on specific time as per system clock but now i am just trying to execute it after x amounts of seconds .
any help would really mean a lot to me.Thanks
search for tkinter .after method.
This will alow you to run a command every x seconds.
The problem your tkinter code runs only once, is since its set up first and then goes into a loop, (root.mainloop()) , hence never returning to your code to display anything again.
Example : tkinter: how to use after method
I think you need thread and queue...let me show a little demo.
I've set time.sleep(1) per seconds in the thead class.
In this matter you get two advantages, first repet your funcion any times
you desire and second your program never freeze it self.
import tkinter as tk
import threading
import queue
import datetime
import time
class MyThread(threading.Thread):
def __init__(self, queue,):
threading.Thread.__init__(self)
self.queue = queue
self.check = True
def stop(self):
self.check = False
def run(self):
while self.check:
x = "Doing stuff.. "
y = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = x+y
time.sleep(1)
self.queue.put(msg)
class App(tk.Frame):
def __init__(self,):
super().__init__()
self.master.title("Hello World")
self.master.protocol("WM_DELETE_WINDOW",self.on_close)
self.queue = queue.Queue()
self.my_thread = None
self.init_ui()
def init_ui(self):
self.f = tk.Frame()
w = tk.Frame()
tk.Button(w, text="Start", command=self.launch_thread).pack()
tk.Button(w, text="Stop", command=self.stop_thread).pack()
tk.Button(w, text="Close", command=self.on_close).pack()
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
self.f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
def launch_thread(self):
if (threading.active_count()!=0):
self.my_thread = MyThread(self.queue)
self.my_thread.start()
self.periodiccall()
def stop_thread(self):
if(threading.active_count()!=1):
self.my_thread.stop()
def periodiccall(self):
self.checkqueue()
if self.my_thread.is_alive():
self.after(1, self.periodiccall)
else:
pass
def checkqueue(self):
while self.queue.qsize():
try:
ret = self.queue.get(0)
msg = "%s"%(ret)
print(msg)
except queue.Empty:
pass
def on_close(self):
if(threading.active_count()!=1):
self.my_thread.stop()
self.master.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Python,Gtk3: How to make the Progress bar pulsing while other stuffs are running

Based on Classes, i have window which contain a button and progressbar, whenever the button is clicked there two things should happen :
1 - should entried value from dialog pass to class ABCD
2 - While our class ABCD() do his stuff, should our progressbar do regular pulsing untill the class ABCD() finish process.
So the problem is that the progressbar pulse only one time,then stucked there till the class ABCD() finished, then its start pulsing regulary later.
Here is my try:
import gi,time
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject
class DialogExample(Gtk.Dialog):
def __init__(self, parent):
Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK, Gtk.ResponseType.OK))
self.set_default_size(150, 100)
self.Myinput = Gtk.Entry()
box = self.get_content_area()
box.add(self.Myinput)
self.show_all()
class DialogWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Dialog Example")
self.set_border_width(6)
Hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.add(Hbox)
self.button = Gtk.Button("Open dialog")
self.button.connect("clicked", self.on_button_clicked)
Hbox.pack_start(self.button, True, True, 0)
self.progressbar = Gtk.ProgressBar()
Hbox.pack_start(self.progressbar, True, True, 0)
#~~~~~~ Progress Bar
def on_timeout(self, user_data):
"""
Update value on the progress bar
"""
if self.activity_mode:
self.progressbar.pulse()
else:
new_value = self.progressbar.get_fraction() + 0.01
if new_value > 1:
new_value = 0
self.progressbar.set_fraction(new_value)
# As this is a timeout function, return True so that it
# continues to get called
return True
def on_button_clicked(self, widget):
dialog = DialogExample(self)
response = dialog.run()
if response == Gtk.ResponseType.OK:
variable = dialog.Myinput.get_text()
print("start")
dialog.destroy()
#ProgressBar time function
self.timeout_id = GObject.timeout_add(50, self.on_timeout, None)
self.activity_mode = False
self.progressbar.pulse()
#this for Updating the Windows and make the progressbar pulsing while waiting
# the class ABCD finish his stuff, finally should stop pulsing.
while Gtk.events_pending():
Gtk.main_iteration_do(False)
passing_instance = ABCD(variable)
class ABCD(object):
def __init__(self,value_of_dialog):
self.get_value = value_of_dialog
self.for_add = "______ add was done"
self.final_value = self.get_value+self.for_add
time.sleep(10)
print("gonna be finished")
print(self.final_value)
win = DialogWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
As we can see here i already try to make pulse and refresh the windows in this part of code
self.timeout_id = GObject.timeout_add(50, self.on_timeout, None)
self.activity_mode = False
self.progressbar.pulse()
#this for Updating the Windows and make the progressbar pulsing while waiting
# the class ABCD finish his stuff, finally should stop pulsing.
while Gtk.events_pending():
Gtk.main_iteration_do(False)
Otherwise because in my class ABCD() i have time.sleep(10) should
the progress bar pulse only for that time 10 seconds later only then
stop.
How should this code gonna be, i need someone provide me the correct code, with little explain.
The issue with using sleep in order to emulate the passing of time is that sleep will stop everything that is happening in the thread which in this case prevents the thread to reach Gtk.main() which is needed to make your progressbar pulse or update.
So in order to do this properly there are 2 options:
Run ABCD in a separate thread such that the main thread can reach Gtk.main(). Which than will make sure that the progressbar moves as expected. A quick example of this looks like this:
self.abcd_thread = ABCD(variable)
self.abcd_thread.start()
class ABCD(Thread):
def __init__(self, value_of_dialog):
super(ABCD, self).__init__()
self.get_value = value_of_dialog
self.for_add = "______ add was done"
self.final_value = self.get_value+self.for_add
def run(self):
print "Starting " + self.name
time.sleep(10)
print("gonna be finished")
print(self.final_value)
print "Exiting " + self.name
When using this you can use self.abcd_thread.isAlive() to see whether the thread is still computing things. The way to return information heavily depends on the job placed in the thread.
Replace the time.sleep with the following fragment:
now = time.time()
while time.time() - now < 10:
# Insert any code here
Gtk.main_iteration_do(False)
This will still emulate ABCD doing stuff for 10 seconds but because we call Gtk.main_iteration_do(False) in each iteration of the loop GTK is able to update the interface during the loop.
In general the second option is the easiest as it only involves making Gtk.main_iteration_do(False) call during whatever your doing. The first option on the other hand is more useful when dealing with complex computations where adding Gtk calls doesn't fit in easily.

wxpython - Running threads sequentially without blocking GUI

I've got a GUI script with all my wxPython code in it, and a separate testSequences module that has a bunch of tasks that I run based on input from the GUI. The tasks take a long time to complete (from 20 seconds to 3 minutes), so I want to thread them, otherwise the GUI locks up while they're running. I also need them to run one after another, since they all use the same hardware. (My rationale behind threading is simply to prevent the GUI from locking up.) I'd like to have a "Running" message (with varying number of periods after it, i.e. "Running", "Running.", "Running..", etc.) so the user knows that progress is occurring, even though it isn't visible. I'd like this script to run the test sequences in separate threads, but sequentially, so that the second thread won't be created and run until the first is complete. Since this is kind of the opposite of the purpose of threads, I can't really find any information on how to do this... Any help would be greatly appreciated.
Thanks in advance!
gui.py
import testSequences
from threading import Thread
#wxPython code for setting everything up here...
for j in range(5):
testThread = Thread(target=testSequences.test1)
testThread.start()
while testThread.isAlive():
#wait until the previous thread is complete
time.sleep(0.5)
i = (i+1) % 4
self.status.SetStatusText("Running"+'.'*i)
testSequences.py
import time
def test1():
for i in range(10):
print i
time.sleep(1)
(Obviously this isn't the actual test code, but the idea is the same.)
You cannot wait in the GUI-thread with a while loop because you block the processing of the event-queue. One solution is to poll the state of the thread with a timer:
import wx
import time
from threading import Thread
def test1():
for i in range(10):
print i
time.sleep(1)
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Test")
panel = wx.Panel(self, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
panel.SetSizer(sizer)
self.button = wx.Button(panel, 0, "Start")
sizer.Add(self.button, 0, wx.ALIGN_LEFT)
self.button.Bind(wx.EVT_BUTTON, self.OnButton)
self.text = wx.StaticText(panel, 0, "No test is running")
sizer.Add(self.text, 0, wx.ALIGN_LEFT)
self.timer = wx.Timer(self)
def OnButton(self, event):
self.testThread = Thread(target=test1)
self.testThread.start()
self.text.SetLabel("Running")
self.button.Disable()
self.Bind(wx.EVT_TIMER, self.PollThread)
self.timer.Start(20, oneShot=True)
event.Skip()
def PollThread(self, event):
if self.testThread.isAlive():
self.Bind(wx.EVT_TIMER, self.PollThread)
self.timer.Start(200, oneShot=True)
self.text.SetLabel(self.text.GetLabel() + ".")
else:
self.button.Enable()
self.text.SetLabel("Test completed")
app = wx.PySimpleApp()
TestFrame().Show()
app.MainLoop()
Figured out a way to do this. Instead of creating threads in my gui.py, I created a class that inherits from Thread, and runs all the tests in that class, then posts wxPython events when one test is done (so I can update the status bar) and when all tests are done (so I can inform the user that all tests are complete.
myEVT_TESTDONE = wx.NewEventType()
EVT_TESTDONE = wx.PyEventBinder(myEVT_TESTDONE , 1)
myEVT_ALLDONE = wx.NewEventType()
EVT_ALLDONE = wx.PyEventBinder(myEVT_ALLDONE, 1)
class TestDone(wx.PyCommandEvent):
def __init__(self, etype, eid, val=None):
wx.PyCommandEvent.__init__(self, etype, eid)
self._val = val
def GetValue(self):
return self._val
class AllDone(wx.PyCommandEvent):
def __init__(self, etype, eid):
wx.PyCommandEvent.__init__(self, etype, eid)
class TestSequence(Thread):
def __init__(self, parent, queue):
Thread.__init__(self)
self._queue = queue
self._parent = parent
self.start()
def run(self):
testCount = 0
for test in self._queue:
#Time-intensive task goes here
for i in range(10):
print i
sleep(1)
evt = TestDone(myEVT_TESTDONE, -1, i)
wx.PostEvent(self._parent, evt)
evt = AllDone(myEVT_ALLDONE, -1)
wx.PostEvent(self._parent, evt)
class MainSequence(wx.Frame):
def __init__(self, parent, id, title):
self.Bind(EVT_TESTDONE, self.testDoneEvt)
self.Bind(EVT_ALLDONE, self.allDoneEvt)
#...the rest of the wxPython code
def testDoneEvt(self, event):
#Set what to be done after every test, e.g. update progress bar
step = event.GetValue()
def allDoneEvt(self, event):
#Set what to be done after all tests, e.g. display "Tests complete"
program = wx.App()
window = MainSequence(None, -1, 'App title')
program.MainLoop()

Categories

Resources