Reputation: 1118
I am creating a program that runs multiple threads where each thread updates a variable and then displays that value using tkinter
.
The only problem is that I get a RuntimeError
whenever I try and update the display:
Exception in thread Thread-x:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "program.py", line 15, in body
update()
File "program.py", line 11, in update
display.config({"text" : "x = {0}".format(x)})
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1479, in configure
return self._configure('configure', cnf, kw)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1470, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
RuntimeError: main thread is not in main loop
Some of the solutions I have tried to fix the error are:
global
)However, none of these solutions worked (the RuntimeError
still kept occurring).
Below is my program:
import tkinter, time, threading
window = tkinter.Tk()
x = 0
display = tkinter.Label(window)
display.pack()
def update():
global x
x += 1
display.config({"text" : "x = {0}".format(x)}) #It says the error is on this line
def body():
time.sleep(3)
update()
body()
def start_threads():
for i in range(5):
thread = threading.Thread(target=body)
thread.start(); thread.join()
start = tkinter.Button(window, text="Start", command=start_threads)
start.pack()
I do not know how to fix the RuntimeError
, so any help with that would be appreciated.
Upvotes: 0
Views: 140
Reputation: 1118
After a bit of experimentation and the idea suggested by Piero Bird, I have come up this solution:
import tkinter, threading
def start_counter():
for i in range(1):
bot = threading.Thread(target=add_one)
bot.start(); bot.join()
temp_window = tkinter.Tk()
temp_window.withdraw()
window.after(100, start_counter)
def add_one():
global count
count += 1
if __name__ == "__main__":
temp = 0
count = 0
window = tkinter.Tk()
window.minsize(width=500, height=500)
display = tkinter.Label(window)
display.pack()
start = tkinter.Button(window, text="Start", command=start_counter)
start.pack()
def update():
global temp, first
if count != temp:
display.config({"text":"x = {0}".format(count)})
temp = count
window.after(1, update)
window.after(1, update)
Upvotes: 0
Reputation: 119
This is actually due to your sleep function, this freezes the main thread for tkinter which you cannot do.
Here's some code that will work:
import tkinter
x = 0
repeat = 0
def start_counter():
global x, repeat
repeat+=1
x += 1
display.config({"text" : "x = {0}".format(x)})
if repeat < 5:
#3000 because 1000 in a second
window.after(3000, start_counter)
window = tkinter.Tk()
display = tkinter.Label(window)
display.pack()
start = tkinter.Button(window, text="Start", command=start_counter)
start.pack()
window.mainloop()
Notice how I use "window.after(3000, function)". This tells tkinter to do something after 3 seconds and thus doesn't freeze the main thread. If you want it to sleep before even showing the number 1, you need to change a few things, in which case I'd be happy to update my code for you :)
Upvotes: 1