Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
625 views
in Technique[技术] by (71.8m points)

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

I have a tkinter interface that needs to be automatically refreshed every 5 minutes. So far there is no problem, you would just have to do something like:

root.after(300000, function_to_run, args_of_fun_to_run)

The problem is that I have to do this for an "infinite" amount of time. The scenario is that the GUI will running on a PC attached to a TV displaying some info 24/7 in my office. This works for around 8 hours and then I get the following error:

enter image description here

Now, I know that the traceback goes all the way to a line in one of my custom modules that uses matplotlib. None of my custom modules uses any loops so I knw that the error does not directly come from one of them (please correct me if I'm wrong), so it must be from me repeating the function an infinite amount of time. This is the code from my main function:

from tkinter import *
from tkinter import ttk
import logging
import datetime
import sys

sys.path.append(r'C:UsersmeDesktopseprated 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:")

What can I modify here to avoid this error??

Thanks!

EDIT:

As many have suggested, I have moved the mainloop call outside of the main function. The las few lines of my code look like this now:

try:
    maininterface(28, 23.5, 6)
    root.mainloop()

except:
    logging.exception("Oops:")

The root.after call remains inside the function. After running it like this, it closes after 5 minutes. Does anyone know why the mainloop is not being called?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

How to use mainloop and after

In short, the correct way is to make sure you call mainloop precisely once, and then have your periodic function reschedule itself when it's done working:

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

root.mainloop()

Remove the recursion

The problem in your code is that you have the call to mainloop in the same function that calls after -- you're creating an infinite loop during each iteration of your infinite loop. This is the immediate cause of the problem. You need to move the call to mainloop out of the maininterface function so that it is only called once.

Fix the memory leak

You also need to refactor maininterface a bit. It appears you keep creating new widgets without ever destroying old widgets, which is a memory leak. Eventually your program will run out of memory. Of course, creating just one new widget every five minutes isn't egregious, but it will add up over time for an app that must run 24/7.

It's usually best to simply update existing widgets rather than to destroy and recreate them, but if you are, you need to do more than just call grid_forget. All that does is remove them from view, but they are still taking up memory. If you truly want to delete old widgets, call the destroy method.

Close your file properly

You are apparently attempting to close a file with file.close, but the proper syntax is file.close() (note the trailing parenthesis)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...