[feature] Parameterized macros (#16)

Adds the option to have parameters in Macros and show a dialog to the user on execution to change them.
Here's and example:

PID_CALIBRATE 
HEATER={label:Heater, default:extruder, options:extruder|extruder1}
TARGET={label:Temperature, unit:°C, default:190}
WRITE_FILE={label:WriteFile, default:0, options:0|1}
This commit is contained in:
mmone 2018-08-25 16:55:48 +02:00 committed by GitHub
parent 33ae991612
commit 769b8a53ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 158 additions and 5 deletions

View File

@ -209,6 +209,12 @@ class KlipperPlugin(
name="Performance Graph",
template="klipper_graph_dialog.jinja2",
custom_bindings=True
),
dict(
type="generic",
name="Macro Dialog",
template="klipper_param_macro_dialog.jinja2",
custom_bindings=True
)
]
@ -221,6 +227,7 @@ class KlipperPlugin(
"js/klipper_leveling.js",
"js/klipper_pid_tuning.js",
"js/klipper_offset.js",
"js/klipper_param_macro.js",
"js/klipper_graph.js"
],
css=["css/klipper.css"],

View File

@ -12,6 +12,7 @@ $(function() {
self.loginState = parameters[1];
self.connectionState = parameters[2];
self.levelingViewModel = parameters[3];
self.paramMacroViewModel = parameters[4];
self.shortStatus = ko.observable();
self.logMessages = ko.observableArray();
@ -53,7 +54,21 @@ $(function() {
}
self.executeMacro = function(macro) {
OctoPrint.control.sendGcode(macro.macro());
var paramObjRegex = /{(.*?)}/g;
if (macro.macro().match(paramObjRegex) == null) {
OctoPrint.control.sendGcode(
macro.macro().replace(/(?:\r\n|\r|\n)/g, " ")
);
} else {
self.paramMacroViewModel.process(macro);
var dialog = $("#klipper_macro_dialog");
dialog.modal({
show: 'true',
backdrop: 'static'
});
}
}
self.onGetStatus = function() {
@ -97,7 +112,13 @@ $(function() {
OCTOPRINT_VIEWMODELS.push({
construct: KlipperViewModel,
dependencies: ["settingsViewModel", "loginStateViewModel", "connectionViewModel", "klipperLevelingViewModel"],
dependencies: [
"settingsViewModel",
"loginStateViewModel",
"connectionViewModel",
"klipperLevelingViewModel",
"klipperMacroDialogViewModel"
],
elements: ["#tab_plugin_klipper_main", "#sidebar_plugin_klipper", "#navbar_plugin_klipper"]
});
});

View File

@ -0,0 +1,76 @@
// OctoPrint Klipper Plugin
//
// Copyright (C) 2018 Martin Muehlhaeuser <github@mmone.de>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
$(function() {
function KlipperMacroDialogViewModel(parameters) {
var self = this;
self.parameters = ko.observableArray();
self.interpolatedCmd;
self.macro;
self.macroName = ko.observable();
var paramObjRegex = /{(.*?)}/g;
var keyValueRegex = /(\w*)\s*:\s*([\w°"|]*)/g;
self.process = function(macro) {
self.macro = macro.macro();
self.macroName(macro.name());
var matches = self.macro.match(paramObjRegex);
var params = [];
for (var i=0; i < matches.length; i++) {
var obj = {};
var res = keyValueRegex.exec(matches[i]);
while (res != null) {
if("options" == res[1]) {
obj["options"] = res[2].split("|");
} else {
obj[res[1]] = res[2];
}
res = keyValueRegex.exec(matches[i]);
}
if(!("label" in obj)) {
obj["label"] = "Input " + (i+1);
}
if(!("unit" in obj)) {
obj["unit"] = "";
}
if("default" in obj) {
obj["value"] = obj["default"];
}
params.push(obj);
}
self.parameters(params);
}
self.executeMacro = function() {
var i=-1;
function replaceParams(match) {
i++;
return self.parameters()[i]["value"];
}
expanded = self.macro.replace(paramObjRegex, replaceParams)
expanded = expanded.replace(/(?:\r\n|\r|\n)/g, " ");
OctoPrint.control.sendGcode(expanded);
}
}
OCTOPRINT_VIEWMODELS.push({
construct: KlipperMacroDialogViewModel,
dependencies: [],
elements: ["#klipper_macro_dialog"]
});
});

View File

@ -0,0 +1,32 @@
<div id="klipper_macro_dialog" class="modal hide fade small" tabindex="-1" role="dialog" aria-labelledby="klipper_macro_dialog_label" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h3 id="klipper_pid_tuning_dialog_label">{{ _('Run - ') }}<span data-bind="text: macroName"></span></h3>
</div>
<div class="modal-body">
<div class="control-group" data-bind="foreach: parameters">
<label class="control-label" data-bind="text: label"></label>
<div class="controls">
<!-- ko if: $data.hasOwnProperty("options") -->
<div class="input-append">
<select data-bind="options: options, value: value"></select>
<span class="add-on" data-bind="text: unit"></span>
</div>
<!-- /ko -->
<!-- ko if: !$data.hasOwnProperty("options") -->
<div class="input-append">
<input type="text" class="input-block-level span2" data-bind="value: value">
<span class="add-on" data-bind="text: unit"></span>
</div>
<!-- /ko -->
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-block" data-bind="click: executeMacro" data-dismiss="modal">
<i class="icon-cross"></i> {{ _('OK') }}
</button>
</div>
</div>
</div>
</div>

View File

@ -6,6 +6,7 @@
<li><a href="#conf" data-toggle="tab">Klipper Configuration</a></li>
</ul>
<div class="tab-content">
<!-- Basics -->
<div class="tab-pane active" id="basic">
<div class="control-group">
<label class="control-label">{{ _('Serial Port') }}</label>
@ -38,6 +39,7 @@
</div>
</div>
</div>
<!-- Macros -->
<div class="tab-pane" id="macros">
<div class="control-group">
<div class="controls">
@ -69,7 +71,7 @@
</div>
</div>
</div>
<label class="control-label">{{ _('GCODE') }}</label>
<label class="control-label">{{ _('Command') }}</label>
<div class="controls">
<div class="row-fluid">
<div class="span8">
@ -86,7 +88,21 @@
<a href='#' data-bind='click: addMacro' class="fa fa-plus-circle"></a> {{ _('Add Macro') }}
</div>
</div>
<div class="control-group">
<span class="help-block">
To show a dialog that asks for parameters you can write your macro like in the following example:<br>
</span>
</div>
<div class="control-group">
<pre>
PID_CALIBRATE
HEATER={label:Heater, default:extruder, options:extruder|extruder1}
TARGET={label:Temperature, unit:°C, default:190}
WRITE_FILE={label:WriteFile, default:0, options:0|1}
</pre>
</div>
</div>
<!-- Leveling -->
<div class="tab-pane" id="level">
<div class="control-group">
<span class="help-block">
@ -164,6 +180,7 @@
</div>
</div>
</div>
<!-- Klipper Conf -->
<div class="tab-pane" id="conf">
<div class="control-group">
<textarea id="plugin-klipper-config" rows="31" class="block" data-bind="value: settings.settings.plugins.klipper.config"></textarea>

View File

@ -10,7 +10,7 @@
&nbsp;
<button class="btn btn-mini pull-right clear-btn" data-bind="click: onClearLog, enable: isActive()"
title="Clear Log">
<i class="fa fa-trash"></i> {{ _('Clear') }}
<i class="fa fa-trash"></i>{{ _('Clear') }}
</button>
</div>
<div class="span4">