为什么从工作线程更新tkinter控件似乎有效?
我在研究tkinter多线程的时候,发现大家都提到tkinter必须在主线程中运行(就像很多图形界面框架一样),而当一个单独的线程需要和图形界面沟通时,必须使用队列,而不是直接从工作线程访问界面组件。
我想学习使用队列的方法,但当然一开始我想看看如果我做错了会发生什么,所以我写了这段代码,用来近似计算圆周率(π),运行了几秒钟:
import tkinter as tk
from tkinter import ttk
from threading import Thread
import math
window = tk.Tk()
window.geometry("300x150")
lbl = tk.Label(window, text="Press Start")
lbl.place(width=280, height=60, x=10, y=10)
pb = ttk.Progressbar(window)
pb.place(width=280, height=25, x=10, y=80)
def calculate():
""" 1 / i^2 = PI^2 / 6 """
s = 0.0
for i in range(1, 10000001):
s += (1 / i**2)
if i % 1000000 == 0:
value = math.sqrt(s * 6)
lbl.config(text=value) #???
pb.step(10) #???
def start():
lbl.config(text="Press Start")
#calculate() #irresponsive GUI this way, obviously
t = Thread(target=calculate)
t.start()
btn = tk.Button(window, text="Start", command=start)
btn.place(width=280, height=25, x=10, y=115)
window.mainloop()
根据我的理解,标记为???
的行是问题所在。但这段代码运行得很好。界面保持响应,标签和进度条都在更新。
这种跨线程错误是不可预测的吗?有时候会发生,有时候又不会?这段代码最终会出问题吗?还是说我太天真了,认为唯一好的解决方案就是使用队列?我觉得在C#中这会导致cross-thread operation not valid
的错误,但在Python中似乎没有这个问题。
1 个回答
2
在普通的消费类应用程序中,有一件事情是绝对不能接受的:比如你点击了“开始”按钮,然后在进度条完成之前就关闭了窗口。这种情况下,如果一个大型应用程序的不同部分相互依赖,关闭正在被其他线程使用的界面元素,可能会导致一些不好的后果。我发现,在终端运行这个程序时,有时候它会在意外退出时卡得很厉害,甚至按 ctrl-c 也没有用。
顺便提一下,
s += (1 / i ** 2)
这一行代码是在进行整数除法,所以 s
的值只会改变一次。