A long time ago, I posted an ASCII-art download meter in Perl to kragen-hacks. It drew a little progress bar in equals signs and estimated what time the file would finish downloading. Here's a version with Python and Tkinter.
It's useful even when iconified, at least if you're using a window manager that displays the iconname and keeps it up to date. First, progbar.py, which is imported by the download-meter program: """A crude progress bar for Tkinter. """ import Tkinter class Progress: def __init__(self, parentwidget, width=300, height=22): self.width = width self.frame = Tkinter.Frame(parentwidget, relief='sunken', borderwidth=2, height=height) self.foreground = Tkinter.Frame(self.frame, relief='raised', borderwidth=2, height=height) self.background = Tkinter.Frame(self.frame, background='#555588', height=height) self.foreground.pack(side='left', fill='both', expand=1) self.fullness = 0 self.redraw() def redraw(self): # When I didn't pack_forget the background, it would never shrink # below three pixels (unless the progress bar was constrained # in size). So every time the thing got full, it would grow by # three pixels. Likewise, when I didn't pack_forget the foreground, # it would never shrink below one pixel, so every time the thing got # empty, it would grow by one pixel. if self.winfo_ismapped(): if self.background.winfo_ismapped(): oldbgwidth = self.background.winfo_width() else: oldbgwidth = 0 if self.foreground.winfo_ismapped(): oldfgwidth = self.foreground.winfo_width() else: oldfgwidth = 0 width = (oldfgwidth + oldbgwidth) else: width = self.width fgwidth = int(self.fullness * width + 0.5) bgwidth = width - fgwidth if bgwidth: self.background.pack(side='left', fill='both', expand=1) self.background.configure(width=bgwidth) else: self.background.pack_forget() if fgwidth: if bgwidth: before={'before': self.background} else: before = {} self.foreground.pack(before, side='left', fill='both', expand=1) self.foreground.configure(width=fgwidth) else: self.foreground.pack_forget() self.frame.update() def setfullness(self, fullness): self.fullness = fullness self.redraw() def __getattr__(self, name): return getattr(self.frame, name) Here's the download meter program itself. #!/usr/bin/env python """Draws a progress bar. Some programs that download stuff have progress bars that tell you how much of the file is downloaded, how much longer you can expect it to take, etc. But some don't, and none of them tell me what time they expect it to finish. (I have to calculate that myself.) So here's a program to solve that problem. Point it at a file, tell it how big you expect the file to be when it is fully downloaded, and it will watch the file and linearly extrapolate when it thinks the file will finish downloading, as well as drawing you a cute progress bar. """ # Make emacs happy: ' import progbar, Tkinter, sys, os.path, time def usage(argv0): sys.stderr.write("%s: Usage: %s filename expectedsize\n" % (argv0, argv0)) return -1 class downloadmeter: def __init__(self, window, filename, ultimatesize, donecb): self.window = window self.filename = filename self.ultimatesize = ultimatesize self.origsize = None self.starttime = None self.pb = progbar.Progress(window) self.donecb = donecb frame = Tkinter.Frame(window) self.fnlabel, self.sizelabel, self.percentlabel, self.etalabel = [ Tkinter.Label(frame, text=x) for x in (filename, '', '', 'ETA unknown')] for x in self.pb, frame: x.pack(side='top', fill='both') frame.pack(expand=1) for x in frame.winfo_children(): x.pack(side='left', expand=1, fill='both') self.update() def update(self): # reschedule ourself self.window.after(200, self.update) try: size = os.path.getsize(self.filename) except OSError, v: # file doesn't exist self.sizelabel.configure(text=v) return now = time.time() if self.origsize is None: self.origsize = size self.starttime = now if size >= self.ultimatesize: self.donecb() self.sizelabel.configure(text=str(size)) fullness = size/float(self.ultimatesize) self.pb.setfullness(fullness) percent = "%d%%" % int(fullness * 100) self.percentlabel.configure(text=percent) # if we're in a toplevel window, set its icon name try: self.window.wm_iconname("%s %s" % (percent, self.filename)) except AttributeError: pass # but if not, that's OK if now > self.starttime: # rate in bytes per second slope = (size - self.origsize) / (now - self.starttime) if slope > 0: timetowait = (self.ultimatesize - size) / slope eta = now + timetowait _, _, _, h, m, s, _, _, _ = time.localtime(eta) self.etalabel.configure(text="ETA %02d:%02d:%02d" % (h, m, s)) def main(argv): if len(argv) != 3: sys.exit(usage(argv[0])) w = Tkinter.Tk() w.wm_title("%s download meter" % argv[1]) dm = downloadmeter(w, argv[1], int(argv[2]), lambda: sys.exit(0)) w.mainloop() if __name__ == "__main__": main(sys.argv)