Open a progress bar until function task ends - python

I have an event listener on left double click inside a wx.Grid that opens a dialog box to confirm the change. If the user clicks on "Yes" I call a function that takes around 6 seconds to execute, meanwhile the window freezes. What I want to do is to open a progress bar so the user can wait until the function finishes its task, or better, have the progress bar in the dialog box.
I have no idea where to start as I've never needed a progress bar until now.
A lot of the solutions I've looked at suggest threading, but I'm pretty inexperienced with threads in Python.
I'm hoping someone will be able to give me a hand displaying the progress for a running task using wxPython.
Here is my code so far:
def OnCellLeftDClick(self, evt):
if evt.GetCol() == 17:
dlg = wx.MessageDialog(None, "Do you want to change " + self.GetCellValue(evt.GetRow(), 1) + " bid?",'Updater',wx.YES_NO | wx.ICON_QUESTION)
result = dlg.ShowModal()
if result == wx.ID_YES:
from chanbeBid import changeBidTb
changeBidTb(self.GetCellValue(evt.GetRow(), 1), self.GetCellValue(evt.GetRow(), 16))
evt.Skip()
Thank you,

You can use the _thread module to create the new thread and pubsub to handle the communication between the threads. One way to do it is shown below. I added a few comments to the code.
import wx
import time
import _thread
from pubsub import pub
seconds = 10
class MyFrame(wx.Frame):
def __init__(self):
"""
Just a button to start counting
"""
super().__init__(None, title='Counting until...')
self.panel = wx.Panel(self)
self.button = wx.Button(self.panel, label='Count', pos=(50, 50))
self.button.Bind(wx.EVT_BUTTON, self.OnCount)
## This will subscribe the window to the message 'Finish counting'.
## Thus, everytime the message is broadcast the self.Pass method
## will be executed.
pub.subscribe(self.Pass, 'Finish counting')
def OnCount(self, event):
dlg = wx.MessageDialog(None, "Do you want to count?",
style=wx.YES_NO|wx.ICON_QUESTION)
if dlg.ShowModal() == wx.ID_YES:
self.count()
else:
pass
def count(self):
## Creates and starts a new thread that will execute the self.wait
## method. Notice that the thread is started before the ProgressDialog
## because the ProgressDialog will keep the main thread busy
_thread.start_new_thread(self.wait, (seconds,))
## ProgressDialog that will keep running until maxV is reached or
## until self.keepGoing is set to False
maxV = 100
dlg = wx.ProgressDialog("Progress dialog example",
"An informative message",
maximum = maxV,
parent=self,
style = 0
| wx.PD_APP_MODAL
#| wx.PD_CAN_ABORT
#| wx.PD_CAN_SKIP
| wx.PD_ELAPSED_TIME
#| wx.PD_ESTIMATED_TIME
#| wx.PD_REMAINING_TIME
#| wx.PD_AUTO_HIDE
)
self.keepGoing = True
count = 0
while self.keepGoing and count < maxV:
count += 1
wx.MilliSleep(250)
wx.SafeYield()
(keepGoing, skip) = dlg.Update(count)
dlg.Destroy()
def wait(self, secs):
## This is the function that is executed by the new thread
## when it finishs the wx.CallAfter method broadcast the message
## 'Finish counting' that triggers self.Pass as mentioned above.
time.sleep(secs)
wx.CallAfter(pub.sendMessage, 'Finish counting')
def Pass(self):
## Changes self.keepGoing to false so the ProgressDialog is destroyed.
self.keepGoing = False
if __name__ == "__main__":
app = wx.App()
frame = MyFrame()
frame.Show()
app.MainLoop()
Regarding having the progress bar in the dialog window asking to proceed. This can be done but you need to build your custom dialog window and probably use wx.Gauge instead of wx.ProgressDialog. Also most probably you will want this custom window to be modal (freeze all other windows in the program so the user has to wait for the function running in the thread to finish). For this add the following method to the custom dialog class,
def ShowModal(self):
"""
wx.Dialog behavior
"""
self._disabler = wx.WindowDisabler(self)
self.Show()
self.eventLoop = wx.GUIEventLoop()
self.eventLoop.Run()
and show the custom dialog in the same way as normal wx.Dialog, custom_dialog.ShowModal()

