Reputation: 7924
Is It possible to improve my progressbar in Tkinter-Python adding a label in the middle (ex: reading file)?
I tried to find a elegant coding solution but without a real result
from Tkinter import *
import ttk
import tkFileDialog
import time
class MainWindow(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("ProgressBar example")
self.master.minsize(200, 100)
self.grid(sticky=E+W+N+S)
top = self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
self.start_ind = Button(self, text='Start indeterminate', command=self.start_ind, activeforeground="red")
self.start_ind.grid(row=0, column=0, pady=2, padx=2, sticky=E+W+N+S)
self.pbar_ind = ttk.Progressbar(self, orient="horizontal", length=300, mode="indeterminate")
self.pbar_ind.grid(row=1, column=0, pady=2, padx=2, sticky=E+W+N+S)
def start_ind(self):
for i in xrange(50):
self.pbar_ind.step(1)
self.update()
# Busy-wait
time.sleep(0.1)
if __name__=="__main__":
d = MainWindow()
d.mainloop()
Upvotes: 3
Views: 12352
Reputation: 11
I created a solution for it, which works.
I use a label that is placed on top of the progressbar and the background of the label updates in sync with the progressbar using relwidth and the same color as the progressbar.
from threading import Thread
from tkinter import *
from tkinter import ttk
import time
#Tkinter window
class GUI(Frame, object):
def __init__(self, progress_frame):
super().__init__(progress_frame)
self.progress_frame = progress_frame
self.progress_frame.geometry('300x100')
self.progress_frame.title('Progressbar')
self.progressbar = ttk.Progressbar(self.progress_frame, orient='horizontal', mode='determinate', length=280)
# place the progressbar
self.progressbar.grid(column=0, row=1, columnspan=2, padx=10, ipady=3)
# initialize label
self.value = StringVar()
self.value.set(self.update_progress_label("0 MB/s"))
self.value_label = Label(self.progress_frame, textvariable=self.value, font='default 10', borderwidth=0)
#set background to grey
self.value_label['bg'] = '#e6e6e6'
self.value_label.grid(column=0, row=1, columnspan=2, padx=10, pady=20)
self.current_value = 0
self.start_download()
def update_progress_label(self, mb_s): #the value you want to show in the label
return f"{self.progressbar['value']}% / {mb_s}"
def start_download(self): #start thread that does calculation
download_thread = Download()
download_thread.start()
self.monitor(download_thread)
def monitor(self, download_thread): # monitor download progress
""" Monitor the download thread """
if download_thread.is_alive():
progress = download_thread.value
# update the label
self.value.set(self.update_progress_label(download_thread.mb_s))
widget_x, widget_width = self.value_label.winfo_x(), self.value_label.winfo_width() # get position and width of text label
progressbar_pixellen = self.progressbar.winfo_width() # get total width of progressbar
# get the current position in pxl of progressbar
calculation_ppixel = progress*progressbar_pixellen/100
# get the overlap with the text label
calculation_ptext = calculation_ppixel-widget_x+self.progressbar.winfo_x()
# ensure that no negative value is set
calculation_text = max(calculation_ptext/widget_width, 0)
if calculation_text>0: # if calculation_text (relwidth) is 0 it will still show a small bar, so don't update the label
# update the label with the new text value and the color of the progressbar
self.pblabel = Label(self.value_label, textvariable=self.value, font='default 10', background='#06b025', borderwidth=0, anchor="w")
# relwidth will only show the given percentage of the text in the color
self.pblabel.place(x=0, y=0, anchor="nw", relwidth=calculation_text)
# update the progressbar progress
self.progressbar['value'] = progress
# update the label
self.value_label.update_idletasks()
# rerun this method in 100ms
self.after(100, lambda: self.monitor(download_thread))
class Download(Thread):
def __init__(self):
super().__init__()
self.picture_file = None
self.value = 0
self.mb_s = 0
def run(self):
""" do some calculation like downloading a file """
for i in range(100):
time.sleep(1)
self.value = i
self.mb_s = "100 MB/s"
if __name__ == '__main__':
progress_frame = Tk()
app = GUI(progress_frame)
app.mainloop()
Upvotes: 1
Reputation: 16169
I added the label inside the progressbar by creating a custom ttk style layout. The text of the label is changed by configuring the style:
from tkinter import Tk
from tkinter.ttk import Progressbar, Style, Button
from time import sleep
root = Tk()
s = Style(root)
# add the label to the progressbar style
s.layout("LabeledProgressbar",
[('LabeledProgressbar.trough',
{'children': [('LabeledProgressbar.pbar',
{'side': 'left', 'sticky': 'ns'}),
("LabeledProgressbar.label", # label inside the bar
{"sticky": ""})],
'sticky': 'nswe'})])
p = Progressbar(root, orient="horizontal", length=300,
style="LabeledProgressbar")
p.pack()
# change the text of the progressbar,
# the trailing spaces are here to properly center the text
s.configure("LabeledProgressbar", text="0 % ")
def fct():
for i in range(1, 101):
sleep(0.1)
p.step()
s.configure("LabeledProgressbar", text="{0} % ".format(i))
root.update()
Button(root, command=fct, text="launch").pack()
root.mainloop()
Upvotes: 9
Reputation:
Have you tried creating a text Label and putting it in the same row/column and setting it the same size like so:
self.Lab = Label(self,length=200)
self.Lab.grid(row=1,column=0,pady=2,padx=2,sticky=E+W+N+S))
But you would want to put it after the progress bar widget.
Upvotes: 1