0

Okay, so I am trying to create a Tkinter GUI that is used to control Selenium processes. I want to add a label to the GUI that shows the runtime from the moment I press the start button. What I want is the runtime clock to update itself WHILE the code is running Selenium processes. The problem is that when I press the "Start" button, the runtime clock will update the first second, and will not update again until the Selenium processes are finished. Here is a minimal reproducible example:

NOTE: IF YOU RUN THIS CODE, PLEASE NOTE THE connect_driver() FUNCTION INSTALLS CHOMEDRIVER

import tkinter as tk
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager


class GUI():
    def __init__(self):
        self.root = tk.Tk()
        self.root.geometry("200x200")

        # run-time clock
        self.run_time_label = tk.Label(self.root, text="Run Time:")
        self.run_time_label.pack()
        self.run_time = tk.Label(self.root, text="00:00:00:00")
        self.run_time.pack()
        self.run_time_data = {"days":0, "hours":0, "minutes":0, "seconds":0}  

        # start button
        self.start_button = tk.Button(self.root, text="Start", bg="green", height=1, width=10, command=self.start)
        self.start_button.pack()

        self.root.mainloop()

    def start(self):
        self.update_runtime()
        self.change_state()
        self.connect_driver()
        self.work()

    def stop(self):
        driver.close()
        driver.quit()
        self.change_state()

    def update_runtime(self):
        if self.run_time_data["seconds"] < 59:
            self.run_time_data["seconds"] += 1
        elif self.run_time_data["seconds"] == 59:
            self.run_time_data["seconds"] = 0
            if self.run_time_data["minutes"] < 59:
                self.run_time_data["minutes"] += 1
            elif self.run_time_data["minutes"] == 59:
                self.run_time_data["minutes"] = 0
                if self.run_time_data["hours"] < 24:
                    self.run_time_data["hours"] += 1
                elif self.run_time_data["hours"] == 23:
                    self.run_time_data["hours"] = 0
                    self.run_time_data["days"] += 1
        
        time_string = "{:02d}:{:02d}:{:02d}:{:02d}".format(self.run_time_data["days"],self.run_time_data["hours"],self.run_time_data["minutes"],self.run_time_data["seconds"])
        self.run_time.config(text=time_string)
        self.root.after(1000, self.update_runtime)

    def change_state(self):
        if self.start_button.cget("text") == "Start":
            self.start_button.configure(text="Stop", bg="red", command=self.stop)
        elif self.start_button.cget("text") == "Stop":
            self.start_button.configure(text="Start", bg="green", command=self.start)
        self.root.update_idletasks()

    def connect_driver(self):
        global driver
        driver = webdriver.Chrome(ChromeDriverManager().install())

    def work(self):
        for _ in range(3):
            driver.get("http://www.google.com")
            driver.get("https://stackoverflow.com")
            driver.get("https://www.youtube.com")

if __name__ == "__main__":
    app = GUI()

When running this code, as you can see the runtime clock will update to 1 second after pressing "Start", and then continue to do the selenium processes but will not update the runtime clock again until the very end. So I tried to look for a solution to this and figured I would probably need to use Multiprocessing to run the update_runtime() function independently. The problem is, I am very new to Multiprocessing (obviously) and it's not immediately obvious to me how I should use it here. Here is what I tried:

import tkinter as tk
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
import multiprocessing as mp


class GUI():
    def __init__(self):
        self.root = tk.Tk()
        self.root.geometry("200x200")

        # run-time clock
        self.run_time_label = tk.Label(self.root, text="Run Time:")
        self.run_time_label.pack()
        self.run_time = tk.Label(self.root, text="00:00:00:00")
        self.run_time.pack()
        self.run_time_data = {"days":0, "hours":0, "minutes":0, "seconds":0}  

        # start button
        self.start_button = tk.Button(self.root, text="Start", bg="green", height=1, width=10, command=self.start)
        self.start_button.pack()

        self.root.mainloop()

    def start(self):
        self.process = mp.Process(target=self.update_runtime)
        self.process.start()
        self.process.join()
        self.change_state()
        self.connect_driver()
        self.work()

    def stop(self):
        driver.close()
        driver.quit()
        self.change_state()

    def update_runtime(self):
        if self.run_time_data["seconds"] < 59:
            self.run_time_data["seconds"] += 1
        elif self.run_time_data["seconds"] == 59:
            self.run_time_data["seconds"] = 0
            if self.run_time_data["minutes"] < 59:
                self.run_time_data["minutes"] += 1
            elif self.run_time_data["minutes"] == 59:
                self.run_time_data["minutes"] = 0
                if self.run_time_data["hours"] < 24:
                    self.run_time_data["hours"] += 1
                elif self.run_time_data["hours"] == 23:
                    self.run_time_data["hours"] = 0
                    self.run_time_data["days"] += 1
        
        time_string = "{:02d}:{:02d}:{:02d}:{:02d}".format(self.run_time_data["days"],self.run_time_data["hours"],self.run_time_data["minutes"],self.run_time_data["seconds"])
        self.run_time.config(text=time_string)
        self.root.after(1000, self.update_runtime)

    def change_state(self):
        if self.start_button.cget("text") == "Start":
            self.start_button.configure(text="Stop", bg="red", command=self.stop)
        elif self.start_button.cget("text") == "Stop":
            self.start_button.configure(text="Start", bg="green", command=self.start)
        self.root.update_idletasks()

    def connect_driver(self):
        global driver
        driver = webdriver.Chrome(ChromeDriverManager().install())

    def work(self):
        for _ in range(3):
            driver.get("http://www.google.com")
            driver.get("https://stackoverflow.com")
            driver.get("https://www.youtube.com")

if __name__ == "__main__":
    app = GUI() 

But when I run this, I get an error : EOFError: Ran out of input

Please please help! :)

1 Answer 1

1

Instead of using multiprocessing, try using threading as for something as simple as updating one label it will work better.

https://realpython.com/intro-to-python-threading/

This way you can create a new function just for updating the label with a loop and call it in the start() function.

Sign up to request clarification or add additional context in comments.

4 Comments

Dang I was gonna say to use Threading. But provide an example, and not just a link please?
The link provides examples and will actually explain how to use it, instead of someone just fixing your code.
@M Z G Thanks! I never even looked at Multithreading before, I always just figured it was the same as Multiprocessing. Just implemented and it works perfectly! Also, welcome to the community!
Using an external link as an answer is not recommended because the external link may be dead in the future.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.