The WCK ProgressBar Widget
Updated September 7, 2003 | June 15, 2003 | Fredrik Lundh
This widget implements a simple progress bar, using the WCK toolkit. Call the set method with a value between 0.0 and 1.0 to set the current progress. The widget redraws itself immediately, so you don’t have to call update or update_idletasks, if you don’t want to.
from WCK import Widget, FONT, BACKGROUND import sys if sys.platform == "win32": FOREGROUND = "systemhighlight" else: FOREGROUND = "dark blue" class ProgressBar(Widget): ui_option_width = 0 # 0=autosize ui_option_height = 0 ui_option_foreground = FOREGROUND ui_option_background = BACKGROUND ui_option_font = FONT ui_option_padx = 2 ui_option_pady = 2 ui_option_relief = "sunken" ui_option_borderwidth = 2 def __init__(self, master, **options): self.value = 0 # current value (0-100) self.ui_init(master, options) def ui_handle_config(self): # calculate default size w = int(self.ui_option_width) if w <= 0: w = 100 h = int(self.ui_option_height) if h <= 0: f = self.ui_font(self.ui_option_foreground, self.ui_option_font) h = f.height w = w + 2*int(self.ui_option_padx) h = h + 2*int(self.ui_option_pady) return w, h def ui_handle_clear(self, draw, x0, y0, x1, y1): pass def ui_handle_repair(self, draw, x0, y0, x1, y1): w, h = self.ui_size() bar = int(w*self.value) / 100 draw.rectangle( (0, 0, bar, h), self.ui_brush(self.ui_option_foreground) ) draw.rectangle( (bar, 0, w, h), self.ui_brush(self.ui_option_background) ) def set(self, value): "Set a new progress value (0.0 to 1.0, wrapping)" value = int(value * 100 + 0.5) if value < 0 or value > 100: value = value % 100 self.value = value # update immediately self.ui_handle_repair(self.ui_draw, 0, 0, 0, 0) def get(self): "Get the current progress value" return self.value / 100.0
Usage
Create the progress bar as usual. To update the bar, call the set method with a floating point value between 0.0 and 1.0 (100%).
progress = ProgressBar(master) progress.pack() count = 1000 for i in range(count): # do something... progress.set(float(i+1) / count)
Displaying a Progress Percentage
Here’s an enhanced version of the ProgressBar widget. This version displays the current progress over the bar, using contrasting colors.
from WCK import Widget, FONT, BACKGROUND import sys if sys.platform == "win32": FOREGROUND = "systemhighlight" else: FOREGROUND = "dark blue" class ProgressBar(Widget): ui_option_width = 0 # 0=autosize ui_option_height = 0 ui_option_format = "%d%%" # None or empty=no label ui_option_foreground = FOREGROUND ui_option_background = BACKGROUND ui_option_font = FONT ui_option_padx = 2 ui_option_pady = 2 ui_option_relief = "sunken" ui_option_borderwidth = 2 def __init__(self, master, **options): self.value = 0 # current value (0-100) self.size = None self.ui_init(master, options) def ui_handle_config(self): # calculate widget size w = int(self.ui_option_width) if w <= 0: w = 100 h = int(self.ui_option_height) if h <= 0: f = self.ui_font(self.ui_option_foreground, self.ui_option_font) h = f.height w = w + 2*int(self.ui_option_padx) h = h + 2*int(self.ui_option_pady) return w, h def ui_handle_resize(self, width, height): self.size = width, height def ui_handle_clear(self, draw, x0, y0, x1, y1): pass def ui_handle_repair(self, draw, x0, y0, x1, y1): if not self.size: return w, h = self.size bar = int(w*self.value) / 100 try: text = self.ui_option_format % int(self.value) except TypeError: # malformed format string: use a reasonable default text = str(int(self.value)) + "%" if text: # calculate text geometry fgfont = self.ui_font( self.ui_option_foreground, self.ui_option_font ) bgfont = self.ui_font( self.ui_option_background, self.ui_option_font ) textwidth, textheight = fgfont.measure(text) tx0 = (w - textwidth) / 2 tx1 = tx0 + textwidth ty0 = (h - textheight) / 2 else: tx0 = tx1 = w # no text fg = self.ui_brush(self.ui_option_foreground) bg = self.ui_brush(self.ui_option_background) # draw the part to the left of the text if bar < tx0: draw.rectangle((0, 0, bar, h), fg) draw.rectangle((bar, 0, tx0, h), bg) else: draw.rectangle((0, 0, tx0, h), fg) if tx0 >= w: return # nothing left to draw # draw the text label, using a pixmap if tx0 < tx1: label = self.ui_pixmap(tx1 - tx0, h) if bar <= tx1: label.rectangle((0, 0, tx1 - tx0, h), bg) label.text((0, ty0), text, fgfont) if bar > tx0: # text covers end of bar bglabel = self.ui_pixmap(bar - tx0, h) bglabel.rectangle((0, 0, bar - tx0, h), fg) bglabel.text((0, ty0), text, bgfont) label.paste(bglabel) else: label.rectangle((0, 0, tx1 - tx0, h), fg) label.text((0, ty0), text, bgfont) draw.paste(label, (tx0, 0)) # draw the part to the right of the text if bar > tx1: draw.rectangle((tx1, 0, bar, h), fg) draw.rectangle((bar, 0, w, h), bg) else: draw.rectangle((tx1, 0, w, h), bg) def set(self, value): "Set a new progress value (0.0 to 1.0, wrapping)" value = int(value * 100 + 0.5) if value < 0 or value > 100: value = value % 100 self.value = value # update immediately self.ui_handle_repair(self.ui_draw, 0, 0, 0, 0) def get(self): "Get the current progress value" return self.value / 100.0
The ui_handle_repair function contains a lot of code, but most of that code is there to draw the text label on top of the bar, and to do that without flicker. The widget solves this by drawing the progress bar in three steps:
- The portions to the left of the text label.
- The text label. This is drawn into a pixmap, which is copied to the screen when done. If the label covers the end of the progress bar, we’ll use an extra pixmap in order to draw different parts of the label in different colors.
- The portions to the right of the text label.