lyxal
lyxal

Reputation: 1118

RuntimeError when using threading and tkinter

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:

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

Answers (2)

lyxal
lyxal

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

Piero Bird
Piero Bird

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

Related Questions