Does a GUI-class argument violate thread-safety in tkinter? - python

I learned the hard way that tkinter is not thread-safe when starting independent threads with tkinter functionality from the main tkinter thread. I got error messages in a (for me) non-reproducible way, mostly "main thread is not in main loop" in connection to internal del calls after I stopped my application. Sometimes the kernel crashed during or after execution, often everything just ran smoothly.
These independent threads should run data acquisitions (DAQ) at a couple of instruments, with different GUIs depending on the type of instrument. Threading seems to be feasible as it is not known from start which instrument will be needed at some time, DAQ tasks should be queued up if an instrument is busy etc.
So, my idea now is to start the DAQ threads without any tkinter functionality from the main thread. The specific DAQ thread knows which specific GUI to use and puts this specific GUI class into a queue which is handled in the main GUI/tkinter thread. The instance of the GUI class will then be created in the GUI/tkinter thread.
Will this approach still violate thread-safety or is everything ok, as long as the GUI instances are created in the main tkinter thread?

As long as you only access tkinter widgets and functions from a single thread, it should work just fine. One exception, as far as I understand, is that it's safe to call the event_genereate method from other threads. You can push data on a queue and then generate an event, then the event can be handled in the main thread where data can be pulled off the queue and processed.

Related

Python multithread and (shared variables || Qt)

