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

Correct way of using root.after and root.mainloop in Tkinter

我有一个tkinter界面需要每5分钟自动刷新一次。到目前为止没有问题,你只需要做这样的事情:

root.after(300000, function_to_run, args_of_fun_to_run)

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

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

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分钟后关闭。有谁知道为什么没有调用主循环?

在函数外调用主循环。

如何使用mainloopafter

简而言之,正确的方法是确保您准确调用 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/7.

的应用程序来说,它会累积起来

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

正确关闭文件

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