1

I have a Python script that has some processes that take some time such as creating a .shp file and opening an .mxd. I've read Python Progress Bar but I would like to avoid using a external package. So far I have;

    from tkinter import *
from tkinter.ttk import *
import os, arcpy

tk=Tk()
progress=Progressbar(tk,orient=HORIZONTAL,length=100,mode='determinate')

def bar():
    import time
    os.startfile("Assignment.mxd")
    progress['value']=20
    tk.update_idletasks()
    time.sleep(8)
    progress['value']=50
    tk.update_idletasks()
    time.sleep(8)
    progress['value']=80
    tk.update_idletasks()
    time.sleep(8)
    progress['value']=100


progress.pack()
Button(tk,text='Open',command=bar).pack()
mainloop()

It seems to be working but I don't think the process to open the .mxd is paying attention to what stage the progress bar is at. I would like to get it so that the process finishing coincides with the progress bar reaching 100%. Is there away to get the process to 'wait' for the progress bar? Could anyone offer some pointers on how I might get this to work? Thanks

Dakan_GIS
  • 61
  • 8

1 Answers1

0

The problem is that the sleep function and possibly any other blocking function you call in your bar function blocks the GUI. A solution could be pushing the actions to a worker thread. A very quick solution I can come up with is this:

from random import randint
from threading import Thread
from tkinter import Tk, DoubleVar, HORIZONTAL
from tkinter.ttk import Frame, Button, Progressbar
from time import sleep

class Worker(Thread):
    def __init__(self, reference):
        super().__init__()
        self.reference = reference

    def run(self):
        print("running...")
        # replace this dummy loop with actual processing tasks
        while self.reference.get_progress() < 100:
            duration = randint(1, 3)
            sleep(duration)
            self.reference.add_progress(10 * duration)
        print("finished.")

class Example(Frame):
    def __init__(self, root):
        super().__init__(master=root)
        self.progress = DoubleVar(value=0.0)
        self.worker = Worker(reference=self)
        self.startbutton = Button(master=self, text="Start", command=self.start)
        self.startbutton.pack()
        self.progressbar = Progressbar(master=self, orient=HORIZONTAL, length=100, mode='determinate', variable=self.progress)
        self.progressbar.pack()
        self.infobutton = Button(master=self, text="Info", command=self.info)
        self.infobutton.pack()
        self.pack()

    def get_progress(self):
        return self.progress.get()

    def set_progress(self, value):
        self.progress.set(value)

    def add_progress(self, value):
        self.progress.set(self.progress.get() + value)

    def start(self):
        # print("sleeping...")
        # sleep(10)  # uncomment this to block the GUI
        print("starting...")
        self.worker.start()

    def info(self):
        print(self.worker.is_alive())

def main():
    root = Tk()
    app = Example(root)
    app.mainloop()

if __name__ == '__main__':
    main()

Note that the info button works before, while and after the thread runs. One problem with this quick solution is that the thread can only be run once. To run it multiple times, some kind of resetting needs to be implemented.

Lukisn
  • 190
  • 8
  • Thanks for that Lukisn but I'm getting this error; TypeError: super() takes at least 1 argument (0 given) – Dakan_GIS Apr 21 '18 at 16:04
  • Python 2? My code is Python 3. Try Frame.__init__(...) and Thread.__init__. – Lukisn Apr 21 '18 at 16:07
  • Hi Lukisn yes I'm using Python 2.7.13. Do you mean - class Example(Frame): def Frame__init__(self, root): ? Sorry, I still very much a beginner on Python. – Dakan_GIS Apr 21 '18 at 18:58
  • Thread.__init__(self) ind Worker class, Frame.__init__(self, master=root) in Example class. – Lukisn Apr 21 '18 at 20:05
  • Thanks for the help Lukisn but I going to try and get this code working; https://stackoverflow.com/questions/25202147/tkinter-progressbar-with-indeterminate-duration?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa. The example here is a bit too complicated for me. Thanks anyway. – Dakan_GIS Apr 22 '18 at 08:39
  • Ok nice. Maybe post your solution here as answer. I would like to see how you solved it. – Lukisn Apr 22 '18 at 18:36