【问题标题】:Sharing variables between threads in Python tkinter在 Python tkinter 中的线程之间共享变量
【发布时间】:2021-04-20 19:58:14
【问题描述】:

是否可以让 GUI 更新变量 'a' 以在 'thread2' 增加值时实时更新?

import tkinter as tk
from threading import Thread
import time


a = 0  # global variable

def thread1(threadname):
    root = tk.Tk()
    w = tk.Label(root, text=a)
    w.pack()
    root.mainloop()


def thread2(threadname):
    global a
    while True:
        a += 1
        time.sleep(1)


thread1 = Thread( target=thread1, args=("Thread-1", ) )
thread2 = Thread( target=thread2, args=("Thread-2", ) )

thread1.start()
thread2.start()

如果我创建一个循环并打印“a”,我会得到正确的结果。

def thread1(threadname):
    global a
    while True:
        print(a)
 #   root = tk.Tk()
 #   w = tk.Label(root, text=a)
 #   w.pack()
 #   root.mainloop()

任何帮助将不胜感激

【问题讨论】:

    标签: python multithreading tkinter


    【解决方案1】:

    当您创建标签时,这不是“实时”连接。这将传递变量a 的当前值。然后您进入主循环,该线程在应用程序退出之前什么都不做。您需要将新值发送到作为主线程的一部分执行的函数,并且该函数将需要访问标签。

    这行得通:

    import tkinter as tk
    from threading import Thread
    import time
    
    
    class GUI(object):
        def __init__(self):
            self.a = 0 
            self.w = tk.Label(root, text=self.a)
            self.w.pack()
            thread2 = Thread( target=self.thread2, args=("Thread-2", ) )
            thread2.start()
    
        def thread2(self,threadname):
            while True:
                self.a += 1
                root.after_idle(self.update)
                time.sleep(1)
    
        def update(self):
            self.w.config(text=self.a)
    
    root = tk.Tk()
    gui = GUI()
    root.mainloop()
    

    可以使用textvariable 进行实时连接,但是您必须将a 的类型更改为tkinter.StringVariable。在这里查看:Update Tkinter Label from variable

    【讨论】:

    • 只是一个简单的问题:.after_idle 线程安全吗?我读到它类似于.after,但它是否足够安全,可以从多个线程中使用,而不用担心 tkinter 崩溃?
    • after API 的全部要点是强制在主线程中调用传递的函数。它基本上是线程同步器。
    • 好吧,我还没有看到有人使用after_idle,我认为after 是为了避免在使用time.sleep 时窗口冻结/变得无响应
    • @TimRoberts 对不起,我绝对是 python 菜鸟。通读 tkinter 文档,我的印象是程序在Tk()mainloop() 之间无限循环,每次迭代都会更新。我想这就是我不明白打印命令为什么起作用的原因,但我的方法并没有尝试将其用作未来的学习经验。当我可以使用 IDE 时,我会试试你的技术。
    • @Dan <tkinter.Tk>.mainloop() 更新窗口,处理所有传入事件到它们各自的绑定函数并处理所有.after.after_idle 脚本。所以实际上在mainloop 内部有代码调用在这种情况下传递给after_idleafter 在我的回答中的函数。
    【解决方案2】:

    尝试像这样使用<tkinter.Tk>.after 循环:

    import tkinter as tk
    
    def loop():
        global a
        a += 1
        label.config(text=a)
        # run `loop` again in 1000 ms
        root.after(1000, loop)
    
    a = 0
    root = tk.Tk()
    label = tk.Label(root, text=a)
    label.pack()
    loop() # Start the loop
    root.mainloop()
    

    我使用了.after 脚本,因为tkinterthreading 不能很好地结合在一起。如果您尝试从第二个线程调用一些tkinter 命令,有时tkinter 可能会崩溃,甚至不会给您一条错误消息。

    【讨论】:

      猜你喜欢
      • 2023-02-20
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-22
      • 1970-01-01
      • 2021-01-27
      • 1970-01-01
      相关资源
      最近更新 更多