Tkinter GUI, I/O & Threading: When to use queues, when events?

[*]

So I did it like this but I do not know if it fits to you or if this is a good way to do this, but it safes you the .after as stated in the comments, which has the benefit that your function do_stuff is just called when needed.

import tkinter as tk
import time
import threading

def get_data():
    time.sleep(3)
    print('sleeped 3')
    _check.set(1)

def do_stuff():
    try:
        root.configure(bg='#'+str(_var.get()))
        _var.set(_var.get()+101010)
    except:
        _var.set(101010)

root = tk.Tk()
_check = tk.IntVar(value=0)
_var = tk.IntVar(value=101010)


def callback(event=None, *args):
    t1 = threading.Thread(target=get_data)
    t1.start()
    
    do_stuff()
    
_check.trace_add('write', callback) #kepp track of that variable and trigger callback if changed
callback() # start the loop



root.mainloop()

Some research:

[The Tcl]

interpreter is only valid in the thread that created it, and all Tk
activity must happen in this thread, also. That means that the
mainloop must be invoked in the thread that created the
interpreter. Invoking commands from other threads is possible;
_tkinter will queue an event for the interpreter thread, which will
then execute the command and pass back the result.

#l1493 var_invoke

 The current thread is not the interpreter thread.  Marshal

       the call to the interpreter thread, then wait for

       completion. */

    if (!WaitForMainloop(self))

        return NULL;

is-it-safe-to-use-a-intvar-doublevar-in-a-python-thread

When you set a variable, it calls the globalsetvar method on the
master widget associated with the Variable. The _tk.globalsetvar
method is implemented in C, and internally calls var_invoke, which
internally calls WaitForMainLoop, which will attempt schedule the
command for execution in the main thread, as described in the quote
from the _tkinter source I included above.

wiki.tcl

     Start
       |
       |<----------------------------------------------------------+
       v                                                           ^
   Do I have    No[*]  Calculate how            Sleep for at       |
   work to do?  -----> long I may sleep  -----> most that much --->|
       |                                        time               |
       | Yes                                                       |
       |                                                           |
       v                                                           |
   Do one callback                                                 |
       |                                                           |
       +-----------------------------------------------------------+

Commonsense

from bugtracker:

Tkinter and threads.

If you want to use both tkinter and threads, the safest method is to
make all tkinter calls in the main thread. If worker threads generate
data needed for tkinter calls, use a queue.Queue to send the data to
the main thread. For a clean shutdown, add a method to wait for
threads to stop and have it called when the window close button [X] is
pressed.

effbot

Just run all UI code in the main thread, and let the writers write to
a Queue object; e.g.

Conclusion

The Way you did it and the way I did it dosent seem like the ideal but they seem not wrong at all. It depends on what is needed.

Leave a Comment