Merge branch 'devel' into dev/pluginLifecycleMgmt

This commit is contained in:
Gina Häußge 2015-04-13 18:24:20 +02:00
commit fc00ef032e
15 changed files with 138 additions and 69 deletions

View file

@ -113,6 +113,8 @@
attempting to connect to their printer on their Raspberry Pis, on which ``/dev/ttyAMA0`` is the OS's serial console
by default). Added configuration of additional ports to the Serial Connection section in the Settings to make it easier
for those people who do indeed have their printer connected to ``/dev/ttyAMA0``.
* Better behaviour of the settings dialog on low-width devices, navigation and content also now scroll independently
from each other (see also [#823](https://github.com/foosel/OctoPrint/pull/823))
### Bug Fixes

View file

@ -168,7 +168,8 @@ FileSelected
Payload:
* ``file``: the file's name
* ``file``: the full path to the file
* ``filename``: the file's name
* ``origin``: the origin of the file, either ``local`` or ``sdcard``
FileDeselected

View file

@ -345,10 +345,6 @@ def params():
# documentation dependencies
install_requires = install_requires + extras_require['develop']
import sys
if sys.platform in ("linux2", "darwin"):
install_requires += ["monotime"]
entry_points = {
"console_scripts": [
"octoprint = octoprint:main"

View file

@ -222,7 +222,7 @@ def index():
templates["sidebar"]["entries"]= dict(
connection=(gettext("Connection"), dict(template="sidebar/connection.jinja2", _div="connection", icon="signal", styles_wrapper=["display: none"], data_bind="visible: loginState.isAdmin")),
state=(gettext("State"), dict(template="sidebar/state.jinja2", _div="state", icon="info-sign")),
files=(gettext("Files"), dict(template="sidebar/files.jinja2", _div="files", icon="list", classes_content=["overflow_visible"], header_addon="sidebar/files_header.jinja2"))
files=(gettext("Files"), dict(template="sidebar/files.jinja2", _div="files", icon="list", classes_content=["overflow_visible"], template_header="sidebar/files_header.jinja2"))
)
# tabs

File diff suppressed because one or more lines are too long

View file

@ -57,6 +57,16 @@ $(function() {
// the view model map is our basic look up table for dependencies that may be injected into other view models
var viewModelMap = {};
// Fix Function#name on browsers that do not support it (IE):
// see: http://stackoverflow.com/questions/6903762/function-name-not-supported-in-ie
if (!(function f() {}).name) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
return this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1];
}
});
}
// helper to create a view model instance with injected constructor parameters from the view model map
var _createViewModelInstance = function(viewModel, viewModelMap){
var viewModelClass = viewModel[0];

View file

@ -160,11 +160,12 @@ $(function() {
self.show = function() {
// show settings, ensure centered position
self.settingsDialog.modal()
.css({
width: 'auto',
'margin-left': function() { return -($(this).width() /2); }
});
self.settingsDialog.modal({
minHeight: function() { return Math.max($.fn.modal.defaults.maxHeight() - 80, 250); }
}).css({
width: 'auto',
'margin-left': function() { return -($(this).width() /2); }
});
return false;
};

View file

@ -102,7 +102,8 @@
layout: function () {
var prop = this.options.height ? 'height' : 'max-height',
value = this.options.height || this.options.maxHeight;
value = this.options.height || this.options.maxHeight,
minHeight = this.options.minHeight;
if (this.options.width){
this.$element.css('width', this.options.width);
@ -122,7 +123,8 @@
this.$element.find('.modal-body')
.css('overflow', '')
.css(prop, '');
.css(prop, '')
.css('min-height', '');
var modalOverflow = $(window).height() - 10 < this.$element.height();
@ -132,6 +134,11 @@
.css(prop, value);
}
if (minHeight) {
this.$element.find('.modal-body')
.css('min-height', minHeight);
}
if (modalOverflow || this.options.modalOverflow) {
this.$element
.css('margin-top', 0)
@ -339,6 +346,7 @@
width: null,
height: null,
maxHeight: null,
minHeight: null,
modalOverflow: false,
consumeTab: true,
focusOn: null,

View file

@ -660,6 +660,11 @@ ul.dropdown-menu li a {
/** Settings dialog */
#settings_dialog {
.modal-body {
#settings_dialog_menu {
margin-left: 0;
}
}
}
/** Footer */
@ -905,3 +910,32 @@ textarea.block {
}
}
}
.modal.large {
width: 975px;
margin-left: -487px;
}
.full-sized-box {
position: absolute;
bottom: 0;
left: 0;
right: 0;
top: 0;
padding: 15px;
.row-fluid {
height: 100%;
}
@media (max-width: 979px) {
position: static;
}
}
.scrollable {
height: 100%;
overflow: auto;
-webkit-overflow-scrolling: touch;
}

View file

@ -1,46 +1,49 @@
<div id="settings_dialog" class="modal hide fade container" tabindex="-1" role="dialog" aria-labelledby="settings_dialog_label" aria-hidden="true">
<div id="settings_dialog" class="modal hide fade large" tabindex="-1" role="dialog" aria-labelledby="settings_dialog_label" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h3 id="settings_dialog_label">{{ _('OctoPrint Settings') }}</h3>
</div>
<div class="modal-body">
<div class="tabbable">
<ul class="nav nav-list span4" id="settingsTabs">
{% set mark_active = True %}
{% for key in templates.settings.order %}
{% set entry, data = templates.settings.entries[key] %}
{% if data is none %}
<li class="nav-header">{{ entry }}</li>
{% else %}
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
<li id="{{ data._div }}_link"
{% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
class="{% if mark_active %}active{% set mark_active = False %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
{% if "styles_link" in data %} style="{{ data.styles_link|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
>
<a href="#{{ data._div }}" data-toggle="tab">{{ entry }}</a>
</li>
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- /ko -->{% endif %}
{% endif %}
{% endfor %}
</ul>
<div class="tab-content span8">
{% set mark_active = True %}
{% for key in templates.settings.order %}
{% set entry, data = templates.settings.entries[key] %}
{% if data is not none %}
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
<div id="{{ data._div }}"
{% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
{% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %}
>
{% include data.template ignore missing %}
</div>
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- /ko -->{% endif %}
{% endif %}
{% endfor %}
<div class="full-sized-box">
<div class="tabbable row-fluid">
<div class="span3 scrollable" id="settings_dialog_menu">
<ul class="nav nav-list" id="settingsTabs">
{% set mark_active = True %}
{% for key in templates.settings.order %}
{% set entry, data = templates.settings.entries[key] %}
{% if data is none %}
<li class="nav-header">{{ entry }}</li>
{% else %}
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
<li id="{{ data._div }}_link"
{% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
class="{% if mark_active %}active{% set mark_active = False %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
{% if "styles_link" in data %} style="{{ data.styles_link|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
>
<a href="#{{ data._div }}" data-toggle="tab">{{ entry }}</a>
</li>
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- /ko -->{% endif %}
{% endif %}
{% endfor %}
</ul>
</div>
<div class="tab-content span9 scrollable" id="settings_dialog_content">
{% set mark_active = True %}
{% for key in templates.settings.order %}
{% set entry, data = templates.settings.entries[key] %}
{% if data is not none %}
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
<div id="{{ data._div }}"
{% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
{% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %}
>
{% include data.template ignore missing %}
</div>
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- /ko -->{% endif %}
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>

View file

@ -1,11 +1,11 @@
<form class="form-horizontal">
<div class="row-fluid">
<div class="offset3 span3"><h4>{{ _('Extruder') }}</h4></div>
<div class="offset4 span3"><h4>{{ _('Extruder') }}</h4></div>
<div class="span3"><h4>{{ _('Bed') }}</h4></div>
</div>
<div data-bind="foreach: temperature_profiles">
<div class="row-fluid" style="margin-bottom: 5px">
<div class="span3">
<div class="span4">
<input type="text" class="span12 text-right" data-bind="value: name">
</div>
<div class="input-append span3">
@ -22,7 +22,7 @@
</div>
</div>
<div class="row-fluid">
<div class="offset9 span2">
<div class="offset10 span2">
<a title="Add Profile" class="btn btn-primary" data-bind="click: addTemperatureProfile"><i class="icon-plus"></i></a>
</div>
</div>

View file

@ -52,8 +52,8 @@
<a class="accordion-toggle" data-toggle="collapse" data-target="#{{ data._div }}">
{% if "icon" in data %}<i class="icon-{{ data.icon }}"></i> {% endif %}{{ entry }}
</a>
{% if "header_addon" in data %}
{% include data.header_addon ignore missing %}
{% if "template_header" in data %}
{% include data.template_header ignore missing %}
{% endif %}
</div>
<div id="{{ data._div }}"

View file

@ -306,6 +306,11 @@ def find_collision_free_name(filename, extension, existing_filenames, max_power=
# TODO unit test!
if not isinstance(filename, unicode):
filename = unicode(filename)
if not isinstance(extension, unicode):
extension = unicode(extension)
def make_valid(text):
return re.sub(r"\s+", "_", text.translate({ord(i):None for i in ".\"/\\[]:;=,"})).lower()

View file

@ -115,7 +115,7 @@ class MachineCom(object):
STATE_ERROR = 9
STATE_CLOSED_WITH_ERROR = 10
STATE_TRANSFERING_FILE = 11
def __init__(self, port = None, baudrate=None, callbackObject=None, printerProfileManager=None):
self._logger = logging.getLogger(__name__)
self._serialLogger = logging.getLogger("SERIAL")
@ -162,6 +162,7 @@ class MachineCom(object):
self._lastCommError = None
self._lastResendNumber = None
self._currentResendCount = 0
self._resendSwallowNextOk = False
self._clear_to_send = CountedEvent(max=10, name="comm.clear_to_send")
self._send_queue = TypedQueue()
@ -255,7 +256,7 @@ class MachineCom(object):
def getState(self):
return self._state
def getStateString(self):
if self._state == self.STATE_NONE:
return "Offline"
@ -287,19 +288,19 @@ class MachineCom(object):
if self._state == self.STATE_TRANSFERING_FILE:
return "Transfering file to SD"
return "?%d?" % (self._state)
def getErrorString(self):
return self._errorValue
def isClosedOrError(self):
return self._state == self.STATE_ERROR or self._state == self.STATE_CLOSED_WITH_ERROR or self._state == self.STATE_CLOSED
def isError(self):
return self._state == self.STATE_ERROR or self._state == self.STATE_CLOSED_WITH_ERROR
def isOperational(self):
return self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PRINTING or self._state == self.STATE_PAUSED or self._state == self.STATE_TRANSFERING_FILE
def isPrinting(self):
return self._state == self.STATE_PRINTING
@ -349,7 +350,7 @@ class MachineCom(object):
def getTemp(self):
return self._temp
def getBedTemp(self):
return self._bedTemp
@ -552,6 +553,7 @@ class MachineCom(object):
self._currentFile = PrintingGcodeFileInformation(filename, offsets_callback=self.getOffsets, current_tool_callback=self.getCurrentTool)
eventManager().fire(Events.FILE_SELECTED, {
"file": self._currentFile.getFilename(),
"filename": os.path.basename(self._currentFile.getFilename()),
"origin": self._currentFile.getFileLocation()
})
self._callback.on_comm_file_selected(filename, self._currentFile.getFilesize(), False)
@ -836,7 +838,7 @@ class MachineCom(object):
filename = preprocessed_line
size = None
if valid_file_type(filename, "gcode"):
if valid_file_type(filename, "machinecode"):
if filter_non_ascii(filename):
self._logger.warn("Got a file from printer's SD that has a non-ascii filename (%s), that shouldn't happen according to the protocol" % filename)
else:
@ -1047,7 +1049,9 @@ class MachineCom(object):
elif self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED:
if "ok" in line:
# if we still have commands to process, process them
if self._resendDelta is not None:
if self._resendSwallowNextOk:
self._resendSwallowNextOk = False
elif self._resendDelta is not None:
self._resendNextCommand()
elif self._sendFromQueue():
pass
@ -1066,8 +1070,12 @@ class MachineCom(object):
self._logger.debug("Ran into a communication timeout, but a blocking command is currently active")
if "ok" in line:
if self._resendDelta is not None:
if self._resendSwallowNextOk:
self._resendSwallowNextOk = False
elif self._resendDelta is not None:
self._resendNextCommand()
else:
if self._sendFromQueue(sendChecksum=True):
pass
@ -1307,6 +1315,8 @@ class MachineCom(object):
lineToResend = int(line.split()[1])
if lineToResend is not None:
self._resendSwallowNextOk = True
lastCommError = self._lastCommError
self._lastCommError = None
@ -1338,7 +1348,6 @@ class MachineCom(object):
self._resendNextCommand()
def _resendNextCommand(self):
lastCommError = self._lastCommError
self._lastCommError = None
# Make sure we are only handling one sending job at a time

View file

@ -313,7 +313,7 @@ class VirtualPrinter():
def _selectSdFile(self, filename):
if filename.startswith("/"):
filename = filename[1:]
file = os.path.join(self._virtualSd, filename).lower()
file = os.path.join(self._virtualSd, filename.lower())
if not os.path.exists(file) or not os.path.isfile(file):
self.outgoing.put("open failed, File: %s." % filename)
else: