Threads, wxpython and statusbar - python

I'm doing a program in which I'm using a wxStatusBar, when a download starts I start a child thread like this:
def OnDownload(self, event):
child = threading.Thread(target=self.Download)
child.setDaemon(True)
child.start()
Download is another function without parameters (except self). I would like to update my statusbar from there with some information about the downloading progress, but when I try to do so I often get Xwindow, glib and segfaults errors. Any idea to solve this?
Solved: I just needed to include wx.MutexGuiEnter() before changing something in the GUI inside the thread and wx.MutexGuiLeave() when finished. For example
def Download(self):
#stuff that doesn't affect the GUI
wx.MutexGuiEnter()
self.SetStatusText("This is a thread")
wx.MutexGuiLeave()
And that's all :D

Most people get directed to the wxPython wiki:
http://wiki.wxpython.org/LongRunningTasks
I also wrote up a little piece on the subject here:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
I don't think I've ever seen your solution before though.

How are you updating the status bar?
I think you should be fine if you create a custom event, and then post it via wx.PostEvent to notify the frame/status bar in the GUI thread.
For download progress in a status bar, you might want your event to look something like this:
DownloadProgressEvent, EVT_DL_PROGRESS = wx.lib.newevent.NewEvent()
# from the thread...
event = DownloadProgressEvent(current=100, total=1000, filename="foo.jpg")
wx.PostEvent(frame, event)
# from the frame:
def OnDownloadProgress(self, event):
self.statusbar.update_dl_msg(event.current, event.total, event.filename)
Here's some more detail from the wxPython wiki.

Related

Calling a class method in Python/PySide

I spent longer than I'd care to admit think of a suitable 'question' heading for this topic, as my issue is somewhat hard to articulate.
Here is a quick summary of the situation:
I'm writing a basic GUI with Python 3.4 and PySide
I'm using QFileSystemWatcher to monitor a particular file
When the file is changed, QFileSystemWatcher calls a method, which in turn calls a method within a PySide Class
All of the above seems to be working perfectly, except the GUI-specific actions detailed in the PySide Class method aren't being executed (I'll explain in more detail below).
Example code:
#Establishing the PySide GUI Class
class GUI(QMainWindow, Ui_GUI):
def __init__(self, parent=None)
super(GUI, self).__init__(parent)
self.setupUi(self)
QtCore.QObject.connect(self.Button, QtCore.SIGNAL("clicked()"), self.Run)
def Run(self):
print("1")
self.treeWidget1.clear()
self.treeWidget2.clear()
print("2")
self.label1.setText("Text 1")
self.label2.setText("Text 2")
print("3")
for y in range(0, 5):
self.treeWidget1.resizeColumnsToContents()
print("Finished")
#Establish the file monitoring mechanism, *outside* the PySide class
def FileChanged():
Script = GUI()
Script.Run()
Paths = ['path/to/file']
Watch = QtCore.QFileSystemWatcher(Paths)
Watch.fileChanged.connect(FileChanged)
#Setting up the GUI
if __name__ == '__main__':
app = QApplication(sys.argv)
showGUI = GUI()
showGUI.show()
app.exec_()
As I mentioned above, the above code doesn't return any errors. When I change the file (listed in the path), FileChanged does indeed call the Run() method from the GUI class. However, it won't actually do any of the 'stuff', it will only execute the print commands in between the 'stuff'.
If I then click on the 'Button' in the GUI, it will execute Run() correctly, and properly execute all the 'stuff'.
My question: is there something I'm missing here? If it's calling the method correctly, and is able to execute the various 'print' commands, why is it not executing the actual 'stuff'?
Thanks!
EDIT 1: I've removed the -do stuff- tags and put in some example code. All the 'stuff' code relates to updating various PySide QLabels, QTreeWidgets, etc.
EDIT 2: I forget the () at the end of the treeWidget clear commands.
The Script object created in the FileChanged function has local scope, and will be garbage-collected as soon as the function returns.
If the Run slot gets called when the signal fires, it will carry out all of the changes correctly, but you won't get to see any of those changes, because Script will be deleted before it is ever shown.
In order to for the example script to begin to make any sense, it would need to be re-arranged to something like this:
#Setting up the GUI
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
showGUI = GUI()
#Establish the file monitoring mechanism, *outside* the PySide class
def FileChanged():
showGUI.Run()
Paths = ['path/to/file']
Watch = QtCore.QFileSystemWatcher(Paths)
Watch.fileChanged.connect(FileChanged)
showGUI.show()
app.exec_()
Of course, it's possible that your real code is nothing like the example in your question (which has numerous other errors preventing it from being run), and so this might be no help. But if that is the case, you will have to post a fully working, self-contained example that properly demonstrates the problems you are having.

Create a python tkinter window with no X (close) button

I'm writing a 'wizard' type Python Tkinter GUI that collects information from the user and then performs several actions based on the user's entries: file copying, DB updates, etc. The processing normally takes 30-60 seconds and during that time, I want to:
Provide the user with text updates on the activity and progress
Prevent the user from closing the app until it's finished what it's doing
I started on the route of having the text updates appear in a child window that's configured to be trainsient and using wait_window to pause the main loop until the activities are done. This worked fine for other custom dialog boxes I created which have OK/cancel buttons that call the window's destroy method. The basic approach is:
def myCustomDialog(parent,*args):
winCDLG = _cdlgWin(parent,*args)
winCDLG.showWin()
winCDLG.dlgWin.focus_set()
winCDLG.dlgWin.grab_set()
winCDLG.dlgWin.transient(parent)
winCDLG.dlgWin.wait_window(winCDLG.dlgWin)
return winCDLG.userResponse
class _cdlgWin():
def __init__(self,parent,*args):
self.parent = parent
self.dlgWin = tk.Toplevel()
self.userResponse = ''
def showWin(self):
#Tkinter widgets and geometry defined here
def _btnOKClick(self):
#self.userResponse assigned from user entry/entries on dialog
self.dlgWin.destroy()
def _btnCancelClick(self):
self.dlgWin.destroy()
However this approach isn't working for the new monitor-and-update dialog I want to create.
First, because there's no user-initiated action to trigger the copy/update activities and then the destroy, I have to put them either in showWin, or in another method. I've tried both ways but I'm stuck between a race condition (the code completes the copy/update stuff but then tries to destroy the window before it's there), and never executing the copy/update stuff in the first place because it hits the wait_window before I can activate the other method.
If I could figure out a way past that, then the secondary problem (preventing the user from closing the child window before the work's done) is covered by the answers below.
So... is there any kind of bandaid I could apply to make this approach work the way I want? Or do I need to just scrap this because it can't work? (And if it's the latter, is there any way I can accomplish the original goal?)
self.dlgWin.overrideredirect(1) will remove all of the buttons (make a borderless window). Is that what you're looking for?
As far as I know, window control buttons are implemented by the window manager, so I think it is not possible to just remove one of them with Tkinter (I am not 100% sure though). The common solution for this problem is to set a callback to the protocol WM_DELETE_WINDOW and use it to control the behaviour of the window:
class _cdlgWin():
def __init__(self,parent,*args):
self.parent = parent
self.dlgWin = tk.Toplevel()
self.dlgWin.protocol('WM_DELETE_WINDOW', self.close)
self.userResponse = ''
def close(self):
tkMessageBox.showwarning('Warning!',
'The pending action has not finished yet')
# ...

wxPython: Threading GUI --> Using Custom Event Handler

