Diálogo de progreso muy pythónico
Este es un ejemplo que te muestra suficiente para usar progressbar
:
Sí, eso es todo, tenés una linda barra de progreso ASCII que cruza la terminal, soporta que la cambies de tamaño y se mueve mientras iterás de 0 a 79.
El módulo progressbar
incluso tiene cosas mejores como ETA o velocidades de transferencia, y todo es así de fácil.
¿Ese código... no está bueno? ¿Querés una barra de progreso para ese loop? ¡Lo "envolvés" y listo! Y por supuesto, como yo programo con PyQt, quiero que PyQt tenga algo igual de bueno.
Así se ve el resultado:
Esto lo podés hacer con cualquier toolkit, y probablemente deberías. Tiene un feature extra: podés interrumpir la iteración, y este es el (poco) código:
# -*- 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)
Diviértanse!
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?