Related

Change Stacked Widget index automatically after 3 seconds in PyQt5

I am trying to display the first page of the stacked widget ( the welcome page ) and after 3 seconds replace it by the second page ( the Menu page ) automatically.
I tried this approach but it doesn't work..
..........
self.stackedWidget.setCurrentIndex(0)
time.sleep(3)
self.stackedWidget.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(smartUpdaterUI)
..............
Qt uses a GUI event loop to handle events, and update the UI. Any updates to the GUI won't be visible until control is handed over to the event loop and that update is picked up and processed.
The code you write in Python happens in the same thread as the GUI. So while your code is running, the event loop is not, and changes are not being processed.
Side note: this is why your application can 'hang' if you try and do something long-running like accessing an API, without using a separate thread.
In your code, you're setting the current index to 0, then using Python time.sleep() to wait, before updating to index 1.
self.stackedWidget.setCurrentIndex(0)
time.sleep(3) # no event loop running here
self.stackedWidget.setCurrentIndex(1)
While the time.sleep(3) is happening, the execution is held at this point. This means control is not handed back to the Qt event loop, and the first change is not processed. Once the timeout completes, the second index is set, and only then your function returns control back to Qt.
The event loop now applies both changes, but immediately one after another. So all you see is index being set to 1, without first showing 0 and without any delay.
To avoid this, you need to return control back to the event loop after setting the initial index. The simplest way to do this is to just set it, and then trigger the subsequent update using an asynchronous QTimer.
self.stackedWidget.setCurrentIndex(0)
QTimer.singleShot(3000, lambda: self.stackedWidget.setCurrentIndex(1))
The lambda: is used as an anonymous function, so we can pass 1 to setCurrentIndex by delaying execution until the timer is triggered. If you're only ever jumping to a single page you could do this instead:
def go_to_page_1(self):
self.stackedWidget.setCurrentIndex(1)
self.stackedWidget.setCurrentIndex(0)
QTimer.singleShot(3000, go_to_page_1)
void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
This static function calls a slot after a given time interval.
Try it:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class stackedExample(QWidget):
def __init__(self):
super(stackedExample, self).__init__()
self.leftlist = QListWidget()
self.leftlist.insertItem (0, 'Contact' )
self.leftlist.insertItem (1, 'Personal' )
self.leftlist.insertItem (2, 'Educational' )
self.stack1 = QWidget()
self.stack2 = QWidget()
self.stack3 = QWidget()
self.stack1UI()
self.stack2UI()
self.stack3UI()
self.stackWidget = QStackedWidget(self)
self.stackWidget.addWidget(self.stack1)
self.stackWidget.addWidget(self.stack2)
self.stackWidget.addWidget(self.stack3)
hbox = QHBoxLayout(self)
hbox.addWidget(self.leftlist)
hbox.addWidget(self.stackWidget)
self.setLayout(hbox)
self.leftlist.currentRowChanged.connect(self.display)
self.setGeometry(300, 50, 10, 10)
self.setWindowTitle('StackedWidget demo')
self.stackWidget.setCurrentIndex(0)
QTimer.singleShot(3000, lambda: self.display(1))
QTimer.singleShot(6000, lambda: self.display(2))
QTimer.singleShot(9000, lambda: self.display(0))
self.show()
def stack1UI(self):
layout = QFormLayout()
layout.addRow("Name", QLineEdit())
layout.addRow("Address", QLineEdit())
#self.setTabText(0,"Contact Details")
self.stack1.setLayout(layout)
def stack2UI(self):
layout = QFormLayout()
sex = QHBoxLayout()
sex.addWidget(QRadioButton("Male"))
sex.addWidget(QRadioButton("Female"))
layout.addRow(QLabel("Sex"),sex)
layout.addRow("Date of Birth",QLineEdit())
self.stack2.setLayout(layout)
def stack3UI(self):
layout = QHBoxLayout()
layout.addWidget(QLabel("subjects"))
layout.addWidget(QCheckBox("Physics"))
layout.addWidget(QCheckBox("Maths"))
self.stack3.setLayout(layout)
def display(self, i):
self.stackWidget.setCurrentIndex(i)
def main():
app = QApplication(sys.argv)
ex = stackedExample()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

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.

