Very pythonic progress dialogs.
Sometimes, you see a piece of code and it just feels right. Here's an example I found when doing my "Import Antigravity" session for PyDay Buenos Aires: the progressbar
module.
Here's an example that will teach you enough to use progressbar
effectively:
Yes, that's it, you will get a nice ASCII progress bar that goes across the terminal, supports resizing and moves as you iterate from 0 to 79.
The progressbar
module even lets you do fancier things like ETA or fie transfer speeds, all just as nicely.
Isn't that code just right? You want a progress bar for that loop? Wrap it and you have one! And of course since I am a PyQt programmer, how could I make PyQt have something as right as that?
Here'show the output looks like:
You can do this with every toolkit, and you probably should!. It has one extra feature: you can interrupt the iteration. Here's the (short) code:
# -*- coding: utf-8 -*- import sys, time from PyQt4 import QtCore, QtGui def progress(data, *args): it=iter(data) widget = QtGui.QProgressDialog(*args+(0,it.__length_hint__())) c=0 for v in it: QtCore.QCoreApplication.instance().processEvents() if widget.wasCanceled(): raise StopIteration c+=1 widget.setValue(c) yield(v) if __name__ == "__main__": app = QtGui.QApplication(sys.argv) # Do something slow for x in progress(xrange(50),"Show Progress", "Stop the madness!"): time.sleep(.2)
Have fun!
Really nice one, but I would not use __length_hint__ because, as far as I know, is not documented and could disappear in any future version. Why not len(data)?
What about killing the c variable and writing something like: widget.setValue(widget.value()+1).
I did a quick profiling and checked that cpu time is almost identical (actually a slight advanced on this last version, though I can't say why...)
Which one is more pythonic?
Best Regards
Indeed len(data) is saner!
I just got confused becaue iter(data) had no length anymore!
I prefer the explicit counter because it looks more clear to me, but it's not a big difference :-)
Wow! It took me two readings of the code to realize it was a sequence wrapper that does not modify the sequence — I have never seen one of those before! Very elegant.
Thanks! Of course it was not my idea :-)
Will it convert an iterator into a list to measure its length? E.g. in your QT example where you use xrange.
I don't think so. But how could I test it?
The way you use __length_hint__ now doesn't convert it to a list. If you change that to len(list(it)) then of course it will.
You can test it by creating a large text file (say 100mb) and using file.xreadlines to iterate over them. If the memory usage of the program stays low - it is not creating a list. If you are then it will need to allocate 100mb of memory.
You can also test it more directly by giving it an iterator with side effects.
def aniterator():
for n in range(5):
print("yielding", n)
yield n
for x in progress(aniterator()):
print("processing", x)
If you get all yields before processing, it is creating a list. If it is not, you will get alternating "yielding" and "processing" messages.
I think len(data) won't work with xrange, for example, which is probably why he is using __length__hint__. Perhaps he should default back to len(list(iter)).
Also... on hitting cancel, it seems you might want to raise an error other than StopIteration, because with StopIteration, its hard to tell the difference outside between simply hitting the end of the loop and 'cancel' being called...
Actually len(xrange(100)) does work. I am not sure why I am using __length__hint__ I wrote this in a hurry :-)
Yes, what exception to throw is a matter of taste.
Why throw an exception, rather than just returning?