Python,Tkinter:如何使用线程防止tkintergui主循环崩溃

2024-04-25 02:05:24 发布

您现在位置:Python中文网/ 问答频道 /正文

嗨,我有一个小pythongui界面,有两个按钮,start(启动一个计数器)和stop(假设停止计数器),计数器是一个无限循环,因为我不希望它结束,除非单击第二个按钮。问题是当第一个按钮的函数仍在运行时,第二个按钮无法单击。 我读到我需要使用线程,我已经尝试过了,但我不完全理解如何才能做到这一点。请帮忙。在

from Tkinter import *
import threading


class Threader(threading.Thread):
    def run(self):
        for _ in range(10):
            print threading.current_thread().getName()

    def main(self):
        import itertools
        for i in itertools.count(1, 1):
            print i

    def other(self):
        print "Other"

m = Threader(name="main")
o = Threader(name="other")

try:
    '''From here on we are building the Gui'''
    root = Tk()

    '''Lets build the GUI'''
    '''We need two frames to help sort shit, a left and a right vertical frame'''
    leftFrame = Frame(root)
    leftFrame.pack(side=LEFT)
    rightFrame = Frame(root)
    rightFrame.pack(side=RIGHT)
    '''Widgets'''
    '''Buttons'''
    playButton = Button(leftFrame, text="Play", fg="blue", command=m.main)
    stopButton = Button(rightFrame, text="Stop", fg="red", command=o.other)
    playButton.pack(side=TOP)
    stopButton.pack(side=BOTTOM)

    root.mainloop()
except Exception, e:
    print e

Tags: importselfmaindef计数器root按钮side
2条回答

对于像计数器这样简单的东西,Tkinter的after()方法通常是更好的选择。可以使用实例变量将其设置为on和off。在

class TimerTest():
    def __init__(self, root):
        self.root=root

        Button(root, text="Play", fg="blue",
                            command=self.startit).grid(row=1, column=0)
        Button(root, text="Stop", fg="red",
                            command=self.stopit).grid(row=1, column=1)

        self.is_running=True
        self.count=IntVar()
        Label(root, textvariable=self.count,
              bg="lightblue").grid(row=0, column=0, columnspan=2, sticky="ew")

    def startit(self):
        self.is_running=True
        self.increment_counter()

    def increment_counter(self):
        if self.is_running:
             c=self.count.get()
             c += 1
             self.count.set(c)
             root.after(1000, self.increment_counter)  ## every second

    def stopit(self):
        self.is_running = False

root = Tk()
TT=TimerTest(root)
root.mainloop()

下面是一个使用threading的简短示例。我取出了你的other函数,我不知道你为什么在这里使用itertools。我也把它取出来,用一个简单的线程示例进行设置。在

几件事:

您使用threading.Thread作为Threader的基类进行设置,但实际上从未初始化基类。在

无论何时使用线程,通常都需要定义一个run方法,然后使用start()来启动线程。调用start()将调用run。在

您需要使用线程来防止GUI阻塞,因为tkinter只是一个巨大循环中的一个线程。因此,每当您有一些长时间运行的进程时,它会阻塞这个线程,直到当前进程完成为止。这就是为什么它被放在另一个线程中。Python有一种叫做GIL的东西,它阻止了并行化(我自己编的),因为它一次只能使用一个线程。取而代之的是,它使用时间切片,即它们之间的GIL“轮询”来显示并发运行的多个任务的外观。对于真正的并行处理,应该使用multiprocessing。在

在下面的代码中,我使用了self.daemon = True。将线程设置为守护进程将在您退出主程序(在本例中是tkgui)时终止它

from tkinter import *
import threading, time

class Threader(threading.Thread):

    def __init__(self, *args, **kwargs):

        threading.Thread.__init__(self, *args, **kwargs)
        self.daemon = True
        self.start()

    def run(self):

         while True:
            print("Look a while true loop that doesn't block the GUI!")
            print("Current Thread: %s" % self.name)
            time.sleep(1)

if __name__ == '__main__':

    root = Tk()
    leftFrame = Frame(root)
    leftFrame.pack(side=LEFT)
    rightFrame = Frame(root)
    rightFrame.pack(side=RIGHT)
    playButton = Button(leftFrame, text="Play", fg="blue", 
        command= lambda: Threader(name='Play-Thread'))
    stopButton = Button(rightFrame, text="Stop", fg="red", 
        command= lambda: Threader(name='Stop-Thread'))
    playButton.pack(side=TOP)
    stopButton.pack(side=BOTTOM)
    root.mainloop()

相关问题 更多 >