首页 文章

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

提问于
浏览
4

我目前正在学习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()

enter image description here

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

2 回答

  • 4

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

    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秒 .

  • 6

    Pythonista的答案很棒 . 但我想谈谈其他一些观点 .

    • GUI是事件驱动的 . 它们在循环处理事件中运行,不时地调用代码片段(称为回调函数) . 因此,您的代码或多或少是事件循环中的guest虚拟机 . 正如您所注意到的,您的代码片段应该快速完成,否则它们会停止事件处理,从而使GUI无响应 . 这是一个与线性程序完全不同的编程模型,这些模型经常出现在教程中 . 要执行更长时间运行的计算或任务,您可以将它们分成小块并使用 after . 或者你可以使用 multiprocessing 在另一个进程中找到它们 . 但是,如果他们已经完成,你仍然需要定期检查(再次使用 after ) .

    以下几点源于正确执行多线程这一事实 .

    • CPython(最常用的Python实现)具有所谓的全局解释器锁 . 这确保了一次只有一个线程可以执行Python字节码 . 当其他线程忙于执行Python字节码时,运行GUI的线程什么都不做 . 因此,多线程不能解决无响应的GUI问题 .

    • 很多GUI工具包都不是线程安全的,而tkinter也不例外 . 这意味着您只应该从运行 mainloop 的线程进行tkinter调用 .

相关问题