Tkinter GUI中的多线程,不同类中的线程

das*_*das 4 python tkinter python-multithreading python-2.7 python-3.x

我目前正在学习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() 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

输出是一个带有2个按钮的GUI-第一个打印数字,第二个打印"Hello".但是在单击第一个按钮时,GUI会在数字打印时冻结.在寻找解决方案时,我发现"多线程"可能有所帮助.但经过多次尝试,我无法对我给出的示例程序应用多线程.

Pyt*_*sta 7

你不需要线程来做这么简单的事情.

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"
Run Code Online (Sandbox Code Playgroud)

after方法采用以下参数:

after(delay_ms, callback, arguments)
Run Code Online (Sandbox Code Playgroud)

时间以毫秒为单位,1000毫秒= 1秒.因此,我们通过2,000毫秒延迟2秒.


Rol*_*ith 5

Pythonista的答案非常好。但我想谈谈其他一些要点。

  • GUI是事件驱动的。它们循环处理事件,不时地调用您的代码段(称为回调)。因此,您的代码或多或少是事件循环中的客人。正如您所注意到的,您的代码段应该很快完成,否则它们会使事件处理停止,从而使GUI无响应。这是与教程中经常看到的线性程序完全不同的编程模型。要执行运行时间更长的计算或任务,可以将它们分成小块并使用after。或者,您也可以使用multiprocessing。但是,您仍然需要定期(after再次)检查它们是否完成。

以下几点源于正确进行多线程是困难的事实。

  • CPython(最常用的Python实现)具有所谓的全局解释器锁。这样可以确保一次只能有一个线程在执行Python字节码。当其他线程正忙于执行Python字节码时,运行GUI的线程什么也不做。因此,多线程并不是解决GUI不响应问题的特定解决方案。

  • 许多GUI工具包都不是线程安全的,并且tkinter也不例外。这意味着您只能从运行的线程进行tkinter调用mainloop