1

I'm designing the app in tkinter, I need to read line by line one file each second and view the results on the widgets, also each second. The problem is that my program is reading the whole file and only after this update my Gui.

class Gui(Frame):
def __init__(self, master):
    super(Gui, self).__init__()
    self.master = master

    # ... here are my tkinter widgets 

    self.ser = None
    self.start_update()

def start_update(self):
    if self.ser is None:
        self.ser = open("serial.txt") # open only once
        self.updater()  

def updater(self):
    while True:
        self.raw_line = self.ser.readline()
        if self.raw_line:

            # here I update labels with .configure() method
            self.after(1000, self.updater)
        else:
            print('Probably end of file')
            self.ser.close()
            self.ser = None # can open again

root = Tk()
gui = Gui(root)
root.mainloop()
  • 3
    Remove the `while True:`, your `.after(...` is your loop. Read [While Loop Locks Application](https://stackoverflow.com/questions/28639228/python-while-loop-locks-application) – stovfl Dec 14 '19 at 20:26
  • 1
    using `while True` you run the same code many times in the same moment. `after()` doesn't stops current loop - it only send information to `mainloop` to run `updater` `1000ms` later. But after that it goes back to `while True` and it runs loop again and again. – furas Dec 14 '19 at 20:42
  • Yes, you are right! thank you, now everything works fine! – Алексей Мельников Dec 14 '19 at 22:30

1 Answers1

0

The answer is simple : you have an infinite loop in the main thread that handles the GUI. As a result, the GUI is hanging until the loop is broken.

Here's a simple fix :

class MainWindow(Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.file = None
        self.update_from_file()
        self.master.protocol('WM_DELETE_WINDOW', self.on_close)

    def open_file(self, filename="serial.txt"):
        if self.file is not None:
            self.file = open(filename)

    def update_from_file(self):
        line = self.file.readline()
        if line:
            # update the widgets
            # reminder : when applicable, use tkinter variables
            # as they are easier to handle than manually updating with .config()
        self.after(1000, self.update_from_file)

    def on_close(self):
        self.file.close()
        self.file = None # can open again

root = Tk()
gui = MainWindow(root)
root.mainloop()

This way, the update_from_file method doesn't hang the main thread while it's processing.

Another option that is valid if you need the extra CPU headroom is to create a separate thread with threading, and use that to read from the file. This won't hang the tkinter thread either, since both threads are separated. You'll have to be careful with race conditions though if you intend on editing the file as it is open.

Dogeek
  • 182
  • 9