I am trying to learn how to run a thread off the main GUI app to do my serial port sending/receiving while keeping my GUI alive. My best Googling attempts have landed me at the wxpython wiki on: http://wiki.wxpython.org/LongRunningTasks which provides several examples. I have settled on learning the first example, involving starting a worker thread when the particular button is selected.
I am having trouble understanding the custom-event-definition:
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""
def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
Primarily the
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
I think EVT_RESULT is placed outside the classes so as to make it call-able by both classes (making it global?)
And.. the main GUI app monitors the thread's progress via:
# Set up event handler for any worker thread results
EVT_RESULT(self,self.OnResult)
I also notice that in a lot of examples, when the writer uses
from wx import *
they simply bind things by
EVT_SOME_NEW_EVENT(self, self.handler)
as opposed to
wx.Bind(EVT_SOME_NEW_EVENT, self.handler)
Which doesn't help me understand it any faster.
Thanks,
That's the old style of defining custom events. See the migration guide for more information.
Taken from the migration guide:
If you create your own custom event
types and EVT_* functions, and you
want to be able to use them with the
Bind method above then you should
change your EVT_* to be an instance of wx.PyEventBinder instead of a
function. For example, if you used to
have something like this:
myCustomEventType = wxNewEventType()
def EVT_MY_CUSTOM_EVENT(win, id, func):
win.Connect(id, -1, myCustomEventType, func)
Change it like so:
myCustomEventType = wx.NewEventType()
EVT_MY_CUSTOM_EVENT = wx.PyEventBinder(myCustomEventType, 1)
Here is another post that I made with a couple of example programs that do exactly what you are looking for.
You can define events like this:
from wx.lib.newevent import NewEvent
ResultEvent, EVT_RESULT = NewEvent()
You post the event like this:
wx.PostEvent(handler, ResultEvent(data=data))
Bind it like this:
def OnResult(event):
event.data
handler.Bind(EVT_RESULT, OnResult)
But if you just need to make a call from a non-main thread in the main thread you can use wx.CallAfter, here is an example.
Custom events are useful when you don't want to hard code who is responsible for what (see the observer design pattern). For example, lets say you have a main window and a couple of child windows. Suppose that some of the child windows need to be refreshed when a certain change occurs in the main window. The main window could directly refresh those child windows in such a case but a more elegant approach would be to define a custom event and have the main window post it to itself (and not bother who needs to react to it). Then the children that need to react to that event can do it them selves by binding to it (and if there is more than one it is important that they call event.Skip() so that all of the bound methods get called).
You may want to use Python threads and queues and not custom events. I have a wxPython program (OpenSTV) that loads large files that caused the gui to freeze during the loading. To prevent the freezing, I dispatch a thread to load the file and use a queue to communicate between the gui and the thread (e.g., to communicate an exception to the GUI).
def loadBallots(self):
self.dirtyBallots = Ballots()
self.dirtyBallots.exceptionQueue = Queue(1)
loadThread = Thread(target=self.dirtyBallots.loadUnknown, args=(self.filename,))
loadThread.start()
# Display a progress dialog
dlg = wx.ProgressDialog(\
"Loading ballots",
"Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots),
parent=self.frame, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
)
while loadThread.isAlive():
sleep(0.1)
dlg.Pulse("Loading ballots from %s\nNumber of ballots: %d" %
(os.path.basename(self.filename), self.dirtyBallots.numBallots))
dlg.Destroy()
if not self.dirtyBallots.exceptionQueue.empty():
raise RuntimeError(self.dirtyBallots.exceptionQueue.get())

wxPython won't close Frame with a parent who is a window handle

I have a program in Python that gets a window handle via COM from another program (think of the Python program as an addin) I set this window to be the main Python frame's parent so that if the other program minimizes, the python frame will too. The problem is when I go to exit, and try to close or destroy the main frame, the frame.close never completes it's execution (although it does disappear) and the other program refuses to close unless killed with TaskManager.
Here are roughly the steps we take:
if we are started directly, launch other program
if not, we are called from the other program, do nothing
enter main function:
create new wx.App
set other program as frame parent:
Get handle via COM
create a parent using wx.Window_FromHWND
create new frame with handle as parent
show frame
enter main loop
App.onexit:
close frame
frame = None
handle as parent = None
handle = None
Anybody have any thoughts on this or experience with this sort of thing?
I appreciate any help with this!
[Edit]
This is only the case when I use the handle as a parent, if I just get the handle and close the python program, the other program closes fine
I wonder if your Close call may be hanging in the close-handler. Have you tried calling Destroy instead? If that doesn't help, then the only solution would seem to be "reparenting" or "detaching" your frame -- I don't see a way to do that in wx, but maybe you could drop down to win32 API for that one task...?
If reparenting is all you need, you can try frame.Reparent(None) before frame.Close()
My resolution to this is a little bit hacked, and admittedly not the most elegant solution that I've ever come up with - but it works rather effectively...
Basically my steps are to start a thread that polls to see whether the window handle is existent or not. While it's still existent, do nothing. If it no longer exists, kill the python application, allowing the handle (and main application) to be released.
class CheckingThread(threading.Thread):
'''
This class runs a check on Parent Window to see if it still is running
If Parent Window closes, this class kills the Python Window application in memory
'''
def run(self):
'''
Checks Parent Window in 5 seconds intervals to make sure it is still alive.
If not alive, exit application
'''
self.needKill = False
while not self.needKill:
if self.handle is not None:
if not win32gui.IsWindow(self.handle):
os._exit(0)
break
time.sleep(5)
def Kill(self):
'''
Call from Python Window main application that causes application to exit
'''
self.needKill = True
def SetHandle(self, handle):
'''
Sets Handle so thread can check if handle exists.
This must be called before thread is started.
'''
self.handle = handle
Again, it feels a little hackish, but I don't really see another way around it. If anybody else has better resolutions, please post.

