Skip to main content
 首页 » 编程设计

python之在 Tkinter 中使用 root.after 和 root.mainloop 的正确方法

2025年05月04日11jackei

我有一个 tkinter 界面,需要每 5 分钟自动刷新一次。到目前为止没有问题,您只需要执行以下操作:

root.after(300000, function_to_run, args_of_fun_to_run) 

问题是我必须在“无限”时间内执行此操作。场景是 GUI 将在连接到电视的 PC 上运行,在我的办公室全天候 24/7 显示一些信息。这工作了大约 8 个小时,然后我收到以下错误:

现在,我知道回溯一直到我的一个使用 matplotlib 的自定义模块中的一行。我的自定义模块都没有使用任何循环,所以我知道错误不是直接来自其中一个(如果我错了请纠正我),所以它一定是我无限次地重复这个函数。这是我的主要功能的代码:

from tkinter import * 
from tkinter import ttk 
import logging 
import datetime 
import sys 
 
sys.path.append(r'C:\Users\me\Desktop\seprated sla screens') 
 
import sla_main as sla 
import main_frame as mf 
import sla_grid as sg 
import incoming_volume as iv 
import outgoing_volume as ov 
import read_forecast as fc 
import get_headers as hd 
import vol_graph as vg 
import out_graph as og 
import add_graph as ag 
import sla_reduction_email as sre 
 
################################### 
################################### 
################################### 
################################### 
 
runs = 0 
 
def maininterface(f_size, pic_x, pic_y): 
 
    global runs 
    global root 
 
    start = str(datetime.datetime.now().date()) + ' ' + str(datetime.timedelta(hours=6)) 
 
    screen = sla.slamain(start) 
 
    if runs == 0: 
        root = mf.mainframe(f_size) 
 
        sg.sla_grid(screen, f_size, root) 
 
        file = open('titles in queue.txt', 'r') 
 
        in_q = file.read() 
 
        file.close 
 
        ttk.Label(root, text=in_q, anchor=CENTER, width=15, font=('times', f_size, 'bold')).grid(column=6, row=2, sticky=E) 
 
    if runs > 0: 
 
        ################################### 
        #deletes all rows before making the calculations 
        for label in root.grid_slaves(): 
            if int(label.grid_info()["row"]) > 1: 
                label.grid_forget() 
 
        sg.sla_grid(screen, f_size, root) 
 
        file = open('titles in queue.txt', 'r') 
 
        in_q = file.read() 
 
        file.close 
 
        ttk.Label(root, text=in_q, anchor=CENTER, width=15, font=('times', f_size, 'bold')).grid(column=6, row=2, sticky=E) 
 
 
 
 
    ################################### 
    #all this part is just info for the graph and the graph 
 
    incoming = iv.incomingvolume(start) 
 
    outgoing = ov.outgoingvolume(start) 
 
    forecast = fc.readforecast() 
 
    headers = hd.getheaders() 
 
    vg.volgraph(forecast, incoming, headers, pic_x, pic_y) 
 
    #og.outgraph(forecast, outgoing, headers, pic_x, pic_y) 
 
    ag.addgraph("vol_graph.png", root, 1) 
 
    #ag.addgraph("out_graph.png", root, 2) 
 
    runs = runs + 1 
 
    globals().update(locals()) 
 
    print(str(datetime.datetime.now())) 
 
    root.after(300000, maininterface, f_size, pic_x, pic_y) 
 
    root.mainloop() 
 
 
 
 
logging.basicConfig(level=logging.DEBUG, filename='error_log.txt') 
 
try: 
    maininterface(28, 23.5, 6) 
 
except: 
    logging.exception("Oops:") 

我可以在这里修改什么来避免这个错误?

谢谢!

编辑:

正如许多人所建议的那样,我已将 mainloop 调用移到 main 函数之外。我的最后几行代码现在看起来像这样:

try: 
    maininterface(28, 23.5, 6) 
    root.mainloop() 
 
except: 
    logging.exception("Oops:") 

root.after 调用保留在函数内。这样运行后,5分钟后关闭。有谁知道为什么没有调用主循环?

请您参考如下方法:

如何使用mainloop和after

简而言之,正确的方法是确保您调用 mainloop 一次,然后让您的周期函数在完成工作后重新安排自身:

root = tk.Tk() 
... 
def do_one_iteration(): 
    ... your code here ... 
    root.after(300000, do_one_iteration) 
 
root.mainloop() 

移除递归

您的代码中的问题是您在调用 after 的同一函数中调用了 mainloop - 您在每次迭代期间创建了一个无限循环你的无限循环。这是问题的直接原因。您需要将对 mainloop 的调用移出 maininterface 函数,以便它只被调用一次。

修复内存泄漏

您还需要稍微重构一下maininterface。看起来您一直在创建新的小部件而没有销毁旧的小部件,这是内存泄漏。最终你的程序会耗尽内存。当然,每五分钟只创建一个新小部件并不算过分,但对于必须全天候 24 小时运行的应用来说,它会随着时间的推移而增加。

通常最好只是更新现有的小部件,而不是销毁并重新创建它们,但如果是这样,您需要做的不仅仅是调用 grid_forget。所做的只是将它们从 View 中移除,但它们仍在占用内存。如果您真的想删除旧的小部件,请调用 destroy 方法。

正确关闭文件

您显然是在尝试使用 file.close 关闭文件,但正确的语法是 file.close()(注意尾随的括号)