【问题标题】:RuntimeError when using threading and tkinter使用线程和 tkinter 时出现 RuntimeError
【发布时间】:2018-03-30 23:57:55
【问题描述】:

我正在创建一个运行多个线程的程序,其中每个线程更新一个变量,然后使用tkinter 显示该值。

唯一的问题是,每当我尝试更新显示时,我都会收到 RuntimeError

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


我尝试修复错误的一些解决方案是:

  • 使显示对象成为函数的全局对象(使用global
  • 创建一个单独的函数来更新显示

但是,这些解决方案都不起作用(RuntimeError 仍然不断出现)。

下面是我的程序:

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()

我不知道如何修复 RuntimeError,因此我们将不胜感激。

【问题讨论】:

    标签: python multithreading python-3.x tkinter runtime-error


    【解决方案1】:

    这实际上是由于您的睡眠功能,这会冻结 tkinter 的主线程,这是您无法做到的。

    下面是一些可以工作的代码:

    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()
    

    注意我是如何使用“window.after(3000, function)”的。这告诉 tkinter 在 3 秒后做某事,因此不会冻结主线程。如果您希望它在显示数字 1 之前就休眠,您需要更改一些内容,在这种情况下,我很乐意为您更新我的代码 :)

    【讨论】:

    • 请注意,我想使用threading 库,如问题中所述。不过,我还是会考虑 window.after(3000, function) 的想法。
    • 由于个人经验,我知道这一点,您可以使用线程,但由于 Tkinter 的构建方式,不幸的是 sleep() 会导致问题。
    【解决方案2】:

    经过一些实验和 Piero Bird 提出的想法,我想出了这个解决方案:

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-07-19
      • 2020-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-16
      • 2019-08-18
      相关资源
      最近更新 更多