【问题标题】:Correct way of using root.after and root.mainloop in Tkinter在 Tkinter 中使用 root.after 和 root.mainloop 的正确方法
【发布时间】:2015-03-31 19:32:03
【问题描述】:

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

【问题讨论】:

  • 在函数外调用主循环。
  • 可能与您的问题无关,但file.close 没有做任何事情。你需要file.close()
  • @MalikBrahimi,如果这是答案,请将其作为答案发布。
  • 如果它在 5 分钟后关闭,则 mainloop 正在运行,并且某些原因导致主窗口被破坏。
  • 我没有例外,我的代码 O.o 中的任何地方都没有类似于 root.destroy() 的内容

标签: python python-3.x tkinter


【解决方案1】:

如何使用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。看起来你一直在创建新的小部件而没有破坏旧的小部件,这是内存泄漏。最终您的程序将耗尽内存。当然,每 5 分钟只创建一个新的小部件并不令人震惊,但随着时间的推移,对于必须 24/7 运行的应用程序来说,它会随着时间的推移而增加。

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

正确关闭文件

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

【讨论】:

  • 好的,所以你的回答完全有道理,我修复了内存问题,文件关闭对我来说是一个非常愚蠢的错误。但是,如果我将 root.mainloop 移到函数之外,GUI 会在我运行它时关闭(只需双击 .py 文件),这就是我将它添加到函数中的原因。
  • 您必须致电mainloop 某处。如果你不调用它,你会得到你描述的行为。你一定是把它移到了没有被执行的地方。
  • @rodrigocf:你捕捉到异常了吗?您的代码是否破坏了主窗口?其中任何一个都会导致程序退出。
  • 我没有例外,我的代码 O.o 中的任何地方都没有类似于 root.destroy() 的内容
  • 我刚刚发现 root.quit() 会“绕过”主循环,会在我的函数开头添加它并将主循环保留在函数内给我同样的错误吗?
【解决方案2】:

在函数外调用主循环。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-11
    • 2018-12-22
    • 2015-01-21
    • 1970-01-01
    • 2014-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多