Thread Finished Event in Python

I have a PyQt program, in this program I start a new thread for drawing a complicated image.
I want to know when the thread has finished so I can print the image on the form.
The only obstacle I'm facing is that I need to invoke the method of drawing from inside the GUI thread, so I want a way to tell the GUI thread to do something from inside the drawing thread.
I could do it using one thread but the program halts.
I used to do it in C# using a BackgroundWorker which had an event for finishing.
Is there a way to do such thing in Python? or should I hack into the main loop of PyQt application and change it a bit?
In the samples with PyQt-Py2.6-gpl-4.4.4-2.exe, there's the Mandelbrot app. In my install, the source is in C:\Python26\Lib\site-packages\PyQt4\examples\threads\mandelbrot.pyw. It uses a thread to render the pixmap and a signal (search the code for QtCore.SIGNAL) to tell the GUI thread its time to draw. Looks like what you want.
I had a similar issue with one of my projects, and used signals to tell my main GUI thread when to display results from the worker and update a progress bar.
Note that there are several examples to connect objects and signals in the PyQt reference guide. Not all of which apply to python (took me a while to realize this).
Here are the examples you want to look at for connecting a python signal to a python function.
QtCore.QObject.connect(a, QtCore.SIGNAL("PySig"), pyFunction)
a.emit(QtCore.SIGNAL("pySig"), "Hello", "World")
Also, don't forget to add __pyqtSignals__ = ( "PySig", ) to your worker class.
Here's a stripped down version of what I did:
class MyGui(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.worker = None
def makeWorker(self):
#create new thread
self.worker = Worker(work_to_do)
#connect thread to GUI function
QtCore.QObject.connect(self.worker, QtCore.SIGNAL('progressUpdated'), self.updateWorkerProgress)
QtCore.QObject.connect(self.worker, QtCore.SIGNAL('resultsReady'), self.updateResults)
#start thread
self.worker.start()
def updateResults(self):
results = self.worker.results
#display results in the GUI
def updateWorkerProgress(self, msg)
progress = self.worker.progress
#update progress bar and display msg in status bar
class Worker(QtCore.QThread):
__pyqtSignals__ = ( "resultsReady",
"progressUpdated" )
def __init__(self, work_queue):
self.progress = 0
self.results = []
self.work_queue = work_queue
QtCore.QThread.__init__(self, None)
def run(self):
#do whatever work
num_work_items = len(self.work_queue)
for i, work_item in enumerate(self.work_queue):
new_progress = int((float(i)/num_work_items)*100)
#emit signal only if progress has changed
if self.progress != new_progress:
self.progress = new_progress
self.emit(QtCore.SIGNAL("progressUpdated"), 'Working...')
#process work item and update results
result = processWorkItem(work_item)
self.results.append(result)
self.emit(QtCore.SIGNAL("resultsReady"))
I believe that your drawing thread can send an event to the main thread using QApplication.postEvent. You just need to pick some object as the receiver of the event. More info
Expanding on Jeff's answer: the Qt documentation on thread support states that it's possible to make event handlers (slots in Qt parlance) execute in the thread that "owns" an object.
So in your case, you'd define a slot printImage(QImage) on the form, and a doneDrawing(QImage) signal on whatever is creating the image, and just connect them using a queued or auto connection.

Categories