[Tutor] Tkinter and threading

Peter Otten __peter__ at web.de
Thu Jun 2 12:39:23 EDT 2016


Marco Soldavini wrote:

> Hello,
> probably this is a very naive question, but I've read some stuff on
> Tkinter and its infinite loop.
> 
> Then about how can i bind actions to elements, for example buttons.
> 
> What if I want to run another loop beside the graphical interface in
> the same python script?

I usually adapt http://effbot.org/zone/tkinter-threads.htm

> For example a state machine with a var state which can have some
> discrete string values (like RUNNING, STOPPED, PAUSED, ABORTED, IDLE)
> and a text element on the gui that reports that state.
> 
> So a button cause a transition > the python loop (not the gui loop)
> detects the transition and changes the state var value > the gui
> refresh its value on screen.
> 
> I wrote some code to try it without threading but it does not give the
> expected result as it seems the button update status action is already
> triggered. I am missing some basic point here

Indeed your problem has nothing to do with threads.
> 
> from Tkinter import *
> 
> state = "STOPPED"
> 
> root = Tk()
> 
> myContainer1 = Frame(root)
> myContainer1.pack()
> label1=Label(myContainer1, text=state, font = "Helvetica 16 bold italic")
> label1.pack()
> 
> def change(new_state):
>     label1.config(text=new_state)
> 
> button1 = Button(myContainer1, text="START")
> button1.bind("<Button-1>", change("START"))

In the line above you invoke the change function and bind its result (None) 
to the button1 widget's <Button-1> event. Instead you should pass a function 
that will be invoked by Tkinter when the button is pressed:

def start(event):
    change("START")

button1.bind("<Button-1>", start)

You can define simple functions inline, so

button1.bind("<Button-1>", lambda event: change("START"))

would also work. However, the usual way to bind actions to Button widgets is 
to pass the callback function as the command argument to the constructor:

button = Button(container, command=your_func_that_takes_no_args)

With that approach your script will become

from Tkinter import *

def change(new_state):
    label1.config(text=new_state)

def start():
    change("RUNNING")

def stop():
    change("STOPPED")

root = Tk()

myContainer1 = Frame(root)
myContainer1.pack()

label1 = Label(myContainer1, font="Helvetica 16 bold italic")
label1.pack()

stop()

button1 = Button(myContainer1, text="Start", command=start)
button1.pack()

button2 = Button(myContainer1, text="Stop", command=stop)
button2.pack()

root.mainloop()






More information about the Tutor mailing list