i'm redesigning my software because the last one i did crashed due to a wrong access to a Qt interface by a process started with
serialThread = threading.Thread(target=serialCycle)
serialThread.setDaemon(True)
serialThread.start()
serialThread mainly wait for incoming serial data, decode them and place them in a list (probably i'll move to a numpy array). Informations about connection (serial port, speed, how many data have been received) have to be written in the qt ui. I mainly use global variables to exchange informations between main thread and the serial one; i can ensure that each variable is written only by one thread and read from the other to avoid problems.
The Qt updating is a bit hard to do sending informations to main thread so i looked for another solution. I found this thread ( Updating GUI elements in MultiThreaded PyQT ), but i didn't get the point. If i start a thread with slot and signal can't I have crashes due to multiple access to the same variable?
(Question1) started thread runs in parallel and so they shouldn't change the qt interface...
(Question2) Can't an entire .ui window be loaded "linked" to a different thread so this thread can update it (and obviously not the main thread)
(Question3) Which one is the simpliest way to have a gui that can be updated by different thread about the self status (and to let the user to change parameters)?
Thanks

Start a function from a thread. (GUI thread and QThread errors)

I've made routine with Python's threading module to run a subprocess and wait for it to finish.
I do the threading with the following line :
t1=Thread(target=self.routineFunction, args=(self, "file1.txt", "file2.txt", self.nextFunction)).start()
Inside my function routineFunction(self,file1,file2,nextFunction) I call the next function to run once the subprocess has finished running.
Everything works fine until then.
But if I create new QObject items in my next function, I receive a lot of errors :
- "QPixmap: It is not safe to use pixmaps outside the GUI thread"
- "QObject::startTimer: QTimer can only be used with threads started with QThread"
My guess is that when I call nextFunction from the routine it is ran in the same thread as the routine, hence the errors.
Is there a way to call a function from the routine inside the "main" or "normal" thread ?.
Thank you for your help.
Generally, it is okay to call functions from other threads. But many GUI libraries (QT is among them) have some restrictions on this behavior.
For example, there are designated thread called 'GUI thread' which handles all graphical stuff, like dispatching messages from OS, redrawing windows, etc. And you also restricted to work with GUI withing this only thread. So, for example, you should not create QPixmap in other threads.
QTimer uses some QThread's internal data, so you should use such timers in threads only started with QThread, but not with plain Python thread module.
Returning to your question, if you want ot work with QT, you should spawn your threads using QThread and post events to GUI thread using postEvent() method. This will guarantee consistence of QT internal data structures.
So, you can ran your code in any QT thread, but if you want to work with GUI (your QObject uses QPixmap, so it is the case), you need to handle such calls only in GUI thread.
Ok so I think that werewindle solution is possible. Unfortunatly, I found it difficult to convert my current script from Thread to QThread (I am new to threading in general).
I found a workaround :
Instead of passing nextFunction as an argument, I pass a queue = Queue.Queue() and I use queue.put(True) if my subprocess in my new thread is a success. There is no more nextFunction, I simply wait for a value in the queue with queue.get().
By doing this, I can then continue in the GUI thread.

Handle a signal in another thread in Python

I have a PyGtk (GTK+ 3) application that runs in two threads:
Thread A is a main app thread that executes Gtk.main() and so handles Gtk's events/signals.
Thread B is a PulseAudio event thread that handles all PA's stuff asynchronously.
In certain cases it's necessary to make an event handled by a callback from thread B do something in Gtk objects. The problem with Python is that because of GIL only one thread can run at a time, so it's not possible to change any Gtk-related things directly — it results in a deadlock.
A solution to it might be calling Gdk.threads_init() to allow GIL to be lifted for Gtk, but that seems to result in race conditions, apparently Gtk is not thread-safe enough.
What I want to do is 'flatten out' event handling so that thread B leaves something (event/signal?) for thread A to pick up and handle. in this scenario thread B is not blocked by this operation. As far as I understand, this is not the case with Python's signalling mechanism because it handles signals synchronously.
So my question is: how can I create a sort of custom event that can be picked up by Gtk's main loop and processed by thread A code?
Gtk is NOT threadsafe, you have to write your code so that it is threadsafe.
I don't know what version of pygtk you're using, but the easiest way to queue an action on the GUI thread is with idle_add:
http://www.pygtk.org/pygtk2reference/gobject-functions.html#function-gobject--idle-add
It queue's a function in Gtk's main loop and will get executed on it's thread.
EDIT: This is just the easiest way to get a function called on the GUI thread. If you want do the work of creating a custom gobject signal, I believe (but am not 100% sure) that the signal handler will be called on the GUI thread.

How can I configure a Tkinter widget from a separate class?

I am writing a Tkinter program that requires a loop. I can't run the loop from the same class that Tkinter is in, I'm fairly certain of that much. To run said loop, I believe that I have to use a separate thread, therefore a separate class, to keep Tkinter from freezing. I have gotten Tkinter to run while a loop in the thread prints numbers. However, I need to have it configure a Tkinter window that resides in another class. How would I go about this?
You don't necessarily need another thread, because you don't necessarily need to create a loop (see my answer to your other question about using a nested loop).
However, to answer your specific question, you have to implement a queue. The worker thread will place messages of some sort on the queue, and the main thread polls the queue via the event loop and responds to the message. This is necessary because a worker thread can't directly modify tk widgets.
For an example of using threads and queues with Tkinter, see Tkinter and Threads on effbot.orb. Pay close attention to how it uses after to poll the queue every 100 ms.

QObject (QPlainTextEdit) & Multithreading issues

Im currently trying to learn Networking with Python asyncore and pyqt4.
I coded a small server, which basically listens on some port, and resends all messages it recieves to the sender.
Since both qts QApplication.exec_() and asyncore.loop() are functions which never return i could not start them both in one thread, so i stared asyncore.loop() in a seperate daemon thread.
Whenever my server class (derived from asyncore.dispatcher) establishes or drops a connection, or sends/recieves a message, it calls methods of my window class (derived from QtGui.QMainWindow), which displays the information in a QPlainTextEdit.
But the text is not visible, unless you mark the text with the mouse.
Python console displays following error msg:
QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
I read on some forum, that this may be caused by calling qt-functions from another Thread, and that using signals & slots instead of plain function calling may fix the issue, but i have tried signals aswell, and i still get this error.
So, (if that is really the cause of my problems) whats the correct way to call methods of an qt object from another thread ?
EDIT More Info:
the asyncore.loop() call is located in the child thread, well its not really blocking, but only during the runtime of asyncore.loop() my Server class (asyncore.dispatcher) can do networking.
So, during the runtime of asyncore.loop() the methods of my Server class ARE called by asyncore.loop() (=child thread), and in these i
tried to emit signals to the window class running in the main thread
EDIT: Seems like i got it working now, i had some errors in my code, everything works as intended with signals now.
EDIT: small example: http://paste2.org/p/635612 (dead link)
It appears you're trying to access QtGui classes from a thread other than the main thread. Like in some other GUI toolkits (e.g. Java Swing), that's not allowed. From the Threads and QObjects web page:
Although QObject is reentrant, the GUI
classes, notably QWidget and all its
subclasses, are not reentrant. They
can only be used from the main thread.
A solution is to use signals and slots for communication between the main thread (where the GUI objects live) and your secondary thread(s). Basically, you emit signals in one thread that get delivered to the QObjects via the other thread. The page I linked to above has a good discussion of this. Actually, the whole section on Thread Support in Qt is a good read.
One potential issue you could run into is that, normally, to get full signals and slots support working across threads, you need to start an event loop in the child thread using QThread::exec() (or the PyQt equivalent) so that signals can be delivered to slots in the QObjects that live there. In your case, it sounds like you're making a blocking call to asyncore.loop(), which will prevent you from doing this. But, if you only need to emit signals in one direction (from the child thread to widgets in the main thread), I don't think you'll have a problem.

Categories

Resources