Merge branch 'devel' into dev/pluginLifecycleMgmt
This commit is contained in:
commit
fc00ef032e
15 changed files with 138 additions and 69 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
4
setup.py
4
setup.py
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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">×</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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 }}"
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in a new issue