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.
Related
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.
In a couple of answers (see here and here), when dealing with GUI + asyncio in separate threads, it suggests to use a queue when the asyncio loop needs to communicate with the GUI. However, when the GUI wants to communicate with the asyncio event loop it should use call_soon_threadsafe().
For example, one answer states:
When the event loop needs to notify the GUI to refresh something, it can use a queue as shown here. On the other hand, if the GUI needs to tell the event loop to do something, it can call call_soon_threadsafe or run_coroutine_threadsafe.
What I don't understand is why can the GUI not also use another Queue rather than call_soon_threadsafe()? i.e. can the GUI not put data on a queue for the asyncio loop to get and process? Is it just a design decision or is there some technical reason not to use a queue from GUI to asyncio loop?
There's no appropriate queue class to use. asyncio.Queue isn't safe to interact with from outside the event loop, and queue.Queue would block the event loop.
If you want to use a queue anyway, you could use asyncio.run_coroutine_threadsafe to call an asyncio.Queue's put method.
I want to know if there is an option to know when a task is added/appended to Queue in python.
I have an application where some process runs in thread. depending on the condition a function is called from that thread. the called function will display a window.
currently its working and the window is getting displayed, my main issue is the application gets hanged when the window is displayed. I think this is due to the call of the function which displays the window was with in the thread.
So from my search some one suggested me to use Queue in python.
But when i go through python example shows that a infinite loop will be running in main thread.
If thats the case then my entire app will be in infinite loop.
You didn't specify the exact GUI framework you're using.
If you're using wxPython you can use wx.CallAfter to call a function from another thread:
def ShowWindow(txt):
wx.MessageBox(txt)
From another thread:
wx.CallAfter(ShowWindow,'message from another dimension')
This won't cause your main loop to hang.
It's said that you should not call GUI functions from a thread, but I'm wondering if this is applicable only when you call functions which affects GUI directly or it's applicable on every function provided by GUI library. By example, it is safe to call:
gobject.idle_add(self.gui.get_object('button1').set_sensitive, False)
in a thread? Because self.gui.get_object is a function from the GUI framework but self.gui.get_object('button1') is actually calling it.
Thank you for your answers.
The call you showed there seems safe. As already posted, you can read (get_object) just fine in any thread, but should only modify (set_sensitive) in the main thread. Exactly this is done here, idle_add adds the event to the main loop which is running in the main thread.
Threading with GUI is bit tricky.If you want to do it right, you should not update GUI from within any other thread than main thread (common limitation in GUI libs). However you can make multiple read calls from several threads.
I have a program that receives serial data and uses matplotlib to graph it using Tkinter. I have this working currently, but I've had to use the .after() function to poll a queue for data. In other UI frameworks I've used in the past (different projects in C) there has been a way to ask the UI framework to call a function given to it from the mainloop (either after some amount of time, during idle, etc). If I try to use .after() on a thread which isn't the mainloop, it doesn't work and complains at me.
Is there a way to call a user supplied function, provided on a thread which isn't the mainloop, from the mainloop? Or, is there a way to instruct the mainloop to wake up and do some work if a Queue gets some data?
Thanks.
I've heard that you can call event_generate from the non-GUI thread. If you do call event_generate, I've read that you should give the value of tail to the when parameter.
I've personally only done this in one project, but it seemed to work fine.