Tkinter GUI 中的多线程,不同 类 中的线程
Multi threading in Tkinter GUI, threads in different classes
我目前正在学习 Tkinter GUI 编程。我被困在多线程概念的某个地方。尽管这个话题在这里讨论了好几次,我还是没能理解这个概念并将其应用到我的小示例程序中。
下面是我的代码:
from PIL import Image, ImageTk
from Tkinter import Tk, Label, BOTH
from ttk import Frame, Style
from Tkinter import *
import time
class Widgets(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.grid()
self.parent = parent
self.initUI(parent)
def initUI(self, parent):
self.parent.title("Count Numbers")
for r in range(10):
self.parent.rowconfigure(r, weight=1)
for c in range(10):
self.parent.columnconfigure(c, weight=1)
self.button1 = Button(parent, text = "count")
self.button1.grid(row = 1, column = 1, rowspan = 1, columnspan = 2, sticky = W+E+N+S )
self.button1["command"] = self.countNum
self.button2 = Button(parent, text = "say Hello")
self.button2.grid(row = 1, column = 7, rowspan = 1, columnspan = 2, sticky = W+E+N+S)
self.button2["command"] = PrintHello(self).helloPrint
def countNum(self):
for i in range(10):
print i
time.sleep(2)
class PrintHello(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.grid()
self.parent = parent
def helloPrint(self):
print "Hello"
def main():
root = Tk()
root.geometry("300x200")
app = Widgets(root)
root.mainloop()
if __name__ == '__main__':
main()
输出是一个带有 2 个按钮的 GUI - 第一个打印数字,第二个打印 "Hello"。但是在单击第一个按钮时,GUI 会 frozen 而数字正在打印。在寻找解决方案时,我发现 'multi threading' 可能会有所帮助。但是经过多次尝试,我无法将多线程应用到我给出的示例程序中。
这么简单的事情不需要线程。
GUI 冻结,因为您将 time.sleep
放入函数中,该函数阻塞主线程直到完成。
只需使用 Tk 的内置 after
方法。将您的功能更改为。
def countNum(self, num=0):
if num < 10:
print num
root.after(2000, lambda: self.countNum(num + 1))
else:
print "Stopping after call"
after
方法采用以下参数:
after(delay_ms, callback, arguments)
时间单位为毫秒,1000毫秒=1秒。因此,我们通过 2,000 毫秒延迟 2 秒。
Pythonista 的回答非常好。但我想谈谈一些额外的要点。
- GUI 事件驱动。它们 运行 在一个循环中处理事件,时不时地调用您的代码片段(称为回调)。所以你的代码或多或少是事件循环中的客人。正如您所注意到的,您的代码片段应该快速完成,否则它们会拖延事件处理,使 GUI 无响应。这是一种完全不同于教程中常见的线性程序的编程模型。要执行更长的 运行 宁计算或任务,您可以将它们分成小块并使用
after
。或者您可以在另一个进程中使用 multiprocessing
与他们联系。但是如果他们已经完成,你仍然需要定期检查(再次使用 after
)。
以下几点源于正确进行多线程困难。
CPython(最常用的 Python 实现)具有所谓的全局解释器锁。这确保一次只有 一个线程 可以执行 Python 字节码。当其他线程忙于执行 Python 字节码时,使用 GUI 的线程 运行 什么都不做。
因此,多线程并不是解决 GUI 无响应问题的特定方法。
很多GUI工具包都不是线程安全的,tkinter也不例外。这意味着您应该 仅 从 运行 正在 mainloop
. 的线程进行 tkinter 调用(在 Python 3.x, tkinter
已成为线程安全的。)
我目前正在学习 Tkinter GUI 编程。我被困在多线程概念的某个地方。尽管这个话题在这里讨论了好几次,我还是没能理解这个概念并将其应用到我的小示例程序中。
下面是我的代码:
from PIL import Image, ImageTk
from Tkinter import Tk, Label, BOTH
from ttk import Frame, Style
from Tkinter import *
import time
class Widgets(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.grid()
self.parent = parent
self.initUI(parent)
def initUI(self, parent):
self.parent.title("Count Numbers")
for r in range(10):
self.parent.rowconfigure(r, weight=1)
for c in range(10):
self.parent.columnconfigure(c, weight=1)
self.button1 = Button(parent, text = "count")
self.button1.grid(row = 1, column = 1, rowspan = 1, columnspan = 2, sticky = W+E+N+S )
self.button1["command"] = self.countNum
self.button2 = Button(parent, text = "say Hello")
self.button2.grid(row = 1, column = 7, rowspan = 1, columnspan = 2, sticky = W+E+N+S)
self.button2["command"] = PrintHello(self).helloPrint
def countNum(self):
for i in range(10):
print i
time.sleep(2)
class PrintHello(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.grid()
self.parent = parent
def helloPrint(self):
print "Hello"
def main():
root = Tk()
root.geometry("300x200")
app = Widgets(root)
root.mainloop()
if __name__ == '__main__':
main()
输出是一个带有 2 个按钮的 GUI - 第一个打印数字,第二个打印 "Hello"。但是在单击第一个按钮时,GUI 会 frozen 而数字正在打印。在寻找解决方案时,我发现 'multi threading' 可能会有所帮助。但是经过多次尝试,我无法将多线程应用到我给出的示例程序中。
这么简单的事情不需要线程。
GUI 冻结,因为您将 time.sleep
放入函数中,该函数阻塞主线程直到完成。
只需使用 Tk 的内置 after
方法。将您的功能更改为。
def countNum(self, num=0):
if num < 10:
print num
root.after(2000, lambda: self.countNum(num + 1))
else:
print "Stopping after call"
after
方法采用以下参数:
after(delay_ms, callback, arguments)
时间单位为毫秒,1000毫秒=1秒。因此,我们通过 2,000 毫秒延迟 2 秒。
Pythonista 的回答非常好。但我想谈谈一些额外的要点。
- GUI 事件驱动。它们 运行 在一个循环中处理事件,时不时地调用您的代码片段(称为回调)。所以你的代码或多或少是事件循环中的客人。正如您所注意到的,您的代码片段应该快速完成,否则它们会拖延事件处理,使 GUI 无响应。这是一种完全不同于教程中常见的线性程序的编程模型。要执行更长的 运行 宁计算或任务,您可以将它们分成小块并使用
after
。或者您可以在另一个进程中使用multiprocessing
与他们联系。但是如果他们已经完成,你仍然需要定期检查(再次使用after
)。
以下几点源于正确进行多线程困难。
CPython(最常用的 Python 实现)具有所谓的全局解释器锁。这确保一次只有 一个线程 可以执行 Python 字节码。当其他线程忙于执行 Python 字节码时,使用 GUI 的线程 运行 什么都不做。 因此,多线程并不是解决 GUI 无响应问题的特定方法。
很多GUI工具包都不是线程安全的,tkinter也不例外。这意味着您应该 仅 从 运行 正在的线程进行 tkinter 调用(在 Python 3.x,mainloop
.tkinter
已成为线程安全的。)