Update the text widget of Tkinter as it is written, and not after the end of the class.

I am bound since it is written on a secret machine, I cannot copy + paste here. As a somewhat newbie, my approach is probably unorthodox.

I have a GUI written in Tkinter with a few buttons. Each button is associated with a class that essentially runs a short script. When the button is clicked, I initialize the log_window class, which is just a Tkinter text widget. Then I create a global variable that binds log with log_window , which I just created, and as the script starts I pipe sys.stdout/stderr to log (for this I created a write method). Everything is kosher, except that the log_window text widget log_window not updated with my standard stdout until the class that calls it is complete. However, if I just print inside the class, it will print in the order in which it is called.

Example

 import Tkinter from Tkinter import * import time class log_window: def __init__(self,master): self.textframe = Tkinter.Frame(master) self.text = Text(self.textframe) self.text.pack() self.textframe.pack() def write(self,text): self.text.insert(END,text) class some_func1: # This effectively waits 5 seconds then prints both lines at once def __init__(self,master): log.write("some text") time.sleep(5) log.write("some text") class some_func2: # This prints the first object, waits 5 seconds, then prints the second def __init__(self,master): print "some text" time.sleep(5) print "some text" if __name__ == '__main__': global log root = Tk() log = log_window(root) root.after(100,some_func1, root) root.after(100,some_func2, root) root.mainloop() 

Sorry if my example is a bit confusing, but I think this speaks to the point. I do this through Popen and some system calls, but they are not part of the problem, so I just emphasized that, I suppose, is the LCD of the problem.

+4
source share
2 answers

I don’t know the details of Tkinter concurrency, but messing around shows that if you put

 master.update_idletasks() 

after each call to log.write , it is updated by command. You could provide a log .flush() method for this (for example, with files), or you can simply make log.write call it after writing.

+6
source

When you call sleep , it makes your whole GUI freeze. You must remember that your GUI fires an event loop, which is an infinite loop that wraps all your code. The event loop is responsible for the fact that widgets will be redrawn when they change. When a binding interception occurs, it calls your code from this loop, so while your code is running, the event loop cannot be completed.

You have several options. One of them is to call update_idletasks after adding text to the widget. This allows the event loop service to β€œidle” - events that they plan to execute when the program does nothing. Screen reconfiguration is one such event, and there are others.

Another option is to run your functions in a thread or in a separate process. Since Tkinter is not thread safe, these other threads or processes cannot interact directly with the GUI. What they have to do is push the message into the queue, and then your main (GUI) thread should poll the queue and turn off the messages. It would be easy to create this code in your log class, and queue polling can be done using an event loop - just write a method that pops messages from the queue and inserts them into the widget, the calls themselves use after a few hundred milliseconds later.

+1
source

All Articles