Merge branch 'progress-observer' of git://github.com/aheadley/Minecraft-Overviewer into progress-observer
This commit is contained in:
@@ -188,6 +188,21 @@ the form ``key = value``. Two items take a different form:, ``worlds`` and
|
||||
|
||||
processes = 2
|
||||
|
||||
.. _observer:
|
||||
|
||||
``observer = <observer object>``
|
||||
This lets you configure how the progress of the render is reported. The
|
||||
default is to display a progress bar, unless run on Windows or with stderr
|
||||
redirected to a file. The default value will probably be fine for most
|
||||
people, but advanced users may want to make their own progress reporter (for
|
||||
a web service or something like that) or you may want to force a particular
|
||||
observer to be used. The observer object is expected to have at least ``start``,
|
||||
``add``, ``update``, and ``finish`` methods.
|
||||
|
||||
e.g.::
|
||||
|
||||
observer = ProgressBarObserver()
|
||||
|
||||
.. _outputdir:
|
||||
|
||||
|
||||
|
||||
@@ -34,3 +34,22 @@ a ``python2.6`` package. To do this, add the following line to your
|
||||
|
||||
Then run ``apt-get update`` and ``apt-get install minecraft-overviewer`` and
|
||||
you're all set! See you at the :doc:`running` page!
|
||||
|
||||
CentOS / RHEL / Fedora
|
||||
======================
|
||||
We also provide a RPM repository with pre-built packages for users on RPM-based
|
||||
distros. Note that on CentOS 5, the `EPEL <http://fedoraproject.org/wiki/EPEL>`_
|
||||
repository is required to get Python 2.6 . To add the Overviewer repository to
|
||||
YUM, just run
|
||||
|
||||
::
|
||||
|
||||
wget -O /etc/yum.repos.d/overviewer.repo http://overviewer.org/rpms/overviewer.repo
|
||||
|
||||
Then to install Overviewer run
|
||||
|
||||
::
|
||||
|
||||
yum install Minecraft-Overviewer
|
||||
|
||||
After that head to the :doc:`running` page!
|
||||
|
||||
@@ -40,6 +40,7 @@ from overviewer_core import textures
|
||||
from overviewer_core import optimizeimages, world
|
||||
from overviewer_core import configParser, tileset, assetmanager, dispatcher
|
||||
from overviewer_core import cache
|
||||
from overviewer_core import observer
|
||||
|
||||
helptext = """
|
||||
%prog [--rendermodes=...] [options] <World> <Output Dir>
|
||||
@@ -416,20 +417,9 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
||||
if config['processes'] == 1:
|
||||
dispatch = dispatcher.Dispatcher()
|
||||
else:
|
||||
dispatch = dispatcher.MultiprocessingDispatcher(local_procs=config['processes'])
|
||||
last_status_print = time.time()
|
||||
def print_status(phase, completed, total):
|
||||
# phase is ignored. it's always zero?
|
||||
if (total == 0):
|
||||
percent = 100
|
||||
logging.info("Rendered %d of %d tiles. %d%% complete", completed, total, percent)
|
||||
elif total == None:
|
||||
logging.info("Rendered %d tiles.", completed)
|
||||
else:
|
||||
percent = int(100* completed/total)
|
||||
logging.info("Rendered %d of %d. %d%% complete", completed, total, percent)
|
||||
|
||||
dispatch.render_all(tilesets, print_status)
|
||||
dispatch = dispatcher.MultiprocessingDispatcher(
|
||||
local_procs=config['processes'])
|
||||
dispatch.render_all(tilesets, config['observer'])
|
||||
dispatch.close()
|
||||
|
||||
assetMrg.finalize(tilesets)
|
||||
|
||||
@@ -19,8 +19,6 @@ import multiprocessing.managers
|
||||
import cPickle as pickle
|
||||
import Queue
|
||||
import time
|
||||
import logging
|
||||
|
||||
from signals import Signal
|
||||
|
||||
class Dispatcher(object):
|
||||
@@ -40,7 +38,7 @@ class Dispatcher(object):
|
||||
# keeps track of jobs waiting to run after dependencies finish
|
||||
self._pending_jobs = []
|
||||
|
||||
def render_all(self, tilesetlist, status_callback):
|
||||
def render_all(self, tilesetlist, observer):
|
||||
"""Render all of the tilesets in the given
|
||||
tilesetlist. status_callback is called periodically to update
|
||||
status. The callback should take the following arguments:
|
||||
@@ -74,41 +72,18 @@ class Dispatcher(object):
|
||||
break
|
||||
else:
|
||||
total_jobs += jobs_for_tileset
|
||||
finished_jobs = 0
|
||||
|
||||
# do the first status update
|
||||
self._status_update(status_callback, phase, finished_jobs, total_jobs, force=True)
|
||||
|
||||
observer.start(total_jobs)
|
||||
# go through these iterators round-robin style
|
||||
for tileset, (workitem, deps) in util.roundrobin(work_iterators):
|
||||
self._pending_jobs.append((tileset, workitem, deps))
|
||||
finished_jobs += self._dispatch_jobs()
|
||||
self._status_update(status_callback, phase, finished_jobs, total_jobs)
|
||||
observer.add(self._dispatch_jobs())
|
||||
|
||||
# after each phase, wait for the work to finish
|
||||
while len(self._pending_jobs) > 0 or len(self._running_jobs) > 0:
|
||||
finished_jobs += self._dispatch_jobs()
|
||||
self._status_update(status_callback, phase, finished_jobs, total_jobs)
|
||||
observer.add(self._dispatch_jobs())
|
||||
|
||||
def _status_update(self, callback, phase, completed, total, force=False):
|
||||
# always called with force=True at the beginning, so that can
|
||||
# be used to set up state. After that, it is called after
|
||||
# every _dispatch_jobs() often; this function is used to
|
||||
# decide how often the actual status callback should be
|
||||
# called.
|
||||
if force:
|
||||
self._last_status_update = completed
|
||||
if callback:
|
||||
callback(phase, completed, total)
|
||||
return
|
||||
|
||||
if callback is None:
|
||||
return
|
||||
|
||||
update_interval = 100 # XXX arbitrary
|
||||
if self._last_status_update < 0 or completed >= self._last_status_update + update_interval or completed < self._last_status_update:
|
||||
self._last_status_update = completed
|
||||
callback(phase, completed, total)
|
||||
observer.finish()
|
||||
|
||||
def _dispatch_jobs(self):
|
||||
# helper function to dispatch pending jobs when their
|
||||
|
||||
164
overviewer_core/observer.py
Normal file
164
overviewer_core/observer.py
Normal file
@@ -0,0 +1,164 @@
|
||||
# This file is part of the Minecraft Overviewer.
|
||||
#
|
||||
# Minecraft Overviewer is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or (at
|
||||
# your option) any later version.
|
||||
#
|
||||
# Minecraft Overviewer is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
import logging
|
||||
import progressbar
|
||||
import sys
|
||||
|
||||
class Observer(object):
|
||||
"""Base class that defines the observer interface.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._current_value = None
|
||||
self._max_value = None
|
||||
self.start_time = None
|
||||
self.end_time = None
|
||||
|
||||
def start(self, max_value):
|
||||
"""Signals the start of whatever process. Must be called before update
|
||||
"""
|
||||
self._set_max_value(max_value)
|
||||
self.start_time = time.time()
|
||||
self.update(0)
|
||||
return self
|
||||
|
||||
def is_started(self):
|
||||
return self.start_time is not None
|
||||
|
||||
def finish(self):
|
||||
"""Signals the end of the processes, should be called after the
|
||||
process is done.
|
||||
"""
|
||||
self.end_time = time.time()
|
||||
|
||||
def is_finished(self):
|
||||
return self.end_time is not None
|
||||
|
||||
def is_running(self):
|
||||
return self.is_started() and not self.is_finished()
|
||||
|
||||
def add(self, amount):
|
||||
"""Shortcut to update by increments instead of absolute values. Zero
|
||||
amounts are ignored.
|
||||
"""
|
||||
if amount:
|
||||
self.update(self.get_current_value() + amount)
|
||||
|
||||
def update(self, current_value):
|
||||
"""Set the progress value. Should be between 0 and max_value. Returns
|
||||
whether this update is actually displayed.
|
||||
"""
|
||||
self._current_value = current_value
|
||||
return False
|
||||
|
||||
def get_percentage(self):
|
||||
"""Get the current progress percentage. Assumes 100% if max_value is 0
|
||||
"""
|
||||
if self.get_max_value() is 0:
|
||||
return 100.0
|
||||
else:
|
||||
return self.get_current_value() * 100.0 / self.get_max_value()
|
||||
|
||||
def get_current_value(self):
|
||||
return self._current_value
|
||||
|
||||
def get_max_value(self):
|
||||
return self._max_value
|
||||
|
||||
def _set_max_value(self, max_value):
|
||||
self._max_value = max_value
|
||||
|
||||
class LoggingObserver(Observer):
|
||||
"""Simple observer that just outputs status through logging.
|
||||
"""
|
||||
def __init__(self):
|
||||
super(Observer, self).__init__()
|
||||
#this is an easy way to make the first update() call print a line
|
||||
self.last_update = -101
|
||||
|
||||
def finish(self):
|
||||
logging.info("Rendered %d of %d. %d%% complete", self.get_max_value(),
|
||||
self.get_max_value(), 100.0)
|
||||
super(LoggingObserver, self).finish()
|
||||
|
||||
def update(self, current_value):
|
||||
super(LoggingObserver, self).update(current_value)
|
||||
if self._need_update():
|
||||
logging.info("Rendered %d of %d. %d%% complete",
|
||||
self.get_current_value(), self.get_max_value(),
|
||||
self.get_percentage())
|
||||
self.last_update = current_value
|
||||
return True
|
||||
return False
|
||||
|
||||
def _need_update(self):
|
||||
cur_val = self.get_current_value()
|
||||
if cur_val < 100:
|
||||
return cur_val - self.last_update > 10
|
||||
elif cur_val < 500:
|
||||
return cur_val - self.last_update > 50
|
||||
else:
|
||||
return cur_val - self.last_update > 100
|
||||
|
||||
default_widgets = [
|
||||
progressbar.Percentage(), ' ',
|
||||
progressbar.Bar(marker='=', left='[', right=']'), ' ',
|
||||
progressbar.CounterWidget(), ' ',
|
||||
progressbar.GenericSpeed(format='%.2ft/s'), ' ',
|
||||
progressbar.ETA(prefix='eta ')
|
||||
]
|
||||
class ProgressBarObserver(progressbar.ProgressBar, Observer):
|
||||
"""Display progress through a progressbar.
|
||||
"""
|
||||
|
||||
#the progress bar is only updated in increments of this for performance
|
||||
UPDATE_INTERVAL = 25
|
||||
|
||||
def __init__(self, widgets=default_widgets, term_width=None, fd=sys.stderr):
|
||||
super(ProgressBarObserver, self).__init__(widgets=widgets,
|
||||
term_width=term_width, fd=fd)
|
||||
self.last_update = 0 - (self.UPDATE_INTERVAL + 1)
|
||||
|
||||
def start(self, max_value):
|
||||
self._set_max_value(max_value)
|
||||
logging.info("Rendering %d total tiles." % max_value)
|
||||
super(ProgressBarObserver, self).start()
|
||||
|
||||
def is_started(self):
|
||||
return self.start_time is not None
|
||||
|
||||
def finish(self):
|
||||
self._end_time = time.time()
|
||||
super(ProgressBarObserver, self).finish()
|
||||
|
||||
def update(self, current_value):
|
||||
if super(ProgressBarObserver, self).update(current_value):
|
||||
self.last_update = self.get_current_value()
|
||||
|
||||
percentage = Observer.get_percentage
|
||||
|
||||
def get_current_value(self):
|
||||
return self.currval
|
||||
|
||||
def get_max_value(self):
|
||||
return self.maxval
|
||||
|
||||
def _set_max_value(self, max_value):
|
||||
self.maxval = max_value
|
||||
|
||||
def _need_update(self):
|
||||
return self.get_current_value() - self.last_update > self.UPDATE_INTERVAL
|
||||
399
overviewer_core/progressbar.py
Normal file
399
overviewer_core/progressbar.py
Normal file
@@ -0,0 +1,399 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
#
|
||||
# progressbar - Text progressbar library for python.
|
||||
# Copyright (c) 2005 Nilton Volpato
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
"""Text progressbar library for python.
|
||||
|
||||
This library provides a text mode progressbar. This is tipically used
|
||||
to display the progress of a long running operation, providing a
|
||||
visual clue that processing is underway.
|
||||
|
||||
The ProgressBar class manages the progress, and the format of the line
|
||||
is given by a number of widgets. A widget is an object that may
|
||||
display diferently depending on the state of the progress. There are
|
||||
three types of widget:
|
||||
- a string, which always shows itself;
|
||||
- a ProgressBarWidget, which may return a diferent value every time
|
||||
it's update method is called; and
|
||||
- a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it
|
||||
expands to fill the remaining width of the line.
|
||||
|
||||
The progressbar module is very easy to use, yet very powerful. And
|
||||
automatically supports features like auto-resizing when available.
|
||||
"""
|
||||
|
||||
__author__ = "Nilton Volpato"
|
||||
__author_email__ = "first-name dot last-name @ gmail.com"
|
||||
__date__ = "2006-05-07"
|
||||
__version__ = "2.2"
|
||||
|
||||
# Changelog
|
||||
#
|
||||
# 2006-05-07: v2.2 fixed bug in windows
|
||||
# 2005-12-04: v2.1 autodetect terminal width, added start method
|
||||
# 2005-12-04: v2.0 everything is now a widget (wow!)
|
||||
# 2005-12-03: v1.0 rewrite using widgets
|
||||
# 2005-06-02: v0.5 rewrite
|
||||
# 2004-??-??: v0.1 first version
|
||||
|
||||
|
||||
import sys, time
|
||||
from array import array
|
||||
try:
|
||||
from fcntl import ioctl
|
||||
import termios
|
||||
except ImportError:
|
||||
pass
|
||||
import signal
|
||||
|
||||
class ProgressBarWidget(object):
|
||||
"""This is an element of ProgressBar formatting.
|
||||
|
||||
The ProgressBar object will call it's update value when an update
|
||||
is needed. It's size may change between call, but the results will
|
||||
not be good if the size changes drastically and repeatedly.
|
||||
"""
|
||||
def update(self, pbar):
|
||||
"""Returns the string representing the widget.
|
||||
|
||||
The parameter pbar is a reference to the calling ProgressBar,
|
||||
where one can access attributes of the class for knowing how
|
||||
the update must be made.
|
||||
|
||||
At least this function must be overriden."""
|
||||
pass
|
||||
|
||||
class ProgressBarWidgetHFill(object):
|
||||
"""This is a variable width element of ProgressBar formatting.
|
||||
|
||||
The ProgressBar object will call it's update value, informing the
|
||||
width this object must the made. This is like TeX \\hfill, it will
|
||||
expand to fill the line. You can use more than one in the same
|
||||
line, and they will all have the same width, and together will
|
||||
fill the line.
|
||||
"""
|
||||
def update(self, pbar, width):
|
||||
"""Returns the string representing the widget.
|
||||
|
||||
The parameter pbar is a reference to the calling ProgressBar,
|
||||
where one can access attributes of the class for knowing how
|
||||
the update must be made. The parameter width is the total
|
||||
horizontal width the widget must have.
|
||||
|
||||
At least this function must be overriden."""
|
||||
pass
|
||||
|
||||
|
||||
class ETA(ProgressBarWidget):
|
||||
"Widget for the Estimated Time of Arrival"
|
||||
def __init__(self, prefix='ETA: ', format='%H:%M:%S'):
|
||||
self.format = format
|
||||
self.prefix = prefix
|
||||
|
||||
def format_time(self, seconds):
|
||||
return time.strftime(self.format, time.gmtime(seconds))
|
||||
|
||||
def update(self, pbar):
|
||||
if pbar.currval == 0:
|
||||
return self.prefix + '-' * len(self.format)
|
||||
elif pbar.finished:
|
||||
return 'Time: %s' % self.format_time(pbar.seconds_elapsed)
|
||||
else:
|
||||
eta = pbar.seconds_elapsed * pbar.maxval / pbar.currval - pbar.seconds_elapsed
|
||||
return self.prefix + self.format_time(eta)
|
||||
|
||||
class GenericSpeed(ProgressBarWidget):
|
||||
"Widget for showing the values/s"
|
||||
def __init__(self, format='%6.2f ?/s'):
|
||||
if callable(format):
|
||||
self.format = format
|
||||
else:
|
||||
self.format = lambda speed: format % speed
|
||||
def update(self, pbar):
|
||||
if pbar.seconds_elapsed < 2e-6:
|
||||
speed = 0.0
|
||||
else:
|
||||
speed = float(pbar.currval) / pbar.seconds_elapsed
|
||||
return self.format(speed)
|
||||
|
||||
class FileTransferSpeed(ProgressBarWidget):
|
||||
"Widget for showing the transfer speed (useful for file transfers)."
|
||||
def __init__(self):
|
||||
self.fmt = '%6.2f %s'
|
||||
self.units = ['B','K','M','G','T','P']
|
||||
def update(self, pbar):
|
||||
if pbar.seconds_elapsed < 2e-6:#== 0:
|
||||
bps = 0.0
|
||||
else:
|
||||
bps = float(pbar.currval) / pbar.seconds_elapsed
|
||||
spd = bps
|
||||
for u in self.units:
|
||||
if spd < 1000:
|
||||
break
|
||||
spd /= 1000
|
||||
return self.fmt % (spd, u+'/s')
|
||||
|
||||
class RotatingMarker(ProgressBarWidget):
|
||||
"A rotating marker for filling the bar of progress."
|
||||
def __init__(self, markers='|/-\\'):
|
||||
self.markers = markers
|
||||
self.curmark = -1
|
||||
def update(self, pbar):
|
||||
if pbar.finished:
|
||||
return self.markers[0]
|
||||
self.curmark = (self.curmark + 1)%len(self.markers)
|
||||
return self.markers[self.curmark]
|
||||
|
||||
class Percentage(ProgressBarWidget):
|
||||
"Just the percentage done."
|
||||
def __init__(self, format='%3d%%'):
|
||||
self.format = format
|
||||
|
||||
def update(self, pbar):
|
||||
return self.format % pbar.percentage()
|
||||
|
||||
class CounterWidget(ProgressBarWidget):
|
||||
"Simple display of (just) the current value"
|
||||
def update(self, pbar):
|
||||
return str(pbar.currval)
|
||||
|
||||
class FractionWidget(ProgressBarWidget):
|
||||
def __init__(self, sep=' / '):
|
||||
self.sep = sep
|
||||
def update(self, pbar):
|
||||
return '%2d%s%2d' % (pbar.currval, self.sep, pbar.maxval)
|
||||
|
||||
class Bar(ProgressBarWidgetHFill):
|
||||
"The bar of progress. It will strech to fill the line."
|
||||
def __init__(self, marker='#', left='|', right='|'):
|
||||
self.marker = marker
|
||||
self.left = left
|
||||
self.right = right
|
||||
def _format_marker(self, pbar):
|
||||
if isinstance(self.marker, (str, unicode)):
|
||||
return self.marker
|
||||
else:
|
||||
return self.marker.update(pbar)
|
||||
def update(self, pbar, width):
|
||||
percent = pbar.percentage()
|
||||
cwidth = width - len(self.left) - len(self.right)
|
||||
marked_width = int(percent * cwidth / 100)
|
||||
m = self._format_marker(pbar)
|
||||
bar = (self.left + (m*marked_width).ljust(cwidth) + self.right)
|
||||
return bar
|
||||
|
||||
class ReverseBar(Bar):
|
||||
"The reverse bar of progress, or bar of regress. :)"
|
||||
def update(self, pbar, width):
|
||||
percent = pbar.percentage()
|
||||
cwidth = width - len(self.left) - len(self.right)
|
||||
marked_width = int(percent * cwidth / 100)
|
||||
m = self._format_marker(pbar)
|
||||
bar = (self.left + (m*marked_width).rjust(cwidth) + self.right)
|
||||
return bar
|
||||
|
||||
default_widgets = [Percentage(), ' ', Bar()]
|
||||
class ProgressBar(object):
|
||||
"""This is the ProgressBar class, it updates and prints the bar.
|
||||
|
||||
The term_width parameter may be an integer. Or None, in which case
|
||||
it will try to guess it, if it fails it will default to 80 columns.
|
||||
|
||||
The simple use is like this:
|
||||
>>> pbar = ProgressBar().start()
|
||||
>>> for i in xrange(100):
|
||||
... # do something
|
||||
... pbar.update(i+1)
|
||||
...
|
||||
>>> pbar.finish()
|
||||
|
||||
But anything you want to do is possible (well, almost anything).
|
||||
You can supply different widgets of any type in any order. And you
|
||||
can even write your own widgets! There are many widgets already
|
||||
shipped and you should experiment with them.
|
||||
|
||||
When implementing a widget update method you may access any
|
||||
attribute or function of the ProgressBar object calling the
|
||||
widget's update method. The most important attributes you would
|
||||
like to access are:
|
||||
- currval: current value of the progress, 0 <= currval <= maxval
|
||||
- maxval: maximum (and final) value of the progress
|
||||
- finished: True if the bar is have finished (reached 100%), False o/w
|
||||
- start_time: first time update() method of ProgressBar was called
|
||||
- seconds_elapsed: seconds elapsed since start_time
|
||||
- percentage(): percentage of the progress (this is a method)
|
||||
"""
|
||||
def __init__(self, maxval=100, widgets=default_widgets, term_width=None,
|
||||
fd=sys.stderr):
|
||||
assert maxval > 0
|
||||
self.maxval = maxval
|
||||
self.widgets = widgets
|
||||
self.fd = fd
|
||||
self.signal_set = False
|
||||
if term_width is None:
|
||||
try:
|
||||
self.handle_resize(None,None)
|
||||
signal.signal(signal.SIGWINCH, self.handle_resize)
|
||||
self.signal_set = True
|
||||
except:
|
||||
self.term_width = 79
|
||||
else:
|
||||
self.term_width = term_width
|
||||
|
||||
self.currval = 0
|
||||
self.finished = False
|
||||
self.start_time = None
|
||||
self.seconds_elapsed = 0
|
||||
|
||||
def handle_resize(self, signum, frame):
|
||||
h,w=array('h', ioctl(self.fd,termios.TIOCGWINSZ,'\0'*8))[:2]
|
||||
self.term_width = w
|
||||
|
||||
def percentage(self):
|
||||
"Returns the percentage of the progress."
|
||||
return self.currval*100.0 / self.maxval
|
||||
|
||||
def _format_widgets(self):
|
||||
r = []
|
||||
hfill_inds = []
|
||||
num_hfill = 0
|
||||
currwidth = 0
|
||||
for i, w in enumerate(self.widgets):
|
||||
if isinstance(w, ProgressBarWidgetHFill):
|
||||
r.append(w)
|
||||
hfill_inds.append(i)
|
||||
num_hfill += 1
|
||||
elif isinstance(w, (str, unicode)):
|
||||
r.append(w)
|
||||
currwidth += len(w)
|
||||
else:
|
||||
weval = w.update(self)
|
||||
currwidth += len(weval)
|
||||
r.append(weval)
|
||||
for iw in hfill_inds:
|
||||
r[iw] = r[iw].update(self, (self.term_width-currwidth)/num_hfill)
|
||||
return r
|
||||
|
||||
def _format_line(self):
|
||||
return ''.join(self._format_widgets()).ljust(self.term_width)
|
||||
|
||||
def _need_update(self):
|
||||
return True
|
||||
|
||||
def update(self, value):
|
||||
"Updates the progress bar to a new value."
|
||||
assert 0 <= value <= self.maxval
|
||||
self.currval = value
|
||||
if not self._need_update() or self.finished:
|
||||
return False
|
||||
if not self.start_time:
|
||||
self.start_time = time.time()
|
||||
self.seconds_elapsed = time.time() - self.start_time
|
||||
if value != self.maxval:
|
||||
self.fd.write(self._format_line() + '\r')
|
||||
else:
|
||||
self.finished = True
|
||||
self.fd.write(self._format_line() + '\n')
|
||||
return True
|
||||
|
||||
def start(self):
|
||||
"""Start measuring time, and prints the bar at 0%.
|
||||
|
||||
It returns self so you can use it like this:
|
||||
>>> pbar = ProgressBar().start()
|
||||
>>> for i in xrange(100):
|
||||
... # do something
|
||||
... pbar.update(i+1)
|
||||
...
|
||||
>>> pbar.finish()
|
||||
"""
|
||||
self.update(0)
|
||||
return self
|
||||
|
||||
def finish(self):
|
||||
"""Used to tell the progress is finished."""
|
||||
self.update(self.maxval)
|
||||
if self.signal_set:
|
||||
signal.signal(signal.SIGWINCH, signal.SIG_DFL)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
import os
|
||||
|
||||
def example1():
|
||||
widgets = ['Test: ', Percentage(), ' ', Bar(marker=RotatingMarker()),
|
||||
' ', ETA(), ' ', FileTransferSpeed()]
|
||||
pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
|
||||
for i in range(1000000):
|
||||
# do something
|
||||
pbar.update(10*i+1)
|
||||
pbar.finish()
|
||||
print
|
||||
|
||||
def example2():
|
||||
class CrazyFileTransferSpeed(FileTransferSpeed):
|
||||
"It's bigger between 45 and 80 percent"
|
||||
def update(self, pbar):
|
||||
if 45 < pbar.percentage() < 80:
|
||||
return 'Bigger Now ' + FileTransferSpeed.update(self,pbar)
|
||||
else:
|
||||
return FileTransferSpeed.update(self,pbar)
|
||||
|
||||
widgets = [CrazyFileTransferSpeed(),' <<<', Bar(), '>>> ', Percentage(),' ', ETA()]
|
||||
pbar = ProgressBar(widgets=widgets, maxval=10000000)
|
||||
# maybe do something
|
||||
pbar.start()
|
||||
for i in range(2000000):
|
||||
# do something
|
||||
pbar.update(5*i+1)
|
||||
pbar.finish()
|
||||
print
|
||||
|
||||
def example3():
|
||||
widgets = [Bar('>'), ' ', ETA(), ' ', ReverseBar('<')]
|
||||
pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
|
||||
for i in range(1000000):
|
||||
# do something
|
||||
pbar.update(10*i+1)
|
||||
pbar.finish()
|
||||
print
|
||||
|
||||
def example4():
|
||||
widgets = ['Test: ', Percentage(), ' ',
|
||||
Bar(marker='0',left='[',right=']'),
|
||||
' ', ETA(), ' ', FileTransferSpeed()]
|
||||
pbar = ProgressBar(widgets=widgets, maxval=500)
|
||||
pbar.start()
|
||||
for i in range(100,500+1,50):
|
||||
time.sleep(0.2)
|
||||
pbar.update(i)
|
||||
pbar.finish()
|
||||
print
|
||||
|
||||
|
||||
example1()
|
||||
example2()
|
||||
example3()
|
||||
example4()
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
|
||||
from settingsValidators import *
|
||||
import util
|
||||
from observer import ProgressBarObserver, LoggingObserver
|
||||
import platform
|
||||
import sys
|
||||
|
||||
# renders is a dictionary mapping strings to dicts. These dicts describe the
|
||||
# configuration for that render. Therefore, the validator for 'renders' is set
|
||||
@@ -93,3 +96,10 @@ processes = Setting(required=True, validator=int, default=-1)
|
||||
# memcached is an option, but unless your IO costs are really high, it just
|
||||
# ends up adding overhead and isn't worth it.
|
||||
memcached_host = Setting(required=False, validator=str, default=None)
|
||||
|
||||
if platform.system() == 'Windows' or not sys.stderr.isatty():
|
||||
obs = LoggingObserver()
|
||||
else:
|
||||
obs = ProgressBarObserver()
|
||||
|
||||
observer = Setting(required=True, validator=validateObserver, default=obs)
|
||||
|
||||
@@ -182,6 +182,12 @@ def validateCrop(value):
|
||||
value[1],value[3] = value[3],value[1]
|
||||
return value
|
||||
|
||||
def validateObserver(observer):
|
||||
if all(map(lambda m: hasattr(observer, m), ['start', 'add', 'update', 'finish'])):
|
||||
return observer
|
||||
else:
|
||||
raise ValidationException("%r does not look like an observer" % repr(observer))
|
||||
|
||||
def make_dictValidator(keyvalidator, valuevalidator):
|
||||
"""Compose and return a dict validator -- a validator that validates each
|
||||
key and value in a dictionary.
|
||||
|
||||
@@ -319,9 +319,9 @@ class TileSet(object):
|
||||
"to date.",
|
||||
self.options['name'],
|
||||
)
|
||||
logging.warning("You won't get percentage progress for "+
|
||||
"this run only, because I don't know how many tiles "+
|
||||
"need rendering. I'll be checking them as I go")
|
||||
logging.warning("The total tile count will be (possibly "+
|
||||
"wildly) inaccurate, because I don't know how many "+
|
||||
"tiles need rendering. I'll be checking them as I go")
|
||||
self.options['renderchecks'] = 1
|
||||
else:
|
||||
logging.debug("No rendercheck mode specified for %s. "+
|
||||
@@ -397,7 +397,8 @@ class TileSet(object):
|
||||
# Yeah functional programming!
|
||||
return {
|
||||
0: lambda: self.dirtytree.count_all(),
|
||||
1: lambda: None,
|
||||
#there is no good way to guess this so just give total count
|
||||
1: lambda: (4**(self.treedepth+1)-1)/3,
|
||||
2: lambda: self.dirtytree.count_all(),
|
||||
}[self.options['renderchecks']]()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user