【问题标题】:How to make tkinter wait for a new thread to complete?如何让 tkinter 等待新线程完成?
【发布时间】:2019-05-06 15:31:50
【问题描述】:

如何让函数fix() 中的tkinter 执行等到label's 文本被更改然后打印end。

当前工作:当我单击sub 按钮时,会创建新线程并执行for 循环。经过 10000000 次循环后,我的标签将更改为 9999999。但在我的标签更改之前,tkinter 会打印 end。

我尝试了t.join(),但它冻结了 GUI。

import tkinter as tk
from tkinter import ttk
import threading

root = tk.Tk()
label = tk.Label(text='vinoth')
label.pack()

def fix():
    a=0
    t = threading.Thread(target=count, args=(a,))
    t.start()
    #t.join()
    print('end')

def count(a):
        for i in range(0,10000000):
            a=i
        label['text'] = a

button = tk.Button(text='sub', command=fix)
button.pack()
dropdown = ttk.Combobox()
dropdown.pack()

root.mainloop()

【问题讨论】:

  • 我将用多行代码替换 print('end') 行,tkinter 多线程不支持这些。所以我必须在新线程中执行工作并回到主线程。请指导我如何暂停函数 fix 直到线程 t 完成。
  • 你想要这样的东西:how to wait until callback has completed

标签: python tkinter


【解决方案1】:

我通过在 while 循环中使用 root.update() 函数进行了尝试。不知道这是否是立即做的事情!但无论如何都要发布代码:

import tkinter as tk
from tkinter import ttk
import threading

root = tk.Tk()

class test():

    def __init__(self):
        self.label = tk.Label(text='vicks')
        self.label.pack()
        button = tk.Button(text='sub', command=self.fix)
        button.pack()
        dropdown = ttk.Combobox()
        dropdown.pack()

    def fix(self):
        self.finished = False
        t = threading.Thread(target=self.count)
        t.daemon = True
        t.start()
        self.check_status()
        print('end')

    def check_status(self):
        while self.finished is False:  #keeps running until the variable finished becomes True
            root.update()

    def count(self):
        a=0
        for i in range(100000000):
            a = i
        self.label['text'] = a
        self.finished = True

c = test()

root.mainloop()

【讨论】:

  • 这看起来很简单,也很重要。非常感谢!似乎有很多方法可以实现目标。
【解决方案2】:

您必须小心多线程tkinter 应用程序,因为 tcl/tk 的接口不支持它。这意味着只有主线程可以调用tkinter 及其小部件。

也就是说,您可以通过使用通用 after() 方法来安排函数定期运行并与线程通信,或者通过像 Queue 这样的线程安全机制或通过全局变量加上Lock 控制对它或它们的并发访问。

这是一个使用后者来完成您想要完成的事情的示例:

import tkinter as tk
from tkinter import ttk
import threading


POLLING_DELAY = 250  # ms
lock = threading.Lock()  # Lock for shared resources.
finished = False

root = tk.Tk()
label = tk.Label(text='vinoth')
label.pack()

def fix():
    global finished

    with lock:
        finished = False
    t = threading.Thread(target=count)
    t.daemon = True
    root.after(POLLING_DELAY, check_status)  # Start polling.
    t.start()

def check_status():
    with lock:
        if not finished:
            root.after(POLLING_DELAY, check_status)  # Keep polling.
        else:
            print('end')

def count():
    global finished

    for i in range(10000000):
        a = i
    with lock:
        finished = True
        label['text'] = a


button = tk.Button(text='sub', command=fix)
button.pack()
dropdown = ttk.Combobox()
dropdown.pack()

root.mainloop()

【讨论】:

  • 太美了!您能否也提供一个队列示例?我真的很感激。
  • @User1493:如果我的回答解决了你的问题,请采纳。仅供参考,过去我曾经发布过一个answer 到一个有关等待线程完成的相关问题,该问题使用Queue。
  • @martineau,感谢您的支持。但是您共享的代码从不打印end
  • 当daemon thread完成执行时,变量finished如何变成True?
  • @user1404:count() 函数在for 循环之后将其设置为True。但是我忘记在几个改变它的值的函数中声明它global,这就是为什么它没有打印end——尽管这是一个蹩脚的借口,原因很简单,因为我自己不使用全局变量只是忘记了……
猜你喜欢
  • 2017-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-22
  • 2020-09-21
  • 2021-08-10
  • 2011-06-05
  • 2010-12-26
相关资源
最近更新 更多