diff --git a/cura/PrinterOutput/GenericOutputController.py b/cura/PrinterOutput/GenericOutputController.py index a21425af92..470848c208 100644 --- a/cura/PrinterOutput/GenericOutputController.py +++ b/cura/PrinterOutput/GenericOutputController.py @@ -64,6 +64,9 @@ class GenericOutputController(PrinterOutputController): def homeBed(self, printer): self._output_device.sendCommand("G28 Z") + def sendRawCommand(self, printer: "PrinterOutputModel", command: str): + self._output_device.sendCommand(command) + def setJobState(self, job: "PrintJobOutputModel", state: str): if state == "pause": self._output_device.pausePrint() diff --git a/cura/PrinterOutput/PrinterOutputController.py b/cura/PrinterOutput/PrinterOutputController.py index b5b0dd0be3..58c6ef05a7 100644 --- a/cura/PrinterOutput/PrinterOutputController.py +++ b/cura/PrinterOutput/PrinterOutputController.py @@ -16,6 +16,7 @@ class PrinterOutputController: self.can_abort = True self.can_pre_heat_bed = True self.can_pre_heat_hotends = True + self.can_send_raw_gcode = True self.can_control_manually = True self._output_device = output_device @@ -46,8 +47,11 @@ class PrinterOutputController: def moveHead(self, printer: "PrinterOutputModel", x, y, z, speed): Logger.log("w", "Move head not implemented in controller") - def homeBed(self, printer): + def homeBed(self, printer: "PrinterOutputModel"): Logger.log("w", "Home bed not implemented in controller") - def homeHead(self, printer): - Logger.log("w", "Home head not implemented in controller") \ No newline at end of file + def homeHead(self, printer: "PrinterOutputModel"): + Logger.log("w", "Home head not implemented in controller") + + def sendRawCommand(self, printer: "PrinterOutputModel", command: str): + Logger.log("w", "Custom command not implemented in controller") diff --git a/cura/PrinterOutput/PrinterOutputModel.py b/cura/PrinterOutput/PrinterOutputModel.py index e936ace196..928a882c8c 100644 --- a/cura/PrinterOutput/PrinterOutputModel.py +++ b/cura/PrinterOutput/PrinterOutputModel.py @@ -110,6 +110,10 @@ class PrinterOutputModel(QObject): def homeBed(self): self._controller.homeBed(self) + @pyqtSlot(str) + def sendRawCommand(self, command: str): + self._controller.sendRawCommand(self, command) + @pyqtProperty("QVariantList", constant = True) def extruders(self): return self._extruders @@ -245,6 +249,13 @@ class PrinterOutputModel(QObject): return self._controller.can_pre_heat_hotends return False + # Does the printer support sending raw G-code at all + @pyqtProperty(bool, constant=True) + def canSendRawGcode(self): + if self._controller: + return self._controller.can_send_raw_gcode + return False + # Does the printer support pause at all @pyqtProperty(bool, constant=True) def canPause(self): diff --git a/plugins/UM3NetworkPrinting/ClusterUM3PrinterOutputController.py b/plugins/UM3NetworkPrinting/ClusterUM3PrinterOutputController.py index 076c4584af..707443b9ea 100644 --- a/plugins/UM3NetworkPrinting/ClusterUM3PrinterOutputController.py +++ b/plugins/UM3NetworkPrinting/ClusterUM3PrinterOutputController.py @@ -15,6 +15,7 @@ class ClusterUM3PrinterOutputController(PrinterOutputController): self.can_pre_heat_bed = False self.can_pre_heat_hotends = False self.can_control_manually = False + self.can_send_raw_gcode = False def setJobState(self, job: "PrintJobOutputModel", state: str): data = "{\"action\": \"%s\"}" % state diff --git a/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py b/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py index 7a0e113d5b..b12a31b6cf 100644 --- a/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py +++ b/plugins/UM3NetworkPrinting/LegacyUM3PrinterOutputController.py @@ -20,6 +20,7 @@ class LegacyUM3PrinterOutputController(PrinterOutputController): self._preheat_printer = None self.can_control_manually = False + self.can_send_raw_gcode = False # Are we still waiting for a response about preheat? # We need this so we can already update buttons, so it feels more snappy. diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 385977141c..9de66ad0be 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -1,3 +1,6 @@ +// Copyright (c) 2017 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 @@ -11,6 +14,7 @@ Item implicitWidth: parent.width height: visible ? UM.Theme.getSize("sidebar_extruder_box").height : 0 property var printerModel + Rectangle { color: UM.Theme.getColor("sidebar") @@ -209,7 +213,7 @@ Item } } - Button //The pre-heat button. + Button // The pre-heat button. { id: preheatButton height: UM.Theme.getSize("setting_control").height diff --git a/resources/qml/PrinterOutput/ManualPrinterControl.qml b/resources/qml/PrinterOutput/ManualPrinterControl.qml index 43fa769fb5..70961a2eb2 100644 --- a/resources/qml/PrinterOutput/ManualPrinterControl.qml +++ b/resources/qml/PrinterOutput/ManualPrinterControl.qml @@ -9,7 +9,6 @@ import QtQuick.Layouts 1.1 import UM 1.2 as UM import Cura 1.0 as Cura - Item { property var printerModel @@ -125,7 +124,6 @@ Item return true; } - MonitorSection { label: catalog.i18nc("@label", "Printer control") @@ -429,6 +427,120 @@ Item } } + Row + { + id: customCommandInputRow + + width: base.width - 2 * UM.Theme.getSize("default_margin").width + height: childrenRect.height + UM.Theme.getSize("default_margin").width + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + + spacing: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "Send G-code") + color: UM.Theme.getColor("setting_control_text") + font: UM.Theme.getFont("default") + + width: Math.floor(parent.width * 0.4) - UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("setting_control").height + verticalAlignment: Text.AlignVCenter + } + + Row + { + // Input field for custom G-code commands. + Rectangle + { + id: customCommandControl + + // state + visible: printerModel != null ? printerModel.canSendRawGcode: true + enabled: { + if (printerModel == null) { + return false // Can't send custom commands if not connected. + } + if (!connectedPrinter.acceptsCommands) { + return false // Not allowed to do anything + } + if (connectedPrinter.jobState == "printing" || connectedPrinter.jobState == "pre_print" || connectedPrinter.jobState == "resuming" || connectedPrinter.jobState == "pausing" || connectedPrinter.jobState == "paused" || connectedPrinter.jobState == "error" || connectedPrinter.jobState == "offline") { + return false // Printer is in a state where it can't react to custom commands. + } + return true + } + + // style + color: !enabled ? UM.Theme.getColor("setting_control_disabled") : UM.Theme.getColor("setting_validation_ok") + border.width: UM.Theme.getSize("default_lining").width + border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : customCommandControlMouseArea.containsMouse ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") + + // size + width: UM.Theme.getSize("setting_control").width + height: UM.Theme.getSize("setting_control").height + + // highlight + Rectangle + { + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("setting_control_highlight") + opacity: customCommandControl.hovered ? 1.0 : 0 + } + + // cursor hover popup + MouseArea + { + id: customCommandControlMouseArea + hoverEnabled: true + anchors.fill: parent + cursorShape: Qt.IBeamCursor + + onHoveredChanged: + { + if (containsMouse) { + base.showTooltip( + base, + { x: 0, y: customCommandControlMouseArea.mapToItem(base, 0, 0).y }, + catalog.i18nc("@tooltip of G-code command input", "Send a custom G-code command to the connected printer. Press 'enter' to send the command.") + ) + } else { + base.hideTooltip() + } + } + } + + TextInput + { + id: customCommandControlInput + + // style + font: UM.Theme.getFont("default") + color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text") + selectByMouse: true + clip: true + enabled: parent.enabled + renderType: Text.NativeRendering + + // anchors + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("setting_unit_margin").width + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + // send the command when pressing enter + // we also clear the text field + Keys.onReturnPressed: + { + printerModel.sendRawCommand(customCommandControlInput.text) + customCommandControlInput.text = "" + } + } + } + } + } + ListModel { id: distancesModel