I've got a working code for the training of a neural network. Now I want to create a TkInter GUI to be able to e.g. change the parameters on the fly. I imagine it being like a remote control. But I am struggling to get a responsive interface. The training of one epoch will take a couple of minutes without me being able to intercept.
I've seen a lot of examples which use the .after() method to maybe update a clock on the GUI, which is fine, because updating doesn't take minutes. I can't get it to work, when the callback takes minutes.
I recreated my problem with the minimal amount of code:
from time import sleep
import tkinter as tk
def trainEpoch(i):
print ("Training", i)
sleep(1) #in reality more like sleep(300)
def clickBtn():
print ("Button pressed")
root = tk.Tk()
btn = tk.Button(root, text="Click Me", command=clickBtn)
btn.grid(column=0, row=0)
for i in range (5):
trainEpoch(i)
print ("finished" )
root.mainloop()
This code first "trains" the network, then creates a responsive TkInter window (which I fully understand).
Just to be clear, I wish the following behaviour:
I want the GUI to open before the training starts and be clickable while the training is doing its thing. I don't care if it gets destroyed afterwards.
Do I need threading for this kind of problem?
You cant use sleep in the same thread as tkinter. Due to tkinter being single threaded you will block the mainloop from doing anything until the sleeping is complete.
Here is an example using after()
to perform the same task without blocking your tkinter application.
import tkinter as tk
def train_epoch(i):
if i <= 5:
print("Training", i)
i += 1
root.after(2000, lambda i=i: train_epoch(i))
else:
print("finished")
def click_btn():
print("Button pressed")
root = tk.Tk()
tk.Button(root, text="Click Me", command=click_btn).grid(column=0, row=0)
train_epoch(1)
root.mainloop()
Results:
If you need to use threading you can interact with a variable in the global namespace between tkinter and the threaded function.
See this example:
import tkinter as tk
import threading
import time
def train_epoch():
global some_var
while some_var <= 5:
print(some_var)
time.sleep(0.5)
def click_btn():
global some_var
some_var += 1
print("Button pressed")
root = tk.Tk()
some_var = 0
tk.Button(root, text="Click Me", command=click_btn).pack()
thread = threading.Thread(target=train_epoch)
thread.start()
root.mainloop()