placing values on TextCtrl in subthread cause doesn't always work and cause random segmentation faults

I'm trying to create a gui application using wxpython and I got some issues with the TextCtrl element. The effect that I am trying to achieve is that the user will enter a command to a text field (command) and the command might pop a message that appears in the `(out) field. After some time (0.7 seconds on this example) the message will get back to a default message ("OutPut"). I have two problems:
The message doesn't always appear.
The program sometime crashes due to a segmentation fault and I don't get any Error message to handle that.
I guess that the two related in some way, but I don't know why. In the following example, I only type "test" and wait until the original message appear. Both problems happen on that scenario.
I post here two files that serve as smallest working example. File number 1, creates the GUI,
import wx
import os.path
import os
from threading import Thread
from time import sleep
from MsgEvent import *
class MainWindow(wx.Frame):
def __init__(self):
super(MainWindow, self).__init__(None, size=(400,200),)
#style=wx.MAXIMIZE)
self.CreateInteriorWindowComponents()
self.CreateKeyBinding()
self.command.SetFocus()
self.Layout()
def Test(self):
self.command.SetValue('open')
self.ParseCommand(None)
def PostMessage(self,msg):
'''For its some reason, this function is called twice,
the second time without any input. I could'nt understand why.
For that, the test :if msg == None'''
if msg == None: return
worker = MessageThread(self,msg,0.7,'OutPut')
worker.start()
def CreateKeyBinding(self):
self.command.Bind(wx.EVT_CHAR,self.KeyPressed)
def KeyPressed(self,event):
char = event.GetUniChar()
if char == 13 and not event.ControlDown(): #Enter
if wx.Window.FindFocus() == self.command:
self.ParseCommand(event)
else:
event.Skip()
def ParseCommand(self,event):
com = self.command.GetValue().lower() #The input in the command field
self.PostMessage(com)
def CreateInteriorWindowComponents(self):
''' Create "interior" window components. In this case it is just a
simple multiline text control. '''
self.panel = wx.Panel(self)
font = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
font.SetPointSize(12)
self.vbox = wx.BoxSizer(wx.VERTICAL)
#Out put field
self.outBox = wx.BoxSizer(wx.HORIZONTAL)
self.out = wx.TextCtrl(self.panel, style=wx.TE_READONLY|wx.BORDER_NONE)
self.out.SetValue('OutPut')
self.out.SetFont(font)
self.outBox.Add(self.out,proportion=1,flag=wx.EXPAND,border=0)
self.vbox.Add(self.outBox,proportion=0,flag=wx.LEFT|wx.RIGHT|wx.EXPAND,border=0)
#Setting the backgroudn colour to window colour
self.out.SetBackgroundColour(self.GetBackgroundColour())
#Commands field
self.commandBox = wx.BoxSizer(wx.HORIZONTAL)
self.command = wx.TextCtrl(self.panel, style=wx.TE_PROCESS_ENTER)
self.command.SetFont(font)
self.commandBox.Add(self.command, proportion=1, flag=wx.EXPAND)
self.vbox.Add(self.commandBox, proportion=0, flag=wx.LEFT|wx.RIGHT|wx.EXPAND, border=0)
self.panel.SetSizer(self.vbox)
return
#Close the window
def OnExit(self, event):
self.Close() # Close the main window.
app = wx.App()
frame = MainWindow()
frame.Center()
frame.Show()
app.MainLoop()
And file number 2, called MsgThread.py handle the events.
import wx
import threading
import time
myEVT_MSG = wx.NewEventType()
EVT_MSG = wx.PyEventBinder(myEVT_MSG,1)
class MsgEvent(wx.PyCommandEvent):
""" event to signal that a message is ready """
def __init__(self,etype,eid,msg='',wait=0,msg0=''):
""" create the event object """
wx.PyCommandEvent.__init__(self,etype,eid)
self._msg = unicode(msg)
self._wait_time = wait
self._reset_message = unicode(msg0)
def GetValue(self):
""" return the value from the event """
return self._msg
class MessageThread(threading.Thread):
def __init__(self,parent,msg='',wait=0,msg0=''):
"""
parent - The gui object that shuold recive the value
value - value to handle
"""
threading.Thread.__init__(self)
if type(msg) == int:
msg = unicode(msg)
self._msg = msg
self._wait_time = wait
self._reset_message = msg0
self._parent = parent
print self._msg
def run(self):
""" overide thread.run Don't call this directly, its called internally when you call Thread.start()"""
self._parent.out.SetValue(unicode(self._msg))
time.sleep(self._wait_time)
self._parent.out.SetValue(self._reset_message)
self._parent.MessageFlag = False
event = MsgEvent(myEVT_MSG,-1,self._msg)
wx.PostEvent(self._parent,event)
What is faulty?
WX Python is not thread safe except for 3 functions (wx.CallAfter, wx.CallLater, wx.PostEvent)
So basically what you have to do is ensure you never call any widget directly from within the subthread. You may use events or CallAfter.
You also might want to check this:
Nice writeup about threads and wxpython
This might help as well
Lokla

