0

I am trying to create a tkinter GUI for a script which performs some task. The task is triggered by clicking a start button, and I would like to add a dynamic label showing that the task is "in progress" by displaying: "Working." → "Working.." → "Working..."

I referred to this post and wrote the following script. Here I used a progress bar to represent my "task", and was expecting the status label to change (as stated above) WHILE the progress bar is updating.

import tkinter as tk

class UI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title('Hello World')

        self.prog_Label = tk.Label(self.root, text='Progress')
        self.prog_Label.grid(row=0, column=0, sticky=tk.W, padx=20, pady=(10, 0))

        self.prog_Bar = tk.ttk.Progressbar(self.root)
        self.prog_Bar.configure(value=0, mode='determinate', orient='horizontal')

        self.prog_Bar.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=20, pady=5)

        self.exe_Btn = tk.Button(self.root, text='Start', padx=15, command=self.run, relief='groove')
        self.exe_Btn.grid(row=2, column=0, padx=80, pady=(40, 20), sticky=tk.E)

        self.prog_Label = tk.Label(self.root, text='Status:-')
        self.prog_Label.grid(row=3, column=0, sticky=tk.W, padx=20, pady=10)

        self.root.mainloop()

    def run(self):
        self.update_status('Working')
        n = 0
        self.prog_Bar.configure(value=n, maximum=100000, mode='determinate', orient='horizontal')
        for i in range(100000):
            n += 1
            self.prog_Bar.configure(value=n)
            self.prog_Bar.update_idletasks()

    def update_status(self, status=None):
        if status is not None:
            current_status = 'Status: ' + status
        else:
            current_status = self.prog_Label['text']
            if current_status.endswith('...'):
                current_status = current_status.replace('...', '')
            else:
                current_status += '.'

        self.prog_Label['text'] = current_status
        self.prog_Label.update_idletasks()
        self._status = self.root.after(1000, self.update_status)

if __name__ == '__main__':
    ui = UI()

However, the program behaves in a way that, when the start button is clicked, although the status label changes from '-' to 'Working' immediately, it only starts to add the dots after the progress bar reaches the end.

Is there a way to modify it so as to achieve my purpose?

jiao
  • 45
  • 1
  • 7

1 Answers1

1

I changed your structure a little so your task is now in its own class, instead of sleeping you would perform the task there. I added a thread for the task as i assumed that it would need its own process, this stops the application freezing as it would block the main UI loop.

import threading
import time

import tkinter as tk
import tkinter.ttk as ttk

class Task:
    def __init__(self):
        self.percent_done = 0

        threading.Thread(target = self.run).start()

    def run(self):
        while self.percent_done < 1:
            self.percent_done += 0.1

            # Do your task here

            time.sleep(0.5)

        self.percent_done = 1


class Application():
    def __init__(self):
        self.root = tk.Tk()

        self.root.title("Window Title")

        self.task = None
        self.label_dots = 0

        self.prog_bar = tk.ttk.Progressbar(self.root)
        self.prog_bar.configure(value=0, maximum=100, mode='determinate', orient='horizontal')

        self.prog_bar.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=20, pady=5)

        self.run_btn = tk.Button(self.root, text='Start', padx=15, command=self.start_task, relief='groove')
        self.run_btn.grid(row=2, column=0, padx=80, pady=(40, 20), sticky=tk.E)

        self.prog_label = tk.Label(self.root, text='Status: -')
        self.prog_label.grid(row=3, column=0, sticky=tk.W, padx=20, pady=10)

    def start_task(self):       
        self.task = Task()

        self.update_ui()


    def update_ui(self):
        # Percent is between 0 and 1 
        if 0 < self.task.percent_done < 1:
            status = "Working"

            self.label_dots += 1
            self.label_dots = self.label_dots % 4
        else:
            status = "Finished"

            self.label_dots = 0

        self.prog_bar.configure(value=self.task.percent_done * 100)

        label_text = "Status: - " + status + ("." * self.label_dots)

        self.prog_label.config(text = label_text)

        if status != "Finished":
            self.root.after(1000, self.update_ui)           


Application().root.mainloop()
Joshua Nixon
  • 1,379
  • 2
  • 12
  • 25
  • I've just tried your solution and it worked perfectly as what I wanted. Thank you so much! – jiao Jun 10 '19 at 02:47