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

Python 在Tkinter中使用root.after和root.mainloop的正确方法,python,python-3.x,tkinter,Python,Python 3.x,Tkinter,我有一个tkinter界面,需要每5分钟自动刷新一次。到目前为止没有问题,您只需执行以下操作: root.after(300000, function_to_run, args_of_fun_to_run) 问题是,我必须在无限长的时间内这样做。该场景是GUI将在连接到电视的PC上运行,在我的办公室中全天候显示一些信息。这工作了大约8个小时,然后我得到以下错误: 现在,我知道回溯一直到使用matplotlib的一个自定义模块中的一行。我的自定义模块都没有使用任何循环,因此我知道错误不是直接来自

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

root.after(300000, function_to_run, args_of_fun_to_run)
问题是,我必须在无限长的时间内这样做。该场景是GUI将在连接到电视的PC上运行,在我的办公室中全天候显示一些信息。这工作了大约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吗?

在函数外部调用mainloop

在函数外部调用mainloop

如何使用mainloop和after 简言之,正确的方法是确保只调用mainloop一次,然后让周期函数在完成工作后重新调度自己:

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

root.mainloop()
移除递归 代码中的问题是,对mainloop的调用与之后调用的函数相同——您在无限循环的每次迭代中都创建了一个无限循环。这是问题的直接原因。您需要将对mainloop的调用移出maininterface函数,以便只调用一次

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

通常最好是简单地更新现有的小部件,而不是销毁和重新创建它们,但如果是这样的话,您需要做的不仅仅是调用grid\u忘记。所做的只是将它们从视图中移除,但它们仍在占用内存。如果确实要删除旧的小部件,请调用destroy方法

正确关闭文件 显然,您正试图使用file.close关闭文件,但正确的语法是file.close注意后面的括号

如何使用mainloop和after 简言之,正确的方法是确保只调用mainloop一次,然后让周期函数在完成工作后重新调度自己:

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

root.mainloop()
移除递归 代码中的问题是,对mainloop的调用与之后调用的函数相同——您在无限循环的每次迭代中都创建了一个无限循环。这是问题的直接原因。您需要将对mainloop的调用移出maininterface函数,以便只调用一次

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

通常最好是简单地更新现有的小部件,而不是销毁和重新创建它们,但如果是这样的话,您需要做的不仅仅是调用grid\u忘记。所做的只是将它们从视图中移除,但它们仍在占用内存。如果确实要删除旧的小部件,请调用destroy方法

正确关闭文件
显然,您正试图使用file.close关闭一个文件,但正确的语法是file.close注意,后面的括号

在函数外部调用mainloop。可能与您的问题无关,但file.close没有任何作用。您需要file.close。@MalikBrahimi,如果这是答案,请将其作为答案发布。如果在5分钟后关闭,则mainloop正在运行,有些东西导致主窗口被破坏。我没有得到任何异常,我的代码中甚至没有类似于root.destroy的任何地方。O调用函数外部的mainloop。可能与您的问题无关,但file.close不起任何作用。您需要file.close。@MalikBrahimi,如果这是答案,请将其作为答案发布。如果它在5分钟后关闭,mainloop正在运行,并且有东西导致主窗口被破坏。我没有得到任何异常,我的代码O.oOk中甚至没有类似于root.destroy的内容,因此您的答案完全有意义,我修复了内存问题,文件关闭是一个非常愚蠢的过程
请听我的。但是,如果我将root.mainloop移到函数外部,当我运行它时,GUI会关闭,只需双击.py文件,这就是为什么我将其添加到函数内部。您必须在某个地方调用mainloop。如果你不叫它,你会得到你描述的行为。你一定是把它移到了一个无法执行的地方。@rodrigocf:你捕获到异常了吗?你的代码正在破坏主窗口吗?这两种情况都会导致程序退出。我没有得到任何异常,我的代码中甚至没有类似于root.destroy的地方。O.oI刚刚发现root.quit会绕过主循环,在我的函数开始时添加它,并将main循环保留在函数中,会给我同样的错误吗?好吧,你的答案完全有道理,我修复了内存问题,文件关闭是我一个非常愚蠢的错误。但是,如果我将root.mainloop移到函数外部,当我运行它时,GUI会关闭,只需双击.py文件,这就是为什么我将其添加到函数内部。您必须在某个地方调用mainloop。如果你不叫它,你会得到你描述的行为。你一定是把它移到了一个无法执行的地方。@rodrigocf:你捕获到异常了吗?你的代码正在破坏主窗口吗?这两种情况都会导致程序退出。我没有得到任何异常,我的代码中甚至没有类似于root.destroy的任何地方。O.oI刚刚发现root.quit会绕过mainloop,在函数开头添加它并将mainloop保留在函数中会给我同样的错误吗?