简体   繁体   中英

Python tkinter Pop Up Progress Bar

I have created a simple program to download from a private azure container and rename a series of .jpg files listed in a csv file. I'm still learning python so I am sure the code is a bit on the rough side! That said, the code works fine and the files download correctly. However, I would like to display a pop up progress bar showing the current progress. I have looked at a number of examples but I'm not sure how best to approach this. Could anyone offer some pointers on the best way? Thanks.

from tkinter import messagebox
import urllib.request
import csv
from datetime import datetime, timedelta
from azure.storage.blob import BlockBlobService
from azure.storage.blob.models import BlobPermissions
from azure.storage.blob.sharedaccesssignature import BlobSharedAccessSignature

account_name = '***'
account_key = '***'
top_level_container_name = '***'
blob_service = BlockBlobService(account_name, account_key)
blob_shared = BlobSharedAccessSignature(account_name, account_key)

root = Tk()
root.withdraw()

csvDir = filedialog.askopenfilename(initialdir="/", title="SELECT CSV FILE", filetypes=(("CSV files", "*.csv"), ("all files", "*.*")))
imageDir = filedialog.askdirectory()

with open(csvDir) as images:
    images = csv.reader(images)
    img_count = 1
    for image in images:
        sas = blob_shared.generate_blob(container_name=top_level_container_name, blob_name=image[0], permission=BlobPermissions.READ, start=datetime.now(), expiry=datetime.now() + timedelta(hours=1))
        sas_url = 'https://' + account_name + '.blob.core.windows.net' + '/' + top_level_container_name + '/' + image[0] + '?' + sas
        print(sas_url)
        urllib.request.urlretrieve(sas_url, imageDir + "/{}.jpg".format(image[1]))
        img_count += 1
messagebox.showinfo("Complete", "Print images have been downloaded")

root.mainloop()

The Base Code

The base code that this project uses as its baseline is Shichao's Blog Post, Progress/speed indicator for urlretrieve() in Python and my version of it is simply a tkinter wrapper is big props to Shichao for the amazing Blog Post.

The Tkinter Code

If you merely wish to see the code without the breakdown, you simply see it and copy and paste the given example:

import time
import urllib.request
import tkinter as tk
import tkinter.ttk as ttk


class download_toplevel:
    def __init__(self, master):
        self.master = master
        self.download_button = tk.Button(self.master, text="Download", command=self.download)
        self.download_button.grid(row=0, column=0)
        self.root = tk.Toplevel(self.master)

        self.progress_bar = ttk.Progressbar(self.root, orient="horizontal",
                                            length=200, mode="determinate")
        self.progress_bar.grid(row=0, column=0)
        self.progress_bar.grid_rowconfigure(0, weight=1)
        self.progress_bar.grid_columnconfigure(0, weight=1)

        self.progress_bar["maximum"] = 100

        self.root.withdraw()

# See https://blog.shichao.io/2012/10/04/progress_speed_indicator_for_urlretrieve_in_python.html
    def reporthook(self, count, block_size, total_size):
        print(count, block_size, total_size)
        if count == 0:
            self.start_time = time.time()
            return
        duration = time.time() - self.start_time
        progress_size = int(count * block_size)
        speed = int(progress_size / (1024 * duration))
        percent = min(int(count*block_size*100/total_size), 100)
        print(percent)
        self.progress_bar["value"] = percent
        self.root.update()

    def save(self, url, filename):
        urllib.request.urlretrieve(url, filename, self.reporthook)


    def download(self):
        self.progress_bar["value"] = 0
        self.root.deiconify()
        self.save("https://files02.tchspt.com/storage2/temp/discord-0.0.9.deb", "discord-0.0.9.deb")

        self.root.withdraw()

def main():
    root = tk.Tk()
    #root.withdraw()
    downloader = download_toplevel(root)
    root.mainloop()

if __name__ == '__main__':
    main()

The Breakdown

The imports

import time
import urllib.request
import tkinter as tk
import tkinter.ttk as ttk

The import of tkinter.ttk is important as the Progress Bar widget is not found in the default tkinter module.

The main loop

def main():
    root = tk.Tk()
    downloader = download_toplevel(root)
    root.mainloop()

if __name__ == '__main__':
    main()

The line if __name__ == '__main__' means if the code is run directly without being imported by another python program execute the following statement, so on you running $ python3 main.py , the code will run the main function.

The main function works by creating the main window root , and passing the window into a variable within the class download_toplevel . This is a neater way of writing python code and is more versatile when working with tkinter as it simplifies the code when looking at it in the future.

The Download Button

        self.master = master
        self.download_button = tk.Button(self.master, text="Download", command=self.download)
        self.download_button.grid(row=0, column=0)

This code adds a button labelled download bound to the command download

The Toplevel Window

self.root = tk.Toplevel(self.master)

This creates a toplevel window that appears on top of the master window

The Progress Bar

        self.progress_bar = ttk.Progressbar(self.root, orient="horizontal",
                                            length=200, mode="determinate")
        self.progress_bar.grid(row=0, column=0)
        self.progress_bar.grid_rowconfigure(0, weight=1)
        self.progress_bar.grid_columnconfigure(0, weight=1)

        self.progress_bar["maximum"] = 100

This creates a ttk.Progressbar onto the Toplevel window, oriented in the x plane, and is 200px long. The grid_*configure(0, weight=1) means that the progress bar should grow to fit the given space, to make sure this happens you should also include , sticky='nsew' in the grid command. self.progress_bar["maximum"] = 100 means that the max value is 100%.

Withdraw

self.root.withdraw()

This withdraws the Toplevel window until it is actually required, ie when the download button is pressed.

The Report Hook

def reporthook(self, count, block_size, total_size):
        print(count, block_size, total_size)
        if count == 0:
            self.start_time = time.time()
            return
        duration = time.time() - self.start_time
        progress_size = int(count * block_size)
        speed = int(progress_size / (1024 * duration))
        percent = min(int(count*block_size*100/total_size), 100)

This code is taken directly from Shichao's Blog Post, and simply works out the percentage completed.

        self.progress_bar["value"] = percent
        self.root.update()

The Progress Bar is then set to the current percentage and the Toplevel is updated, otherwise it remains as a black window (tested on Linux).

The save function

def save(self, url, filename):
        urllib.request.urlretrieve(url, filename, self.reporthook)

This bit of code has been straight up taken from the Blog Post and simply downloads the file and on each block being downloaded and written the progress is sent to the self.reporthook function.

The Download Function

def download(self):
        self.progress_bar["value"] = 0
        self.root.deiconify()
        self.save("https://files02.tchspt.com/storage2/temp/discord-0.0.9.deb", "discord-0.0.9.deb")

        self.root.withdraw()

The download function resets the Progress Bar to 0%, and then the root window is raised (outside of the withdraw). Then the save function is run, you may wish to rewrite the self.save call to read self.after(500, [the_function]) so that your main window is still updated as this program is run. Then the Toplevel window is withdrawn.

Hope this helps, James

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM