From ad556e7413c8f46f1b731c24ec4544d1b9d4298c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Mon, 6 Jan 2014 00:16:33 +0100 Subject: [PATCH] More work on the GCode viewer - confirmation dialog when trying to visualize large files (different threshold for mobile and "regular" devices, configurable of course, although only via config file right now) - should also help a bit with issues leading to #215 - proper clearing of the viewer area when reconnecting to the backend (e.g. after server restart) Also all of this plus previous commits closes #35 --- src/octoprint/printer.py | 2 +- src/octoprint/server/__init__.py | 6 +- src/octoprint/server/api/settings.py | 4 +- src/octoprint/settings.py | 6 +- .../static/gcodeviewer/js/gCodeReader.js | 8 +- .../static/gcodeviewer/js/renderer.js | 169 +++++++++--------- src/octoprint/static/gcodeviewer/js/ui.js | 17 +- .../static/js/app/viewmodels/gcode.js | 72 ++++++-- src/octoprint/templates/index.jinja2 | 137 ++++++++------ 9 files changed, 250 insertions(+), 171 deletions(-) diff --git a/src/octoprint/printer.py b/src/octoprint/printer.py index fe728fac..c42c5b17 100644 --- a/src/octoprint/printer.py +++ b/src/octoprint/printer.py @@ -486,7 +486,7 @@ class Printer(): self._stateMonitor.setState({"state": self._state, "stateString": self.getStateString(), "flags": self._getStateFlags()}) def mcSdFiles(self, files): - eventManager().fire(Events.UPDATED_FILES, {"type": "gcode", "files": files}) + eventManager().fire(Events.UPDATED_FILES, {"type": "gcode"}) self._sdFilelistAvailable.set() def mcFileSelected(self, filename, filesize, sd): diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 00c75537..1fccc3e3 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -51,7 +51,7 @@ def index(): "index.jinja2", webcamStream=settings().get(["webcam", "stream"]), enableTimelapse=(settings().get(["webcam", "snapshot"]) is not None and settings().get(["webcam", "ffmpeg"]) is not None), - enableGCodeVisualizer=settings().get(["feature", "gCodeVisualizer"]), + enableGCodeVisualizer=settings().get(["gcodeViewer", "enabled"]), enableTemperatureGraph=settings().get(["feature", "temperatureGraph"]), enableSystemMenu=settings().get(["system"]) is not None and settings().get(["system", "actions"]) is not None and len(settings().get(["system", "actions"])) > 0, enableAccessControl=userManager is not None, @@ -60,7 +60,9 @@ def index(): debug=debug, gitBranch=branch, gitCommit=commit, - stylesheet=settings().get(["devel", "stylesheet"]) + stylesheet=settings().get(["devel", "stylesheet"]), + gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]), + gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]) ) diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index 04cbd519..53b65ae4 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -54,7 +54,7 @@ def getSettings(): "flipV": s.getBoolean(["webcam", "flipV"]) }, "feature": { - "gcodeViewer": s.getBoolean(["feature", "gCodeVisualizer"]), + "gcodeViewer": s.getBoolean(["gcodeViewer", "enabled"]), "temperatureGraph": s.getBoolean(["feature", "temperatureGraph"]), "waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]), "alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]), @@ -130,7 +130,7 @@ def setSettings(): if "flipV" in data["webcam"].keys(): s.setBoolean(["webcam", "flipV"], data["webcam"]["flipV"]) if "feature" in data.keys(): - if "gcodeViewer" in data["feature"].keys(): s.setBoolean(["feature", "gCodeVisualizer"], data["feature"]["gcodeViewer"]) + if "gcodeViewer" in data["feature"].keys(): s.setBoolean(["gcodeViewer", "enabled"], data["feature"]["gcodeViewer"]) if "temperatureGraph" in data["feature"].keys(): s.setBoolean(["feature", "temperatureGraph"], data["feature"]["temperatureGraph"]) if "waitForStart" in data["feature"].keys(): s.setBoolean(["feature", "waitForStartOnConnect"], data["feature"]["waitForStart"]) if "alwaysSendChecksum" in data["feature"].keys(): s.setBoolean(["feature", "alwaysSendChecksum"], data["feature"]["alwaysSendChecksum"]) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 19798fa2..2dd49557 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -55,8 +55,12 @@ default_settings = { "options": {} } }, + "gcodeViewer": { + "enabled": True, + "mobileSizeThreshold": 2 * 1024 * 1024, # 2MB + "sizeThreshold": 20 * 1024 * 1024, # 20MB + }, "feature": { - "gCodeVisualizer": True, "temperatureGraph": True, "waitForStartOnConnect": False, "alwaysSendChecksum": False, diff --git a/src/octoprint/static/gcodeviewer/js/gCodeReader.js b/src/octoprint/static/gcodeviewer/js/gCodeReader.js index c1f9008b..d0863642 100644 --- a/src/octoprint/static/gcodeviewer/js/gCodeReader.js +++ b/src/octoprint/static/gcodeviewer/js/gCodeReader.js @@ -120,13 +120,12 @@ GCODE.gCodeReader = (function(){ // ***** PUBLIC ******* return { clear: function() { - delete lines; - delete gcode; + model = []; + z_heights = []; }, loadFile: function(reader){ - model = []; - z_heights = []; + this.clear(); var totalSize = reader.target.result.length; lines = reader.target.result.split(/\n/); @@ -144,7 +143,6 @@ GCODE.gCodeReader = (function(){ } } ); - this.clear(); }, setOption: function(options){ diff --git a/src/octoprint/static/gcodeviewer/js/renderer.js b/src/octoprint/static/gcodeviewer/js/renderer.js index 5e1c93c0..a0285d9a 100644 --- a/src/octoprint/static/gcodeviewer/js/renderer.js +++ b/src/octoprint/static/gcodeviewer/js/renderer.js @@ -17,10 +17,10 @@ GCODE.renderer = (function(){ var layerNumStore, progressStore={from: 0, to: -1}; var lastX, lastY; - var dragStart,dragged; + var dragStart, dragged; var scaleFactor = 1.1; var model; - var initialized=false; + var initialized = false; var renderOptions = { colorGrid: "#bbbbbb", bgColorGrid: "#ffffff", @@ -55,7 +55,6 @@ GCODE.renderer = (function(){ var speeds = []; var speedsByLayer = {}; - var reRender = function(){ var p1 = ctx.transformedPoint(0,0); var p2 = ctx.transformedPoint(canvas.width,canvas.height); @@ -183,8 +182,11 @@ GCODE.renderer = (function(){ }; var drawGrid = function() { + console.log("Drawing grid"); + ctx.translate(offsetBedX, offsetBedY); + ctx.beginPath(); var width = renderOptions["bed"]["x"] * zoomFactor; var height = renderOptions["bed"]["y"] * zoomFactor; var origin = { @@ -220,6 +222,8 @@ GCODE.renderer = (function(){ }; var drawLayer = function(layerNum, fromProgress, toProgress, isNotCurrentLayer){ + console.log("Drawing layer " + layerNum + " from " + fromProgress + " to " + toProgress + " (current: " + !isNotCurrentLayer + ")"); + var i; isNotCurrentLayer = typeof isNotCurrentLayer !== 'undefined' ? isNotCurrentLayer : false; @@ -279,7 +283,7 @@ GCODE.renderer = (function(){ x = cmds[i].x; } if (typeof(cmds[i].y) === 'undefined' || isNaN(cmds[i].y)) { - y=prevY/zoomFactor; + y = prevY / zoomFactor; } else { y = -cmds[i].y; } @@ -329,30 +333,58 @@ GCODE.renderer = (function(){ ctx.stroke(); }; - /* - var calculateBedOffset = function(bedDimensions) { - if (!bedDimensions) bedDimensions = renderOptions["bed"]; - - var max = Math.max(bedDimensions.x, bedDimensions.y); - return { - x: (max - bedDimensions.x) / 2 * zoomFactor, - y: (max - bedDimensions.y) / 2 * zoomFactor - }; + var applyOffsets = function(mdlInfo) { + // determine bed and model offsets + if (ctx) ctx.translate(-offsetModelX, -offsetModelY); + if (renderOptions["centerViewport"] || renderOptions["zoomInOnModel"]) { + var canvasCenter = ctx.transformedPoint(canvas.width / 2, canvas.height / 2); + if (mdlInfo) { + offsetModelX = canvasCenter.x - (mdlInfo.min.x + mdlInfo.modelSize.x / 2) * zoomFactor; + offsetModelY = canvasCenter.y + (mdlInfo.min.y + mdlInfo.modelSize.y / 2) * zoomFactor; + } else { + offsetModelX = 0; + offsetModelY = 0; + } + offsetBedX = 0; + offsetBedY = 0; + } else if (mdlInfo && renderOptions["moveModel"]) { + offsetModelX = (renderOptions["bed"]["x"] / 2 - (mdlInfo.min.x + mdlInfo.modelSize.x / 2)) * zoomFactor; + offsetModelY = -1 * (renderOptions["bed"]["y"] / 2 - (mdlInfo.min.y + mdlInfo.modelSize.y / 2)) * zoomFactor; + offsetBedX = -1 * (renderOptions["bed"]["x"] / 2 - (mdlInfo.min.x + mdlInfo.modelSize.x / 2)) * zoomFactor; + offsetBedY = (renderOptions["bed"]["y"] / 2 - (mdlInfo.min.y + mdlInfo.modelSize.y / 2)) * zoomFactor; + } else { + offsetModelX = 0; + offsetModelY = 0; + offsetBedX = 0; + offsetBedY = 0; + } + if (ctx) ctx.translate(offsetModelX, offsetModelY); }; - var applyBedOffset = function(bedDimensions, onlyNew) { - if (true) return; - var bedOffset = calculateBedOffset(bedDimensions); - - if (offsetBedX == bedOffset.x && offsetBedY == bedOffset.y) return; - - ctx.translate(-offsetBedX, offsetBedY); - offsetBedX = bedOffset.x; - offsetBedY = bedOffset.y; - ctx.translate(bedOffset.x, -bedOffset.y); - } - */ - + var applyZoom = function(mdlInfo) { + var pt = ctx.transformedPoint(canvas.width/2,canvas.height/2); + var transform = ctx.getTransform(); + var scaleF; + if (scaleX && scaleY && transform.a && transform.d) { + ctx.translate(pt.x, pt.y); + ctx.scale(1 / scaleX, 1 / scaleY); + ctx.translate(-pt.x, -pt.y); + } + if (mdlInfo && renderOptions["zoomInOnModel"]) { + scaleF = mdlInfo.modelSize.x > mdlInfo.modelSize.y ? (canvas.width - 10) / mdlInfo.modelSize.x : (canvas.height - 10) / mdlInfo.modelSize.y; + scaleF /= zoomFactor; + if (transform.a && transform.d) { + scaleX = scaleF / transform.a; + scaleY = scaleF / transform.d; + ctx.translate(pt.x,pt.y); + ctx.scale(scaleX, scaleY); + ctx.translate(-pt.x, -pt.y); + } + } else { + scaleX = 1; + scaleY = 1; + } + }; // ***** PUBLIC ******* return { @@ -366,22 +398,24 @@ GCODE.renderer = (function(){ offsetModelY = 0; offsetBedX = 0; offsetBedY = 0; - - //applyBedOffset(); }, setOption: function(options){ var mustRefresh = false; + var dirty = false; for (var opt in options) { + if (!options.hasOwnProperty(opt)) continue; if (options[opt] === undefined) continue; - if (options.hasOwnProperty(opt)) renderOptions[opt] = options[opt]; + + dirty = dirty || (renderOptions[opt] != options[opt]); + renderOptions[opt] = options[opt]; if ($.inArray(opt, ["moveModel", "centerViewport", "zoomInOnModel", "bed"])) { mustRefresh = true; } } + if (!dirty) return; if(initialized) { if (mustRefresh) { - //applyBedOffset(); this.refresh(); } else { reRender(); @@ -401,7 +435,7 @@ GCODE.renderer = (function(){ var p2 = ctx.transformedPoint(canvas.width, canvas.height); ctx.clearRect(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y); drawGrid(); - if (model) { + if (model && model.length) { if (layerNum < model.length) { if (renderOptions['showNextLayer'] && layerNum < model.length - 1) { drawLayer(layerNum + 1, 0, this.getLayerNumSegments(layerNum + 1), true); @@ -426,68 +460,39 @@ GCODE.renderer = (function(){ } }, clear: function() { - model = undefined; - initialized = false; - this.render(); + offsetModelX = 0; + offsetModelY = 0; + offsetBedX = 0; + offsetBedY = 0; + scaleX = 1; + scaleY = 1; + speeds = []; + speedsByLayer = {}; + + this.doRender([], 0); }, doRender: function(mdl, layerNum){ model = mdl; - if (!model || !model[layerNum]) return; - var mdlInfo; + var mdlInfo = undefined; prevX = 0; prevY = 0; if (!initialized) this.init(); - mdlInfo = GCODE.gCodeReader.getModelInfo(); - speeds = mdlInfo.speeds; - speedsByLayer = mdlInfo.speedsByLayer; - - // determine bed and model offsets - if (ctx) ctx.translate(-offsetModelX, -offsetModelY); - if (renderOptions["centerViewport"] || renderOptions["zoomInOnModel"]) { - var canvasCenter = ctx.transformedPoint(canvas.width / 2, canvas.height / 2); - offsetModelX = canvasCenter.x - (mdlInfo.min.x + mdlInfo.modelSize.x / 2) * zoomFactor; - offsetModelY = canvasCenter.y + (mdlInfo.min.y + mdlInfo.modelSize.y / 2) * zoomFactor; - offsetBedX = 0; - offsetBedY = 0; - } else if (renderOptions["moveModel"]) { - offsetModelX = (renderOptions["bed"]["x"] / 2 - (mdlInfo.min.x + mdlInfo.modelSize.x / 2)) * zoomFactor; - offsetModelY = -1 * (renderOptions["bed"]["y"] / 2 - (mdlInfo.min.y + mdlInfo.modelSize.y / 2)) * zoomFactor; - offsetBedX = -1 * (renderOptions["bed"]["x"] / 2 - (mdlInfo.min.x + mdlInfo.modelSize.x / 2)) * zoomFactor; - offsetBedY = (renderOptions["bed"]["y"] / 2 - (mdlInfo.min.y + mdlInfo.modelSize.y / 2)) * zoomFactor; - } else { - offsetModelX = 0; - offsetModelY = 0; - offsetBedX = 0; - offsetBedY = 0; - } - if (ctx) ctx.translate(offsetModelX, offsetModelY); - - var pt = ctx.transformedPoint(canvas.width/2,canvas.height/2); - var transform = ctx.getTransform(); - var scaleF; - if (scaleX && scaleY && transform.a && transform.d) { - ctx.translate(pt.x, pt.y); - ctx.scale(1 / scaleX, 1 / scaleY); - ctx.translate(-pt.x, -pt.y); - } - if (renderOptions["zoomInOnModel"]) { - scaleF = mdlInfo.modelSize.x > mdlInfo.modelSize.y ? (canvas.width - 10) / mdlInfo.modelSize.x : (canvas.height - 10) / mdlInfo.modelSize.y; - scaleF /= zoomFactor; - if (transform.a && transform.d) { - scaleX = scaleF / transform.a; - scaleY = scaleF / transform.d; - ctx.translate(pt.x,pt.y); - ctx.scale(scaleX, scaleY); - ctx.translate(-pt.x, -pt.y); + var toProgress = 1; + if (model) { + mdlInfo = GCODE.gCodeReader.getModelInfo(); + speeds = mdlInfo.speeds; + speedsByLayer = mdlInfo.speedsByLayer; + if (model[layerNum]) { + toProgress = model[layerNum].length; } - } else { - scaleX = 1; - scaleY = 1; } - this.render(layerNum, 0, model[layerNum].length); + applyOffsets(mdlInfo); + applyZoom(mdlInfo); + + this.render(layerNum, 0, toProgress); }, refresh: function(layerNum) { if (!layerNum) layerNum = layerNumStore; diff --git a/src/octoprint/static/gcodeviewer/js/ui.js b/src/octoprint/static/gcodeviewer/js/ui.js index 65efc3e8..8f5a42fb 100644 --- a/src/octoprint/static/gcodeviewer/js/ui.js +++ b/src/octoprint/static/gcodeviewer/js/ui.js @@ -7,8 +7,6 @@ var GCODE = {}; GCODE.ui = (function(){ - var gCodeLines = {first: 0, last: 0}; - var uiOptions = { container: undefined, toolOffsets: undefined, @@ -28,7 +26,6 @@ GCODE.ui = (function(){ if (!onlyInfo) { var segmentCount = GCODE.renderer.getLayerNumSegments(layerNum); GCODE.renderer.render(layerNum, 0, segmentCount - 1); - gCodeLines = GCODE.gCodeReader.getGCodeLines(layerNum, 0, segmentCount - 1); } if (uiOptions["onLayerSelected"]) { @@ -45,7 +42,6 @@ GCODE.ui = (function(){ }; var switchCommands = function(layerNum, first, last) { - gCodeLines = GCODE.gCodeReader.getGCodeLines(layerNum, first, last); GCODE.renderer.render(layerNum, first, last); }; @@ -166,11 +162,16 @@ GCODE.ui = (function(){ }, clear: function() { - GCODE.renderer.clear(); GCODE.gCodeReader.clear(); - delete this.worker; + GCODE.renderer.clear(); - this.init(uiOptions); + setProgress("", 0); + if (uiOptions["onLayerSelected"]) { + uiOptions.onLayerSelected(); + } + if (uiOptions["onModelLoaded"]) { + uiOptions.onModelLoaded(); + } }, updateLayerInfo: function(layerNum){ @@ -185,12 +186,10 @@ GCODE.ui = (function(){ changeSelectedLayer: function(newLayerNum) { switchLayer(newLayerNum); - if (callback) callback(newLayerNum, segmentCount); }, changeSelectedCommands: function(layerNum, first, last) { switchCommands(layerNum, first, last); - if (callback) callback(first, last); } } }()); \ No newline at end of file diff --git a/src/octoprint/static/js/app/viewmodels/gcode.js b/src/octoprint/static/js/app/viewmodels/gcode.js index 27b437a7..7bc7e714 100644 --- a/src/octoprint/static/js/app/viewmodels/gcode.js +++ b/src/octoprint/static/js/app/viewmodels/gcode.js @@ -28,6 +28,15 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { self.ui_modelInfo = ko.observable(""); self.ui_layerInfo = ko.observable(""); + self.enableReload = ko.observable(false); + + self.waitForApproval = ko.observable(false); + self.selectedFile = { + name: ko.observable(undefined), + date: ko.observable(undefined), + size: ko.observable(undefined) + }; + self.renderer_centerModel = ko.observable(false); self.renderer_centerViewport = ko.observable(false); self.renderer_zoomOnModel = ko.observable(false); @@ -116,6 +125,9 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { self.layerSlider = undefined; self.layerCommandSlider = undefined; + self.currentLayer = undefined; + self.currentCommand = undefined; + self.initialize = function() { self._configureLayerSlider(); self._configureLayerCommandSlider(); @@ -136,13 +148,16 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { self.reinitialize = function() { self.enabled = false; + self.enableReload(false); self.loadedFilename = undefined; self.loadedFileDate = undefined; - GCODE.ui.clear(); - - self.initialize(); + self.clear(); }; + self.clear = function() { + GCODE.ui.clear(); + } + self._configureLayerSlider = function() { self.layerSlider = $("#gcode_slider_layers").slider({ id: "gcode_layer_slider", @@ -172,6 +187,7 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { }; self.loadFile = function(filename, date){ + self.enableReload(false); if (self.status == "idle" && self.errorCount < 3) { self.status = "request"; $.ajax({ @@ -184,13 +200,14 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { self.loadedFilename = filename; self.loadedFileDate = date; self.status = "idle"; + self.enableReload(true); } }, error: function() { self.status = "idle"; self.errorCount++; } - }) + }); } }; @@ -206,7 +223,8 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { self.layerCommandSlider.slider("disable"); }; - self.refresh = function() { + self.reload = function() { + if (!self.enableReload()) return; self.loadFile(self.loadedFilename, self.loadedFileDate); }; @@ -219,19 +237,25 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { }; self._processData = function(data) { - if (!self.enabled) return; if (!data.job.file || !data.job.file.name && (self.loadedFilename || self.loadedFileDate)) { + self.waitForApproval(false); + self.loadedFilename = undefined; self.loadedFileDate = undefined; - GCODE.renderer.clear(); + self.selectedFile.name(undefined); + self.selectedFile.date(undefined); + self.selectedFile.size(undefined); + + self.clear(); return; } + if (!self.enabled) return; self.currentlyPrinting = data.state.flags && (data.state.flags.printing || data.state.flags.paused); if(self.loadedFilename && self.loadedFilename == data.job.file.name && self.loadedFileDate == data.job.file.date) { - if (self.currentlyPrinting && self.renderer_syncProgress()) { + if (self.currentlyPrinting && self.renderer_syncProgress() && !self.waitForApproval()) { var cmdIndex = GCODE.gCodeReader.getCmdIndexForPercentage(data.progress.completion); if(cmdIndex){ GCODE.renderer.render(cmdIndex.layer, 0, cmdIndex.cmd); @@ -242,11 +266,31 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { } } self.errorCount = 0 - } else if (data.job.file.name && data.job.file.origin != "sdcard") { - self.loadFile(data.job.file.name, data.job.file.date); + } else { + self.clear(); + if (data.job.file.name && data.job.file.origin != "sdcard" + && (!self.waitForApproval() || (self.selectedFile.name() != data.job.file.name || self.selectedFile.date() != data.job.file.date))) { + self.selectedFile.name(data.job.file.name); + self.selectedFile.date(data.job.file.date); + self.selectedFile.size(data.job.file.size); + + if (data.job.file.size > CONFIG_GCODE_SIZE_THRESHOLD || ($.browser.mobile && data.job.file.size > CONFIG_GCODE_MOBILE_SIZE_THRESHOLD)) { + self.waitForApproval(true); + self.loadedFilename = undefined; + self.loadedFileDate = undefined; + } else { + self.waitForApproval(false); + self.loadFile(data.job.file.name, data.job.file.date); + } + } } }; + self.approveLargeFile = function() { + self.waitForApproval(false); + self.loadFile(self.selectedFile.name(), self.selectedFile.date()); + } + self._onProgress = function(type, percentage) { self.ui_progress_type(type); self.ui_progress_percentage(percentage); @@ -258,6 +302,7 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { self.layerSlider.slider("disable"); self.layerSlider.slider("setMax", 1); self.layerSlider.slider("setValue", 0); + self.currentLayer = 0; } else { var output = []; output.push("Model size is: " + model.width.toFixed(2) + "mm × " + model.depth.toFixed(2) + "mm × " + model.height.toFixed(2) + "mm"); @@ -286,6 +331,7 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { self.layerCommandSlider.slider("disable"); self.layerCommandSlider.slider("setMax", 1); self.layerCommandSlider.slider("setValue", [0, 1]); + self.currentCommand = [0, 1]; } else { var output = []; output.push("Layer number: " + layer.number); @@ -312,6 +358,9 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { if (self.currentlyPrinting && self.renderer_syncProgress()) self.renderer_syncProgress(false); var value = event.value; + if (self.currentLayer !== undefined && self.currentLayer == value) return; + self.currentLayer = value; + GCODE.ui.changeSelectedLayer(value); }; @@ -319,6 +368,9 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) { if (self.currentlyPrinting && self.renderer_syncProgress()) self.renderer_syncProgress(false); var tuple = event.value; + if (self.currentCommand !== undefined && self.currentCommand[0] == tuple[0] && self.currentCommand[1] == tuple[1]) return; + self.currentCommand = tuple; + GCODE.ui.changeSelectedCommands(self.layerSlider.slider("getValue"), tuple[0], tuple[1]); }; } diff --git a/src/octoprint/templates/index.jinja2 b/src/octoprint/templates/index.jinja2 index 1ea300ef..17f25b44 100644 --- a/src/octoprint/templates/index.jinja2 +++ b/src/octoprint/templates/index.jinja2 @@ -7,18 +7,18 @@ - {% if stylesheet == "less" %} - - - {% else %} - - {% endif %} + {% if stylesheet == "less" %} + + + {% else %} + + {% endif %} +