Comment créer le téléchargement de la barre de progression TTK?
Question
Je veux montrer une barre de progression pendant le téléchargement d'un fichier à partir du Web en utilisant la méthode urllib.urlretrive
.
Comment puis-je utiliser la ttk.Progressbar
pour effectuer cette tâche?
Voici ce que je l'ai fait jusqu'à présent:
from tkinter import ttk
from tkinter import *
root = Tk()
pb = ttk.Progressbar(root, orient="horizontal", length=200, mode="determinate")
pb.pack()
pb.start()
root.mainloop()
Mais il ne cesse en boucle.
La solution
Pour le mode déterminé que vous ne voulez pas appeler start
. Au lieu de cela, il suffit de configurer le value
du widget ou appeler la méthode step
.
Si vous savez à l'avance combien d'octets que vous allez télécharger (et je suppose que vous faites depuis que vous utilisez le mode déterminé), au numéro que vous allez lire la chose la plus simple à faire est de définir l'option maxvalue
. Ensuite, chaque fois que vous lisez un morceau que vous configurez le value
pour le nombre total d'octets lus. La barre de progression sera alors déterminer le pourcentage.
Voici une simulation pour vous donner une idée approximative:
import tkinter as tk
from tkinter import ttk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.button = ttk.Button(text="start", command=self.start)
self.button.pack()
self.progress = ttk.Progressbar(self, orient="horizontal",
length=200, mode="determinate")
self.progress.pack()
self.bytes = 0
self.maxbytes = 0
def start(self):
self.progress["value"] = 0
self.maxbytes = 50000
self.progress["maximum"] = 50000
self.read_bytes()
def read_bytes(self):
'''simulate reading 500 bytes; update progress bar'''
self.bytes += 500
self.progress["value"] = self.bytes
if self.bytes < self.maxbytes:
# read more bytes after 100 ms
self.after(100, self.read_bytes)
app = SampleApp()
app.mainloop()
Pour cela fonctionne, vous allez avoir besoin pour vous assurer de ne pas bloquer le fil de l'interface graphique. Cela signifie soit que vous lisez en morceaux (comme dans l'exemple) ou faites la lecture dans un thread séparé. Si vous utilisez threads vous ne serez pas en mesure d'appeler directement les méthodes de progressbar parce tkinter est un seul thread.
Vous trouverez peut-être exemple sur progressbar tkdocs.com pour être utile.
Autres conseils
J'ai simplifié le code pour vous.
import sys
import ttk
from Tkinter import *
mGui = Tk()
mGui.geometry('450x450')
mGui.title('Hanix Downloader')
mpb = ttk.Progressbar(mGui,orient ="horizontal",length = 200, mode ="determinate")
mpb.pack()
mpb["maximum"] = 100
mpb["value"] = 50
mGui.mainloop()
Remplacer 50 avec le pourcentage du téléchargement.
Si vous voulez juste une barre de progression pour montrer que le programme est occupé / travail il suffit de changer le mode de déterminé à indéterminé
pb = ttk.Progressbar(root,orient ="horizontal",length = 200, mode ="indeterminate")
Voici un autre exemple simple qui montre également une barre de progression en mouvement. (J'ai simplifié les exemples donnés à https://gist.github.com/kochie/9f0b60384ccc1ab434eb)
import Tkinter
import ttk
root = Tkinter.Tk()
pb = ttk.Progressbar(root, orient='horizontal', mode='determinate')
pb.pack(expand=True, fill=Tkinter.BOTH, side=Tkinter.TOP)
pb.start(50)
root.mainloop()
fenêtre de Modal avec Progressbar pour le plus gros projet
Cet exemple est un peu long, mais testé sur Python 3.6 et peut être utilisé dans le plus grand projet.
# -*- coding: utf-8 -*-
# Modal dialog window with Progressbar for the bigger project
import time
import tkinter as tk
from tkinter import ttk
from tkinter import simpledialog
class MainGUI(ttk.Frame):
''' Main GUI window '''
def __init__(self, master):
''' Init main window '''
ttk.Frame.__init__(self, master=master)
self.master.title('Main GUI')
self.master.geometry('300x200')
self.lst = [
'Bushes01.png', 'Bushes02.png', 'Bushes03.png', 'Bushes04.png', 'Bushes05.png',
'Forest01.png', 'Forest02.png', 'Forest03.png', 'Forest04.png', 'Road01.png',
'Road02.png', 'Road03.png', 'Lake01.png', 'Lake02.png', 'Field01.png']
b = ttk.Button(self.master, text='Start', command=self.start_progress)
b.pack()
b.focus_set()
def start_progress(self):
''' Open modal window '''
s = ProgressWindow(self, 'MyTest', self.lst) # create progress window
self.master.wait_window(s) # display the window and wait for it to close
class ProgressWindow(simpledialog.Dialog):
def __init__(self, parent, name, lst):
''' Init progress window '''
tk.Toplevel.__init__(self, master=parent)
self.name = name
self.lst = lst
self.length = 400
#
self.create_window()
self.create_widgets()
def create_window(self):
''' Create progress window '''
self.focus_set() # set focus on the ProgressWindow
self.grab_set() # make a modal window, so all events go to the ProgressWindow
self.transient(self.master) # show only one window in the task bar
#
self.title(u'Calculate something for {}'.format(self.name))
self.resizable(False, False) # window is not resizable
# self.close gets fired when the window is destroyed
self.protocol(u'WM_DELETE_WINDOW', self.close)
# Set proper position over the parent window
dx = (self.master.master.winfo_width() >> 1) - (self.length >> 1)
dy = (self.master.master.winfo_height() >> 1) - 50
self.geometry(u'+{x}+{y}'.format(x = self.master.winfo_rootx() + dx,
y = self.master.winfo_rooty() + dy))
self.bind(u'<Escape>', self.close) # cancel progress when <Escape> key is pressed
def create_widgets(self):
''' Widgets for progress window are created here '''
self.var1 = tk.StringVar()
self.var2 = tk.StringVar()
self.num = tk.IntVar()
self.maximum = len(self.lst)
self.tmp_str = ' / ' + str(self.maximum)
#
# pady=(0,5) means margin 5 pixels to bottom and 0 to top
ttk.Label(self, textvariable=self.var1).pack(anchor='w', padx=2)
self.progress = ttk.Progressbar(self, maximum=self.maximum, orient='horizontal',
length=self.length, variable=self.num, mode='determinate')
self.progress.pack(padx=2, pady=2)
ttk.Label(self, textvariable=self.var2).pack(side='left', padx=2)
ttk.Button(self, text='Cancel', command=self.close).pack(anchor='e', padx=1, pady=(0, 1))
#
self.next()
def next(self):
''' Take next file from the list and do something with it '''
n = self.num.get()
self.do_something_with_file(n+1, self.lst[n]) # some useful operation
self.var1.set('File name: ' + self.lst[n])
n += 1
self.var2.set(str(n) + self.tmp_str)
self.num.set(n)
if n < self.maximum:
self.after(500, self.next) # call itself after some time
else:
self.close() # close window
def do_something_with_file(self, number, name):
print(number, name)
def close(self, event=None):
''' Close progress window '''
if self.progress['value'] == self.maximum:
print('Ok: process finished successfully')
else:
print('Cancel: process is cancelled')
self.master.focus_set() # put focus back to the parent window
self.destroy() # destroy progress window
root = tk.Tk()
feedback = MainGUI(root)
root.mainloop()
Je voudrais souligner quelque chose qui ne ressort pas de la simulation. Sur certains systèmes d'exploitation, os.stat () retourne 0 si un fichier vide a été ouvert en écriture jusqu'à ce que le descripteur de fichier est fermé. Cela vaincra la capacité de la barre de progression indiquant la taille progression d'un fichier téléchargé.