Search code examples
pythontkintertkinter-text

updating text after running mainloop in tkinter


I want to change the text displaying in frame after my mainloop() has been called. I have created loginfo function to append text in my string but nothing happens. The GUI gets started and displays the text originally contained in it("hi"), I don't see the text I add through loginfo function ("hello") and after exiting the GUI I get the below error.

Traceback (most recent call last):
  File "1.py", line 5, in <module>
    monitor.loginfo()
  File "/home/shreyas/Desktop/test/main.py", line 45, in loginfo
    self.text.configure(state='normal')
  File "/usr/lib/python3.8/tkinter/__init__.py", line 1637, in configure
    return self._configure('configure', cnf, kw)
  File "/usr/lib/python3.8/tkinter/__init__.py", line 1627, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!frame.!text"

My task is to create a function that i will call any time with the text i want to insert. The function will be called after the mainloop is running as i recieve the text to display.

These are the 2 files I created:

main.py

import tkinter
from tkinter import *

class Monitor:
    def __init__(self):
        self.root = Tk()
        self.root.title('Monitor')
        self.root.geometry("800x400")
        self.root.grid_columnconfigure((0,1), weight=1)
        self.root.grid_rowconfigure(0, weight=1)

        """-----------------------------------------------"""
        self.console = Frame(self.root,borderwidth=1)
        self.console.grid(row = 0, column = 0, sticky = W+E+N+S)

        self.console.grid_columnconfigure(0, weight=1)
        self.console.grid_rowconfigure(2, weight=1)

        self.lbl_c = Label(self.console, text="console",bg='white')
        self.lbl_c.grid(row = 1, column = 0, sticky = W+E+N+S)

        self.text = tkinter.Text(self.console)
        self.text.grid(row = 2, column = 0,rowspan = 3, columnspan = 1, sticky = N+S+E+W)
        self.text.insert(tkinter.END,"hi")
        self.text.configure(state='disabled')
        """------------------------------------------------"""
        self.fm = Frame(self.root,borderwidth=1)
        self.fm.grid(row = 0, column = 1, sticky = W+E+N+S)

        self.fm.grid_columnconfigure(0, weight=1)
        self.fm.grid_rowconfigure(2, weight=1)

        self.lbl_fm = Label(self.fm, text="frequency_monitor",bg='white')
        self.lbl_fm.grid(row = 1, column = 0, sticky = W+E+N+S)

        self.text1 = tkinter.Text(self.fm)
        self.text1.grid(row = 2, column = 0,rowspan = 1, columnspan = 1, sticky = N+S+E+W)
        self.text1.insert(tkinter.END,"<---------- Frequency Monitor ---------->\n\n"+"Camera100\n"+"Frequency: 9.6 CPU Time: 3.0ms\n"+("----------------------------------------")+"Screen100\n"+"Frequency: 29.8 CPU Time: 6.0ms\n"+("----------------------------------------"))
        self.text1.configure(state='disabled')
        


    def loginfo(self):
        self.text.configure(state='normal')
        self.text.insert(tkinter.END,"hello")
        self.text.update()
        self.text.configure(state='disabled')

1.py

import main as m

monitor = m.Monitor()
monitor.root.mainloop()
monitor.loginfo()

I use python 3.1 to run my code. Can someone please tell me what's causing the error and how could I achieve the expected result.

update: when i use mainloop() like so

import main as m

monitor = m.Monitor()
monitor.root.mainloop()
monitor.root.update()
monitor.root.update_idletasks()
monitor.loginfo()

i get the same error but when i use while

import main as m

monitor = m.Monitor()
#monitor.root.mainloop()
#monitor.loginfo()


while True:
    monitor.root.update()
    monitor.root.update_idletasks()
    monitor.loginfo()

it updates text and keeps updating it since i called loginfo in while But it doesnot update if i call it outside the while loop.


Solution

  • The code after mainloop() gets called only after your applications is closed. So after the application is closed, the method is called, but the widgets used in the method is destroyed. So change your code to:

    monitor = Monitor()
    monitor.loginfo()
    monitor.root.mainloop()
    

    This way, the function is called before you exit the GUI. Think of mainloop() as a while loop that keeps updating till the window is closed. Technically saying mainloop() is same as:

    while True: # Only exits, because update cannot be used on a destroyed application
        root.update()
        root.update_idletasks()
    

    Edit: Since you wanted a delay, you have to add a button or something that calls this method while the GUI is active, an example is to use after to show the function after some time, like:

    monitor = Monitor()
    
    monitor.root.after(1000,monitor.loginfo) # Cause a delay of 1 second or 1000 millisecond 
    
    monitor.root.mainloop()