Python 在Tkinter中使用root.after和root.mainloop的正确方法
我有一个tkinter界面,需要每5分钟自动刷新一次。到目前为止没有问题,您只需执行以下操作: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的一个自定义模块中的一行。我的自定义模块都没有使用任何循环,因此我知道错误不是直接来自
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保留在函数中会给我同样的错误吗?