0

I am working on a project that follows the basic structure: a sequencer class instantiates a GUI class that provides functionality to update/modify content on the GUI (change background image, update text fields, etc.). In the sequencer class, I want to kick off some method run_test that performs some actions (including some time.sleep() commands, and also updates the GUI to show that the test is running. Obviously, the GUI mainloop freezes when this occurs and the GUI does not update.

What's the right solution here? How do I kick off the run_test in another thread so that the GUI can update?

class TestSequencer():

    def __init__(self):
        self._gui = TestGUI(sequencer=self)
        self._gui.run_gui()

    def start_sequencer(self):
        self._gui.update_background_image()
        self.run_test()

    def run_test(self):
        time.sleep(5)

The result is such that the code sleeps for 5 seconds, after which the GUI updates the background image. I want it to occur immediately, and potentially on the fly as various things occur during run_test (e.g. depending on pass/fail of run_test, show different things on the GUI).

How do I accomplish this?!

  • Read [use threads to preventing main event loop from “freezing”](https://stackoverflow.com/a/16747734/7414759) and [Tkinter understanding mainloop](https://stackoverflow.com/questions/29158220/tkinter-understanding-mainloop) – stovfl Dec 04 '19 at 23:10

1 Answers1

0

I have some code here that might help you. I have already faced your problem and I figure it out with threading another class that will run my code. So unfortunately I cannot show the entire code but i tried to give you something that can help.

DELAY = 100  # millisecs between status label updates

# global flag and a Lock to control concurrent access to it
run_flag_lock = threading.Lock()
running = False

class RunningClass(threading.Thread):
    def __init__(self):
        self.run()

    def run(self):
        global running
        # random work
        YOUR CODE
        with run_flag_lock:
            running = False

class GUI(Frame, object):
    def __init__(self, master)
        super(GUI, self).__init__(master)
        global running
        self.button = Button(self, text="EXECUTE", command=lambda: self.execute(), bg="green", fg='white')
        self.button.grid(row=0, column=0, sticky=E + W)
        with run_flag_lock:
            running = False
        self.after(DELAY, self.update_status, None)  # start status widget updating

    def update_status(self, run_state):
        global running
        """ Update status label text and state of buttons to match running flag. """
        # no need to declare run_flag_lock global since it's not being assigned a value
        with run_flag_lock:
            if running != run_state:  # status change?
                if running:
                    run_state = True
                else:
                    run_state = False
            else:
                try:
                    x = self.temp
                except:
                    pass
        # run again after a delay to repeat status check
        self.after(DELAY, self.update_status, run_state)


    def execute(self):
        global running
        flag_error = False
        with run_flag_lock:
            if not running:
                self.temp = RunningClass()
                self.temp.start()
                running = True

if __name__ == '__main__':
    ROOT = Tk()
    ROOT.title("TEXT")
    LOGIN = GUI(ROOT)
    LOGIN.grid(sticky=E)
    ROOT.mainloop()
Rafael Ferreira
  • 329
  • 2
  • 16