0

I am trying to figure out what the best practice is to execute a parallel function with tkinter, that when completed, changes the GUI, without blocking the mainloop while running.

The specific case is the following, I am trying to check if a file exist, and if it does, do some checks on the content. Based on the result, I want to update the GUI to indicate the result of the check. But as the file is on a network location, it can take some time to complete the check, and I don't want to block the program while the check is performed.

I found an example at https://www.oreilly.com/library/view/python-cookbook/0596001673/ch09s07.html that I could probably make work for this purpose, but is this still a valid way of doing it, or are there better?

UPDATE: I updated the code based on the response from TheLizzard. Would this be a valid implementation? Seems to be working from what I can tell.

import threading
import tkinter as tk
import os
import time


class GUI:
    """Main GUI"""

    def __init__(self):
        """Start the GUI"""

        self.gui_root = tk.Tk()
        self.gui_root.minsize(300, 50)

        # Button to show test if GUI is responsive while checking for file
        self.button = tk.Button(self.gui_root, text="Button", command=lambda: print("text"))
        self.button.pack()

        self.label = tk.Label(self.gui_root, text="This is a label")
        self.label.pack()

        self.check_has_completed = False
        self.result = None

        new_check = threading.Thread(target=find_file, args=(self,))
        new_check.start()

        self.check_for_file()

    def check_for_file(self):
        """Check if the file exist"""
        if self.check_has_completed:
            print(f"Done with the check, result were: {self.result}")
            if self.result:
                self.label.configure(text="Found file and passed test")
            else:
                self.label.configure(text="Didn't find file, or test was not passed")
            return None
        self.gui_root.after(500, self.check_for_file)
        print(f"Response pending...")


def find_file(_gui: GUI):
    """Dummy function, check if a file exist, and if it does, do some work with it, and return result"""
    print("Check started")
    potential_path = os.path.join(os.getcwd(), "Test file.txt")
    time.sleep(5)  # Dummy to simulate processing time
    if os.path.isfile(potential_path):
        # Do some checks here, then return boolean if checks passed
        if True:
            _gui.result = True
            _gui.check_has_completed = True
            return True

    # Return false if file was found, or checks didn't pass
    _gui.result = False
    _gui.check_has_completed = True
    return False


if __name__ == '__main__':
    gui = GUI()
    gui.gui_root.mainloop()
  • 2
    In one thread (preferably the main one) have the GUI running normally with a `tkinter` loop like [this](https://stackoverflow.com/a/459131/11106801) listening for a flag that a second thread sets depending on the file search stuff. Btw it's bad practise to call `tkinter` methods from threads other than the one where you created the `tk.Tk` – TheLizzard Jan 03 '23 at 12:53
  • How would you go about starting the check in another thread, and how to best retrieve the result? I have mainly used concurrent.futures to do parallelize multiple calls to same function before, I'm new to doing separate tasks concurrently. – Another Noob Jan 03 '23 at 13:28

0 Answers0