5

How can I put a label in the middle of a progressbar that shows the percentage? The problem is that python doesn't support transparency for label backgrounds, so I don't know how I can solve that.

martineau
  • 119,623
  • 25
  • 170
  • 301
Kyu96
  • 1,159
  • 2
  • 17
  • 35
  • 2
    maybe helpful [How to make labels background to be transparent in Tkinter?](https://stackoverflow.com/questions/30180138/how-to-make-labels-background-to-be-transparent-in-tkinter), or [How to create transparent widgets using Tkinter?](https://stackoverflow.com/questions/17039481/how-to-create-transparent-widgets-using-tkinter) – chickity china chinese chicken Dec 19 '17 at 23:56

1 Answers1

20

This is possible using a ttk.Style. The idea is to modify the layout of the Horizontal.TProgressbar style (do the same with Vertical.TProgressbar for a vertical progressbar) to add a label inside the bar:

Usual Horizontal.TProgressbar layout:

[('Horizontal.Progressbar.trough',
  {'children': [('Horizontal.Progressbar.pbar',
     {'side': 'left', 'sticky': 'ns'})],
   'sticky': 'nswe'})]

With an additional label:

[('Horizontal.Progressbar.trough',
  {'children': [('Horizontal.Progressbar.pbar',
     {'side': 'left', 'sticky': 'ns'})],
   'sticky': 'nswe'}),
 ('Horizontal.Progressbar.label', {'sticky': 'nswe'})]

Then, the text of the label can be changed with style.configure.

Here is the code:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()

style = ttk.Style(root)
# add label in the layout
style.layout('text.Horizontal.TProgressbar', 
             [('Horizontal.Progressbar.trough',
               {'children': [('Horizontal.Progressbar.pbar',
                              {'side': 'left', 'sticky': 'ns'})],
                'sticky': 'nswe'}), 
              ('Horizontal.Progressbar.label', {'sticky': 'nswe'})])
# set initial text
style.configure('text.Horizontal.TProgressbar', text='0 %', anchor='center')
# create progressbar
variable = tk.DoubleVar(root)
pbar = ttk.Progressbar(root, style='text.Horizontal.TProgressbar', variable=variable)
pbar.pack()

def increment():
    pbar.step()  # increment progressbar 
    style.configure('text.Horizontal.TProgressbar', 
                    text='{:g} %'.format(variable.get()))  # update label
    root.after(200, increment)
    
increment()

root.mainloop()

screenshot of the result

Styling

The font, color and position of the label can be changed using style.configure. For instance,

style.configure('text.Horizontal.TProgressbar', foreground="red", 
                font='Arial 20', anchor='w')

gives screenshot of result

Multiple progressbars

The text is set through the style therefore to have multiple progressbars with different labels, one needs to use a different style for each. However, there is no need to set the layout for each style: create the layout 'text.Horizontal.TProgressbar' like in the above code and then use substyles 'pb1.text.Horizontal.TProgressbar', 'pb2.text.Horizontal.TProgressbar', ... for each progressbar. Then the text of a single progressbar can be changed with

style.configure('pb1.text.Horizontal.TProgressbar', text=...)
j_4321
  • 15,431
  • 3
  • 34
  • 61
  • Very good solution! However, there's an error on my side `TypeError: getint() argument must be str, not float` on line 23, but if I change type of `variable` to `StringVar` it works as expected! – CommonSense Dec 20 '17 at 09:12
  • @CommonSense thanks for the feedback, it does not raise an error on my computer, I guess it depends on the format of current value of the progressbar (which seems to be a float in your case). I will fix the answer. – j_4321 Dec 20 '17 at 09:16
  • Sure it works, but why you think that `StringVar` isn't an universal solution in a such case, since it used as a `text` filler? – CommonSense Dec 20 '17 at 09:27
  • 1
    @CommonSense because I want to round the result to display an integer percentage. So if I use a `StringVar`, I need to convert it to float, then round it and display it: `style.configure('text.Horizontal.TProgressbar', text='{:.0f} %'.format(float(variable.get())))` instead of `style.configure('text.Horizontal.TProgressbar', text='{:.0f} %'.format(variable.get()))`. – j_4321 Dec 20 '17 at 09:31
  • 1
    I see, it makes sense! In addition, you can switch to more universal format like `{0:g}`, which would cut off all trailing zeroes, for both cases when either `step` is a default `1.0` value or `step` is a some fraction, specified by user/program. Some kind of compromise, but surely it's a matter of taste! – CommonSense Dec 20 '17 at 09:54
  • How to make that text (%) appear on the left side of the bar? I tried changing the `'side':'left'` but that goes too left and sits on the border of the bar. Unable to read the text in that case. Any way to make it go left and give it some padding from the border? Also how to change the font, font color, etc for this text? – Meet Dec 16 '21 at 09:52
  • 1
    @Meet I have edited my answer to show how to change the position, color and font of the text. I have not found a way to add padding from the border but you can add a space on the left of the text. – j_4321 Dec 16 '21 at 15:10
  • See https://stackoverflow.com/a/70381567/6415268 for how to add padding to the left of the text. – j_4321 Dec 17 '21 at 09:29
  • If I have multiple progress bars, how do I change progress text specifically? I tried pbar.configure(text="25 %") but it gives error as - unknown option "-text". Obviously configure the style changes for all progress bars – Sandeep S D Dec 23 '21 at 06:29
  • 3
    @SandeepSD use a different style name for each progressbar. To avoid needing to configure the layout for each progressbar, just add a different prefix to `'text.Horizontal.TProgressbar'`, e.g. 'pb1.text.Horizontal.TProgressbar', for each progressbar. – j_4321 Dec 23 '21 at 10:47
  • @j_4321 Thanks. That helps! I will upto 12 progress bars in my game I am developing! – Sandeep S D Dec 24 '21 at 00:38