如何在tkinter后台运行函数

12

我刚接触GUI编程,想用tkinter写一个Python程序。我只想让它在后台运行一个简单的函数,并通过GUI进行干预。

该函数从0一直计数到无穷大,直到按下按钮为止。至少这是我的愿望。但我不知道如何在后台运行此函数,因为tkinter的主循环(mainloop())始终控制着程序。如果我在一个无限循环中启动该函数,主循环(mainloop())无法执行,GUI也会卡住。

我希望在每个周期后把控制权返回给主循环(mainloop()),但是如何在没有用户触发事件的情况下将控制权从主循环(mainloop())返回到runapp函数呢?

下面是一些导致GUI无响应的示例代码:

from Tkinter import *

class App:
    def __init__(self, master):

        frame = Frame(master)
        frame.pack()

        self.button = Button(frame, text="START", command=self.runapp)
        self.button.pack(side=LEFT)

        self.hi_there = Button(frame, text="RESTART", command=self.restart)
        self.hi_there.pack(side=LEFT)

        self.runapp()

    def restart(self):
        print "Now we are restarting..."

    def runapp(self):
        counter = 0
        while (1):
            counter =+ 1
            time.sleep(0.1)
5个回答

12

基于事件的编程在概念上很简单。只需想象你的程序文件末尾是一个简单的无限循环:

while <we have not been told to exit>:
    <pull an event off of the queue>
    <process the event>

因此,如果您想要持续运行一些小任务,您只需要将其拆分成易于处理的小块,并将这些小块放置在事件队列中。每次通过循环时,计算的下一个迭代将自动执行。

您可以使用after方法将对象放置在事件队列中。因此,创建一个方法来增加数字,然后重新安排它在几毫秒后运行。看起来应该像这样:

def add_one(self):
    self.counter += 1
    self.after(1000, self.add_one)

上述代码每秒钟更新一次计数器。在程序初始化时,您调用它一次,之后它会自我调用,反复执行等等。

只有当您能够将大问题(在本例中为“永久计数”)拆分为小步骤(例如“加一”)时,此方法才有效。如果您正在执行类似于慢速数据库查询或大量计算的操作,则此技术不一定有效。


会不会导致堆栈溢出?调用自身的函数会在内存中保留调用者函数,进行递归操作。 - Berry Tsakala
@BerryTsakala:不是的,因为这不是一个调用自身的函数。它是一个将自己添加到外部队列的函数。当前函数返回,因此每次调用时都会从堆栈中删除它。 - Bryan Oakley

4
尝试理解这个例子:在后台更新时钟并更新GUI(无需2个线程)。
# use Tkinter to show a digital clock
# tested with Python24    vegaseat    10sep2006
from Tkinter import *
import time
root = Tk()
time1 = ''
clock = Label(root, font=('times', 20, 'bold'), bg='green')
clock.pack(fill=BOTH, expand=1)
def tick():
    global time1
    # get the current local time from the PC
    time2 = time.strftime('%H:%M:%S')
    # if time string has changed, update it
    if time2 != time1:
        time1 = time2
        clock.config(text=time2)
    # calls itself every 200 milliseconds
    # to update the time display as needed
    # could use >200 ms, but display gets jerky
    clock.after(200, tick)
tick()
root.mainloop(  )

credits: link to site


4

2
对于这么简单的程序,绝对没有必要使用线程。那就像用电锯杀蚂蚁一样。 - Bryan Oakley

2

我没有足够的声望来评论Bryan Oakley的答案(我发现它对我的程序非常有效),所以我会在这里添加我的经验。我发现,根据您的后台函数运行时间长短和您想要的时间间隔精度,将self.after调用放在重复函数的开头可能更好。在Bryan的示例中,看起来像是:

def add_one(self):
    self.after(1000, self.add_one)
    self.counter += 1

这样做可以确保时间间隔完全准确,避免了因函数执行时间过长而导致的时间间隔漂移。


1
为了避免漂移,您可以使用定时器锁定延迟。如何在Python中定期运行函数 - jfs

0
如果您不想与这些线程分离,我想为您的GUI提供一个建议-将GUI函数放置在root.mainloop()语句之前。
例如-
root = tk.Tk()
.
.
graphicsfunction()     #function for triggering the graphics or any other background 
                       #function
root.mainloop()

请点赞如果您喜欢。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接