Merge branch 'devel'
This commit is contained in:
@@ -216,6 +216,8 @@ the form ``key = value``. Two items take a different form:, ``worlds`` and
|
|||||||
|
|
||||||
**You must specify at least one render**
|
**You must specify at least one render**
|
||||||
|
|
||||||
|
.. _outputdir:
|
||||||
|
|
||||||
``outputdir = "<output directory path>"``
|
``outputdir = "<output directory path>"``
|
||||||
This is the path to the output directory where the rendered tiles will
|
This is the path to the output directory where the rendered tiles will
|
||||||
be saved.
|
be saved.
|
||||||
@@ -250,11 +252,61 @@ the form ``key = value``. Two items take a different form:, ``worlds`` and
|
|||||||
observer to be used. The observer object is expected to have at least ``start``,
|
observer to be used. The observer object is expected to have at least ``start``,
|
||||||
``add``, ``update``, and ``finish`` methods.
|
``add``, ``update``, and ``finish`` methods.
|
||||||
|
|
||||||
e.g.::
|
If you want to specify an observer manually, try something like:
|
||||||
|
::
|
||||||
|
|
||||||
|
from observer import ProgressBarObserver()
|
||||||
observer = ProgressBarObserver()
|
observer = ProgressBarObserver()
|
||||||
|
|
||||||
.. _outputdir:
|
There are currently three observers available: ``LoggingObserver``,
|
||||||
|
``ProgressBarObserver`` and ``JSObserver``.
|
||||||
|
|
||||||
|
``LoggingObserver``
|
||||||
|
This gives the normal/older style output and is the default when output
|
||||||
|
is redirected to a file or when running on Windows
|
||||||
|
|
||||||
|
``ProgressBarObserver``
|
||||||
|
This is used by default when the output is a terminal. Displays a text based
|
||||||
|
progress bar and some statistics.
|
||||||
|
|
||||||
|
``JSObserver(outputdir[, minrefresh][, messages])``
|
||||||
|
This will display render progress on the output map in the bottom right
|
||||||
|
corner of the screen. ``JSObserver``.
|
||||||
|
|
||||||
|
* ``outputdir="<output directory path"``
|
||||||
|
Path to overviewer output directory. For simplicity, specify this
|
||||||
|
as ``outputdir=outputdir`` and place this line after setting
|
||||||
|
``outputdir = "<output directory path>"``.
|
||||||
|
**Required**
|
||||||
|
|
||||||
|
* ``minrefresh=<seconds>``
|
||||||
|
Progress information won't be written to file or requested by your
|
||||||
|
web browser more frequently than this interval.
|
||||||
|
|
||||||
|
* ``messages=dict(totalTiles=<string>, renderCompleted=<string>, renderProgress=<string>)``
|
||||||
|
Customises messages displayed in browser. All three messages must be
|
||||||
|
defined as follows:
|
||||||
|
|
||||||
|
* ``totalTiles="Rendering %d tiles"``
|
||||||
|
The ``%d`` format string will be replaced with the total number of
|
||||||
|
tiles to be rendered.
|
||||||
|
|
||||||
|
* ``renderCompleted="Render completed in %02d:%02d:%02d"``
|
||||||
|
The three format strings will be replaced with the number of hours.
|
||||||
|
minutes and seconds taken to complete this render.
|
||||||
|
|
||||||
|
* ``renderProgress="Rendered %d of %d tiles (%d%%)"``
|
||||||
|
The three format strings will be replaced with the number of tiles
|
||||||
|
completed, the total number of tiles and the percentage complete
|
||||||
|
|
||||||
|
Format strings are explained here: http://docs.python.org/library/stdtypes.html#string-formatting
|
||||||
|
All format strings must be present in your custom messages.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from observer import JSObserver
|
||||||
|
observer = JSObserver(outputdir, 10)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _renderdict:
|
.. _renderdict:
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ overviewer.util = {
|
|||||||
var coordsdiv = new overviewer.views.CoordboxView({tagName: 'DIV'});
|
var coordsdiv = new overviewer.views.CoordboxView({tagName: 'DIV'});
|
||||||
coordsdiv.render();
|
coordsdiv.render();
|
||||||
|
|
||||||
|
var progressdiv = new overviewer.views.ProgressView({tagName: 'DIV'});
|
||||||
|
progressdiv.render();
|
||||||
|
progressdiv.updateProgress();
|
||||||
|
|
||||||
if (overviewer.collections.haveSigns) {
|
if (overviewer.collections.haveSigns) {
|
||||||
var signs = new overviewer.views.SignControlView();
|
var signs = new overviewer.views.SignControlView();
|
||||||
signs.registerEvents(signs);
|
signs.registerEvents(signs);
|
||||||
|
|||||||
@@ -137,7 +137,34 @@ overviewer.views.CoordboxView = Backbone.View.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
overviewer.views.ProgressView = Backbone.View.extend({
|
||||||
|
initialize: function() {
|
||||||
|
this.el.id = 'progressDiv';
|
||||||
|
this.el.innerHTML = 'Current Render Progress';
|
||||||
|
overviewer.map.controls[google.maps.ControlPosition.BOTTOM_RIGHT].push(this.el);
|
||||||
|
this.el.hidden = true;
|
||||||
|
$.ajaxSetup({cache: false});
|
||||||
|
},
|
||||||
|
updateProgress: function() {
|
||||||
|
e = this;
|
||||||
|
$.getJSON('progress.json', null, function(d){
|
||||||
|
if (!(d == null||d=='')) {
|
||||||
|
e.el.hidden = false;
|
||||||
|
e.el.innerHTML = d['message'];
|
||||||
|
if (d.update > 0) {
|
||||||
|
setTimeout("e.updateProgress()", d.update);
|
||||||
|
} else {
|
||||||
|
setTimeout("e.updateProgress()", 60000);
|
||||||
|
e.el.innerHTML="Hidden - d.update < 0";
|
||||||
|
e.el.hidden = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e.el.innerHTML="Hidden - !!d==false";
|
||||||
|
e.el.hidden = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* GoogleMapView is responsible for dealing with the GoogleMaps API to create the
|
/* GoogleMapView is responsible for dealing with the GoogleMaps API to create the
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#link, #coordsDiv {
|
#link, #coordsDiv, #progressDiv {
|
||||||
background-color: #fff; /* fallback */
|
background-color: #fff; /* fallback */
|
||||||
background-color: rgba(255,255,255,0.55);
|
background-color: rgba(255,255,255,0.55);
|
||||||
border: 1px solid rgb(0, 0, 0);
|
border: 1px solid rgb(0, 0, 0);
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
import progressbar
|
import progressbar
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
class Observer(object):
|
class Observer(object):
|
||||||
"""Base class that defines the observer interface.
|
"""Base class that defines the observer interface.
|
||||||
@@ -164,3 +166,183 @@ class ProgressBarObserver(progressbar.ProgressBar, Observer):
|
|||||||
|
|
||||||
def _need_update(self):
|
def _need_update(self):
|
||||||
return self.get_current_value() - self.last_update > self.UPDATE_INTERVAL
|
return self.get_current_value() - self.last_update > self.UPDATE_INTERVAL
|
||||||
|
|
||||||
|
class JSObserver(Observer):
|
||||||
|
"""Display progress on index.html using JavaScript
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, outputdir, minrefresh=5, messages=False):
|
||||||
|
"""Initialise observer
|
||||||
|
outputdir must be set to the map output directory path
|
||||||
|
minrefresh specifies the minimum gap between requests, in seconds [optional]
|
||||||
|
messages is a dictionary which allows the displayed messages to be customised [optional]
|
||||||
|
"""
|
||||||
|
self.last_update = -11
|
||||||
|
self.last_update_time = -1
|
||||||
|
self._current_value = -1
|
||||||
|
self.minrefresh = 1000*minrefresh
|
||||||
|
self.json = dict()
|
||||||
|
|
||||||
|
if (messages == False):
|
||||||
|
self.messages=dict(totalTiles="Rendering %d tiles", renderCompleted="Render completed in %02d:%02d:%02d", renderProgress="Rendered %d of %d tiles (%d%%)")
|
||||||
|
elif (isinstance(messages, dict)):
|
||||||
|
if ('totalTiles' in messages and 'renderCompleted' in messages and 'renderProgress' in messages):
|
||||||
|
self.messages = messages
|
||||||
|
else:
|
||||||
|
raise Exception("JSObserver: messages parameter must be a dictionary with three entries: totalTiles, renderCompleted and renderProgress")
|
||||||
|
else:
|
||||||
|
raise Exception("JSObserver: messages parameter must be a dictionary with three entries: totalTiles, renderCompleted and renderProgress")
|
||||||
|
if not os.path.exists(outputdir):
|
||||||
|
raise Exception("JSObserver: Output directory specified (%s) doesn't appear to exist. This should be the same as the Overviewer output directory")
|
||||||
|
|
||||||
|
self.logfile = open(os.path.join(outputdir, "progress.json"), "w+", 0)
|
||||||
|
self.json["message"]=""
|
||||||
|
self.json["update"]=self.minrefresh
|
||||||
|
self.json["messageTime"]=time.time()
|
||||||
|
json.dump(self.json, self.logfile)
|
||||||
|
self.logfile.flush()
|
||||||
|
|
||||||
|
def start(self, max_value):
|
||||||
|
self.logfile.seek(0)
|
||||||
|
self.logfile.truncate()
|
||||||
|
self.json["message"] = self.messages["totalTiles"] % (max_value)
|
||||||
|
self.json["update"] = self.minrefresh
|
||||||
|
self.json["messageTime"] = time.time()
|
||||||
|
json.dump(self.json, self.logfile)
|
||||||
|
self.logfile.flush()
|
||||||
|
self.start_time=time.time()
|
||||||
|
self._set_max_value(max_value)
|
||||||
|
|
||||||
|
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()
|
||||||
|
duration = self.end_time - self.start_time
|
||||||
|
self.logfile.seek(0)
|
||||||
|
self.logfile.truncate()
|
||||||
|
hours = duration // 3600
|
||||||
|
duration = duration % 3600
|
||||||
|
minutes = duration // 60
|
||||||
|
seconds = duration % 60
|
||||||
|
self.json["message"] = self.messages["renderCompleted"] % (hours, minutes, seconds)
|
||||||
|
self.json["update"] = -1 # Initially this was set to False, but that runs into some JS strangeness. -1 is less nice, but works
|
||||||
|
self.json["messageTime"] = time.time()
|
||||||
|
json.dump(self.json, self.logfile)
|
||||||
|
self.logfile.close()
|
||||||
|
|
||||||
|
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
|
||||||
|
if self._need_update():
|
||||||
|
refresh = max(1500*(time.time() - self.last_update_time), self.minrefresh) // 1
|
||||||
|
self.logfile.seek(0)
|
||||||
|
self.logfile.truncate()
|
||||||
|
self.json["message"] = self.messages["renderProgress"] % (self.get_current_value(), self.get_max_value(), self.get_percentage())
|
||||||
|
self.json["update"] = refresh
|
||||||
|
self.json["messageTime"] = time.time()
|
||||||
|
json.dump(self.json, self.logfile)
|
||||||
|
self.logfile.flush()
|
||||||
|
self.last_update_time = time.time()
|
||||||
|
self.last_update = current_value
|
||||||
|
return True
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
class MultiplexingObserver(Observer):
|
||||||
|
"""Combine multiple observers into one.
|
||||||
|
"""
|
||||||
|
def __init__(self, *components):
|
||||||
|
self.components = components
|
||||||
|
super(MultiplexingObserver, self).__init__()
|
||||||
|
|
||||||
|
def start(self, max_value):
|
||||||
|
for o in self.components:
|
||||||
|
o.start(max_value)
|
||||||
|
super(MultiplexingObserver, self).start(max_value)
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
for o in self.components:
|
||||||
|
o.finish()
|
||||||
|
super(MultiplexingObserver, self).finish()
|
||||||
|
|
||||||
|
def update(self, current_value):
|
||||||
|
for o in self.components:
|
||||||
|
o.update(current_value)
|
||||||
|
super(MultiplexingObserver, self).update(current_value)
|
||||||
|
|
||||||
|
class ServerAnnounceObserver(Observer):
|
||||||
|
"""Send the output to a Minecraft server via FIFO or stdin"""
|
||||||
|
def __init__(self, target='/dev/null', pct_interval=10):
|
||||||
|
self.pct_interval = pct_interval
|
||||||
|
self.target_handle = open(target, 'w')
|
||||||
|
self.last_update = 0
|
||||||
|
super(ServerAnnounceObserver, self).__init__()
|
||||||
|
|
||||||
|
def start(self, max_value):
|
||||||
|
self._send_output('Starting render of %d total tiles' % max_value)
|
||||||
|
super(ServerAnnounceObserver, self).start(max_value)
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
self._send_output('Render complete!')
|
||||||
|
super(ServerAnnounceObserver, self).finish()
|
||||||
|
self.target_handle.close()
|
||||||
|
|
||||||
|
def update(self, current_value):
|
||||||
|
super(ServerAnnounceObserver, self).update(current_value)
|
||||||
|
if self._need_update(current_value):
|
||||||
|
self._send_output('Rendered %d of %d tiles, %d%% complete' %
|
||||||
|
(self.get_current_value(), self.get_max_value(),
|
||||||
|
self.get_percentage()))
|
||||||
|
|
||||||
|
def _need_update(self):
|
||||||
|
return self.get_percentage() - \
|
||||||
|
(self.last_update * 100.0 / self.get_max_value()) >= self.pct_interval
|
||||||
|
|
||||||
|
def _send_output(self, output):
|
||||||
|
self.target_handle.write('say %s\n' % output)
|
||||||
|
self.target_handle.flush()
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
from settingsValidators import *
|
from settingsValidators import *
|
||||||
import util
|
import util
|
||||||
from observer import ProgressBarObserver, LoggingObserver
|
from observer import ProgressBarObserver, LoggingObserver, JSObserver
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user