wxpython EVT_KEY_DOWN not binding to the Frame

Disclaimer: Perhaps I've gone about this incorrectly, but Im only about 24 hours into GUI development with wxpython. So general advice is appreciated.
Goal: I'm trying to familiarize myself with wxpython by making a simple screen saver.
Problem: Im trying to bind both mouse movement, and keyboard movement (aka trying to capture ANY user input) so it Destroys() the app on event (typical screensaver behavior). So far, my mouse events are being captured and it destroys properly, but my keyboard events are not (pound on my keyboard as I might!). I've tried EVT_CHAR and CHAR_HOOK as other SO posts have recommended. I've even go as far to see where my focus is - as I think that this is my problem (notice the line has self.SetFocus() - if you remove this, and move the mouse, the returned "self.FindFocus()" thats triggered by mouse movement events is returning None... with the SetFocus() it is now returning my class of SpaceFrame).
Question: Why can't I capture key strokes and activate my keyboardMovement() method? Interestingly enough, the example from here works just fine for the keyboard events down/up. So I'm 100% it's user error.
import wx
import random
MAX_INVADERS = 10
INVADERS_COLORS = ["yellow_invader",
"green_invader",
"blue_invader",
"red_invader"]
class SpaceFrame(wx.Frame):
def __init__(self):
"""
The generic subclassed Frame/"space" window. All of the invaders fall
into this frame. All animation happens here as the parent window
as well.
"""
wx.Frame.__init__(self, None, wx.ID_ANY, "Space Invaders", pos=(0, 0))
self.SetFocus()
self.Bind(wx.EVT_MOTION, self.mouseMovement)
self.Bind(wx.EVT_CHAR_HOOK, self.keyboardMovement)
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour('black')
self.SetBackgroundColour('black')
self.monitorSize = wx.GetDisplaySize()
for invader in range(0, MAX_INVADERS, 1):
randX = random.randint(0, self.monitorSize[0])
self.showInvader(coords=(randX, 0),
invader=random.choice(INVADERS_COLORS),
scale=(random.randint(2, 10)/100.0))
def mouseMovement(self, event, *args):
print self.FindFocus()
def keyboardMovement(self, event, *args):
self.Destroy()
def showInvader(self, coords=(0, 0), invader="green_invader", scale=.05):
"""
Displays an invader on the screen
"""
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.dropInvader, self.timer)
self.timer.Start(1000)
self.invader = wx.Bitmap("{0}.png".format(invader))
self.invader = wx.ImageFromBitmap(self.invader)
self.invader = self.invader.Scale((self.invader.GetWidth()*scale),
(self.invader.GetHeight()*scale),
wx.IMAGE_QUALITY_HIGH)
self.result = wx.BitmapFromImage(self.invader)
self.control = wx.StaticBitmap(self, -1, self.result)
self.control.SetPosition(coords)
self.panel.Show(True)
def moveInvader(self, coords):
self.control.SetPosition(coords)
def dropInvader(self, *args):
# print "this hit"
self.control.SetPosition((100, 600))
if __name__ == "__main__":
application = wx.PySimpleApp()
window = SpaceFrame()
window.ShowFullScreen(True, style=wx.FULLSCREEN_ALL)
application.MainLoop()
Research Done So Far: Maybe I missed something, but nothing stood out to me here.
Source1 - Mouse Vs Python examples
Source2 - SO similar problem (but not exactly my solution)
Source3 - WXpython forum/mailing list
Source4 - WXpython forum/mailing list

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