From d16da3da3ad207915b9d55abb37f902461249bf0 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 29 Apr 2019 14:33:48 +0200 Subject: [PATCH 01/30] Add group_id and fix remove printer CURA-6483 - Added a unique group_id (a GUID) to all created GlobalStack. - Changed version upgrade to generate unique group_ids for GlobalStacks. - RemoveMachine() now uses group_ids to remove hidden GlobalStacks. --- cura/Settings/GlobalStack.py | 3 +++ cura/Settings/MachineManager.py | 7 ++++--- .../VersionUpgrade40to41/VersionUpgrade40to41.py | 9 +++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index 3ec73972dd..a8e6f2f2c0 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -4,6 +4,8 @@ from collections import defaultdict import threading from typing import Any, Dict, Optional, Set, TYPE_CHECKING, List +import uuid + from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal from UM.Decorators import override @@ -33,6 +35,7 @@ class GlobalStack(CuraContainerStack): super().__init__(container_id) self.setMetaDataEntry("type", "machine") # For backward compatibility + self.setMetaDataEntry("group_id", str(uuid.uuid4())) # Assign a new GlobalStack to a unique group by default self._extruders = {} # type: Dict[str, "ExtruderStack"] diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 24d63d5164..5db0a702a1 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -797,7 +797,6 @@ class MachineManager(QObject): self.setActiveMachine(other_machine_stacks[0]["id"]) metadata = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0] - network_key = metadata.get("um_network_key", None) ExtruderManager.getInstance().removeMachineExtruders(machine_id) containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id) for container in containers: @@ -805,8 +804,9 @@ class MachineManager(QObject): CuraContainerRegistry.getInstance().removeContainer(machine_id) # If the printer that is being removed is a network printer, the hidden printers have to be also removed - if network_key: - metadata_filter = {"um_network_key": network_key} + group_id = metadata.get("group_id", None) + if group_id: + metadata_filter = {"group_id": group_id} hidden_containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter) if hidden_containers: # This reuses the method and remove all printers recursively @@ -1366,6 +1366,7 @@ class MachineManager(QObject): new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id) if not new_machine: return + new_machine.setMetaDataEntry("group_id", self._global_container_stack.getMetaDataEntry("group_id")) new_machine.setMetaDataEntry("um_network_key", self.activeMachineNetworkKey()) new_machine.setMetaDataEntry("group_name", self.activeMachineNetworkGroupName) new_machine.setMetaDataEntry("hidden", False) diff --git a/plugins/VersionUpgrade/VersionUpgrade40to41/VersionUpgrade40to41.py b/plugins/VersionUpgrade/VersionUpgrade40to41/VersionUpgrade40to41.py index 03272e63d5..845e9cbb8c 100644 --- a/plugins/VersionUpgrade/VersionUpgrade40to41/VersionUpgrade40to41.py +++ b/plugins/VersionUpgrade/VersionUpgrade40to41/VersionUpgrade40to41.py @@ -3,6 +3,7 @@ import configparser import io +import uuid from typing import Dict, List, Tuple from UM.VersionUpgrade import VersionUpgrade @@ -18,6 +19,7 @@ _renamed_quality_profiles = { "gmax15plus_pla_very_thick": "gmax15plus_global_very_thick" } # type: Dict[str, str] + ## Upgrades configurations from the state they were in at version 4.0 to the # state they should be in at version 4.1. class VersionUpgrade40to41(VersionUpgrade): @@ -95,6 +97,13 @@ class VersionUpgrade40to41(VersionUpgrade): if parser["containers"]["4"] in _renamed_quality_profiles: parser["containers"]["4"] = _renamed_quality_profiles[parser["containers"]["4"]] + # Assign a GlobalStack to a unique group_id. If the GlobalStack has a UM network connection, use the UM network + # key as the group_id. + if "um_network_key" in parser["metadata"]: + parser["metadata"]["group_id"] = parser["metadata"]["um_network_key"] + elif "group_id" not in parser["metadata"]: + parser["metadata"]["group_id"] = str(uuid.uuid4()) + result = io.StringIO() parser.write(result) return [filename], [result.getvalue()] From bed13bf42b27d3858cb469e0e94cd44bba8531be Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 29 Apr 2019 15:04:09 +0200 Subject: [PATCH 02/30] Cleanup and make sure that group_name is set --- cura/Settings/MachineManager.py | 4 +++ .../src/DiscoverUM3Action.py | 21 ++++-------- .../src/UM3OutputDevicePlugin.py | 34 +++++++------------ 3 files changed, 22 insertions(+), 37 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 5db0a702a1..c40b67c2e4 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1655,3 +1655,7 @@ class MachineManager(QObject): if results: machine_type_name = results[0]["name"] return machine_type_name + + # Gets all machines that belong to the given group_id. + def getMachinesInGroup(self, group_id: str) -> List["GlobalStack"]: + return self._container_registry.findContainerStacks(type = "machine", group_id = group_id) diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index 28e7b60a0e..b67f4d7185 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -110,14 +110,12 @@ class DiscoverUM3Action(MachineAction): Logger.log("d", "Attempting to set the group name of the active machine to %s", group_name) global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() if global_container_stack: - meta_data = global_container_stack.getMetaData() - if "group_name" in meta_data: - previous_connect_group_name = meta_data["group_name"] - global_container_stack.setMetaDataEntry("group_name", group_name) - # Find all the places where there is the same group name and change it accordingly - self._replaceContainersMetadata(key = "group_name", value = previous_connect_group_name, new_value = group_name) - else: - global_container_stack.setMetaDataEntry("group_name", group_name) + # Update a GlobalStacks in the same group with the new group name. + group_id = global_container_stack.getMetaDataEntry("group_id") + machine_manager = CuraApplication.getInstance().getMachineManager() + for machine in machine_manager.getMachinesInGroup(group_id): + machine.setMetaDataEntry("group_name", group_name) + # Set the default value for "hidden", which is used when you have a group with multiple types of printers global_container_stack.setMetaDataEntry("hidden", False) @@ -125,13 +123,6 @@ class DiscoverUM3Action(MachineAction): # Ensure that the connection states are refreshed. self._network_plugin.refreshConnections() - ## Find all container stacks that has the pair 'key = value' in its metadata and replaces the value with 'new_value' - def _replaceContainersMetadata(self, key: str, value: str, new_value: str) -> None: - machines = CuraContainerRegistry.getInstance().findContainerStacks(type="machine") - for machine in machines: - if machine.getMetaDataEntry(key) == value: - machine.setMetaDataEntry(key, new_value) - # Associates the currently active machine with the given printer device. The network connection information will be # stored into the metadata of the currently active machine. @pyqtSlot(QObject) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index f820b6244c..41c76dc4c0 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -283,34 +283,24 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key) - global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() + machine_manager = CuraApplication.getInstance().getMachineManager() + global_container_stack = machine_manager.activeMachine if not global_container_stack: return - meta_data = global_container_stack.getMetaData() + for machine in machine_manager.getMachinesInGroup(global_container_stack.getMetaDataEntry("group_id")): + machine.setMetaDataEntry("um_network_key", printer_device.key) + machine.setMetaDataEntry("group_name", printer_device.name) - if "um_network_key" in meta_data: # Global stack already had a connection, but it's changed. - old_network_key = meta_data["um_network_key"] - # Since we might have a bunch of hidden stacks, we also need to change it there. - metadata_filter = {"um_network_key": old_network_key} - containers = self._application.getContainerRegistry().findContainerStacks(type = "machine", **metadata_filter) + # Delete old authentication data. + Logger.log("d", "Removing old authentication id %s for device %s", + global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key) - for container in containers: - container.setMetaDataEntry("um_network_key", printer_device.key) + machine.removeMetaDataEntry("network_authentication_id") + machine.removeMetaDataEntry("network_authentication_key") - # Delete old authentication data. - Logger.log("d", "Removing old authentication id %s for device %s", - global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key) - - container.removeMetaDataEntry("network_authentication_id") - container.removeMetaDataEntry("network_authentication_key") - - # Ensure that these containers do know that they are configured for network connection - container.addConfiguredConnectionType(printer_device.connectionType.value) - - else: # Global stack didn't have a connection yet, configure it. - global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) - global_container_stack.addConfiguredConnectionType(printer_device.connectionType.value) + # Ensure that these containers do know that they are configured for network connection + machine.addConfiguredConnectionType(printer_device.connectionType.value) self.refreshConnections() From defcba692745db35cf2e0b9705ff979e214b8e1a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 29 Apr 2019 15:16:40 +0200 Subject: [PATCH 03/30] Fix that you can only cancel in WizardDialog CURA-6483 --- cura/UI/AddPrinterPagesModel.py | 1 + cura/UI/WelcomePagesModel.py | 2 ++ resources/qml/Cura.qml | 2 -- .../AddNetworkOrLocalPrinterContent.qml | 14 ++++++++++++++ resources/qml/WelcomePages/WizardDialog.qml | 17 ----------------- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/cura/UI/AddPrinterPagesModel.py b/cura/UI/AddPrinterPagesModel.py index 55bb1500ba..d40da59b2a 100644 --- a/cura/UI/AddPrinterPagesModel.py +++ b/cura/UI/AddPrinterPagesModel.py @@ -15,6 +15,7 @@ class AddPrinterPagesModel(WelcomePagesModel): "page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"), "next_page_id": "machine_actions", "next_page_button_text": self._catalog.i18nc("@action:button", "Add"), + "previous_page_button_text": self._catalog.i18nc("@action:button", "Cancel"), }) self._pages.append({"id": "add_printer_by_ip", "page_url": self._getBuiltinWelcomePagePath("AddPrinterByIpContent.qml"), diff --git a/cura/UI/WelcomePagesModel.py b/cura/UI/WelcomePagesModel.py index 10ae9dabf5..c16ec3763e 100644 --- a/cura/UI/WelcomePagesModel.py +++ b/cura/UI/WelcomePagesModel.py @@ -39,6 +39,7 @@ class WelcomePagesModel(ListModel): PageUrlRole = Qt.UserRole + 2 # URL to the page's QML file NextPageIdRole = Qt.UserRole + 3 # The next page ID it should go to NextPageButtonTextRole = Qt.UserRole + 4 # The text for the next page button + PreviousPageButtonTextRole = Qt.UserRole + 5 # The text for the previous page button def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None: super().__init__(parent) @@ -47,6 +48,7 @@ class WelcomePagesModel(ListModel): self.addRoleName(self.PageUrlRole, "page_url") self.addRoleName(self.NextPageIdRole, "next_page_id") self.addRoleName(self.NextPageButtonTextRole, "next_page_button_text") + self.addRoleName(self.PreviousPageButtonTextRole, "previous_page_button_text") self._application = application self._catalog = i18nCatalog("cura") diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index b6d62ec3f0..ab02887774 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -795,7 +795,6 @@ UM.MainWindow title: catalog.i18nc("@title:window", "Add Printer") model: CuraApplication.getAddPrinterPagesModel() progressBarVisible: false - hasCancelButton: true } Cura.WizardDialog @@ -804,7 +803,6 @@ UM.MainWindow title: catalog.i18nc("@title:window", "What's New") model: CuraApplication.getWhatsNewPagesModel() progressBarVisible: false - hasCancelButton: false } Connections diff --git a/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml index 6037868aaa..7ef1941b87 100644 --- a/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml +++ b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml @@ -104,6 +104,20 @@ Item } } + // This "Back" button only shows in the "Add Machine" dialog, which has "back_button_text" set to "Cancel" + Cura.SecondaryButton + { + id: backButton + anchors.left: parent.left + anchors.bottom: parent.bottom + visible: base.currentItem.previous_page_button_text + text: base.currentItem.previous_page_button_text + onClicked: + { + base.endWizard() + } + } + Cura.PrimaryButton { id: nextButton diff --git a/resources/qml/WelcomePages/WizardDialog.qml b/resources/qml/WelcomePages/WizardDialog.qml index 31240b1ef5..c81f9daff0 100644 --- a/resources/qml/WelcomePages/WizardDialog.qml +++ b/resources/qml/WelcomePages/WizardDialog.qml @@ -31,7 +31,6 @@ Window property var model: null // Needs to be set by whoever is using this dialog. property alias progressBarVisible: wizardPanel.progressBarVisible - property alias hasCancelButton: cancelButton.visible onVisibilityChanged: { @@ -54,20 +53,4 @@ Window target: model onAllFinished: dialog.hide() } - - Cura.SecondaryButton - { - id: cancelButton - - text: catalog.i18nc("@button", "Cancel") - - visible: false - - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.margins: UM.Theme.getSize("default_margin").width - - enabled: true - onClicked: dialog.visible = false - } } From c8872cb4a17b6c859754f00ff8ff1434ff583ea0 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 29 Apr 2019 15:52:21 +0200 Subject: [PATCH 04/30] Use a separate function to validate IP address CURA-6483 --- cura/CuraApplication.py | 4 +++ cura/Utils/QtUtil.py | 21 ++++++++++++++ cura/Utils/networking.py | 26 +++++++++++++++++ .../WelcomePages/AddPrinterByIpContent.qml | 29 ++++++++++++++++++- 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 cura/Utils/QtUtil.py create mode 100644 cura/Utils/networking.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 49eec1d778..cc80c6dbfe 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -117,6 +117,8 @@ from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel from cura.UI.WelcomePagesModel import WelcomePagesModel from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel +from cura.Utils.QtUtil import QtUtil + from .SingleInstance import SingleInstance from .AutoSave import AutoSave from . import PlatformPhysics @@ -1028,6 +1030,8 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager) qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) + qmlRegisterType(QtUtil, "Cura", 1, 0, "QtUtil") + qmlRegisterType(WelcomePagesModel, "Cura", 1, 0, "WelcomePagesModel") qmlRegisterType(WhatsNewPagesModel, "Cura", 1, 0, "WhatsNewPagesModel") qmlRegisterType(AddPrinterPagesModel, "Cura", 1, 0, "AddPrinterPagesModel") diff --git a/cura/Utils/QtUtil.py b/cura/Utils/QtUtil.py new file mode 100644 index 0000000000..60860fcd78 --- /dev/null +++ b/cura/Utils/QtUtil.py @@ -0,0 +1,21 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import Optional + +from PyQt5.QtCore import QObject, pyqtSlot + +from . import networking + + +# +# Exposes the util functions to QML using a QObject. +# +class QtUtil(QObject): + + def __init__(self, parent: Optional["QObject"] = None) -> None: + super().__init__(parent = parent) + + @pyqtSlot(str, result = bool) + def isValidIP(self, address: str) -> bool: + return networking.isValidIP(address) diff --git a/cura/Utils/networking.py b/cura/Utils/networking.py new file mode 100644 index 0000000000..ba2bbddff6 --- /dev/null +++ b/cura/Utils/networking.py @@ -0,0 +1,26 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import re + + +_REGEX_IPV4 = re.compile(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$") +_REGEX_IPV6 = re.compile(r"^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$") + + +# Checks if the given string is a valid IPv4 address. +def isIPv4(address: str) -> bool: + return _REGEX_IPV4.fullmatch(address) is not None + + +# Checks if the given string is a valid IPv6 address. +def isIPv6(address: str) -> bool: + return _REGEX_IPV6.fullmatch(address) is not None + + +# Checks if the given string is a valid IPv4 or IPv6 address. +def isValidIP(address: str) -> bool: + return isIPv4(address) or isIPv6(address) + + +__all__ = ["isIPv4", "isIPv6", "isValidIP"] diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index de9562908c..565fa325cb 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -26,6 +26,9 @@ Item property var discoveredPrinter: null property var isPrinterDiscovered: discoveredPrinter != null + // For validating IP address + property var util: Cura.QtUtil{} + // Make sure to cancel the current request when this page closes. onVisibleChanged: { @@ -93,17 +96,36 @@ Item anchors.verticalCenter: addPrinterButton.verticalCenter anchors.left: parent.left + signal invalidInputDetected() + + onInvalidInputDetected: invalidInputLabel.visible = true + validator: RegExpValidator { - regExp: /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))?/ + regExp: /([a-zA-Z0-9.:]+)?/ } + onTextEdited: invalidInputLabel.visible = false + placeholderText: catalog.i18nc("@text", "Place enter your printer's IP address.") enabled: { ! (addPrinterByIpScreen.hasRequestInProgress || addPrinterByIpScreen.isPrinterDiscovered) } onAccepted: addPrinterButton.clicked() } + Label + { + id: invalidInputLabel + anchors.top: hostnameField.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.left: parent.left + visible: false + text: catalog.i18nc("@text", "Place enter a valid IP address.") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + } + Cura.SecondaryButton { id: addPrinterButton @@ -115,6 +137,11 @@ Item onClicked: { const address = hostnameField.text + if (!util.isValidIP(address)) + { + hostnameField.invalidInputDetected() + return + } // This address is already in the discovered printer model, no need to add a manual discovery. if (CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[address]) From 8c42ceb8e603151d1132bff24a07e02cf70f1f7d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 07:40:16 +0200 Subject: [PATCH 05/30] Use socket to validate IP addresses CURA-6483 --- cura/Utils/networking.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cura/Utils/networking.py b/cura/Utils/networking.py index ba2bbddff6..0456191cee 100644 --- a/cura/Utils/networking.py +++ b/cura/Utils/networking.py @@ -1,21 +1,27 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import re - - -_REGEX_IPV4 = re.compile(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$") -_REGEX_IPV6 = re.compile(r"^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$") +import socket # Checks if the given string is a valid IPv4 address. def isIPv4(address: str) -> bool: - return _REGEX_IPV4.fullmatch(address) is not None + try: + socket.inet_pton(socket.AF_INET, address) + result = True + except: + result = False + return result # Checks if the given string is a valid IPv6 address. def isIPv6(address: str) -> bool: - return _REGEX_IPV6.fullmatch(address) is not None + try: + socket.inet_pton(socket.AF_INET6, address) + result = True + except: + result = False + return result # Checks if the given string is a valid IPv4 or IPv6 address. From bb1442a04e1239693892cbea012bd6b9b7018727 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 07:40:33 +0200 Subject: [PATCH 06/30] Update comments CURA-6483 --- resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml index 7ef1941b87..3a19e7807a 100644 --- a/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml +++ b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml @@ -104,7 +104,7 @@ Item } } - // This "Back" button only shows in the "Add Machine" dialog, which has "back_button_text" set to "Cancel" + // This "Back" button only shows in the "Add Machine" dialog, which has "previous_page_button_text" set to "Cancel" Cura.SecondaryButton { id: backButton From ed8127777ce52d39c0871969864b2cd9cf391b7d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 09:43:48 +0200 Subject: [PATCH 07/30] Make a single NetworkingUtil CURA-6483 --- cura/CuraApplication.py | 4 +- cura/Utils/NetworkingUtil.py | 44 +++++++++++++++++++ cura/Utils/QtUtil.py | 21 --------- cura/Utils/networking.py | 32 -------------- .../WelcomePages/AddPrinterByIpContent.qml | 4 +- 5 files changed, 48 insertions(+), 57 deletions(-) create mode 100644 cura/Utils/NetworkingUtil.py delete mode 100644 cura/Utils/QtUtil.py delete mode 100644 cura/Utils/networking.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index cc80c6dbfe..a43af578a1 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -117,7 +117,7 @@ from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel from cura.UI.WelcomePagesModel import WelcomePagesModel from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel -from cura.Utils.QtUtil import QtUtil +from cura.Utils.NetworkingUtil import NetworkingUtil from .SingleInstance import SingleInstance from .AutoSave import AutoSave @@ -1030,7 +1030,7 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager) qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) - qmlRegisterType(QtUtil, "Cura", 1, 0, "QtUtil") + qmlRegisterType(NetworkingUtil, "Cura", 1, 0, "NetworkingUtil") qmlRegisterType(WelcomePagesModel, "Cura", 1, 0, "WelcomePagesModel") qmlRegisterType(WhatsNewPagesModel, "Cura", 1, 0, "WhatsNewPagesModel") diff --git a/cura/Utils/NetworkingUtil.py b/cura/Utils/NetworkingUtil.py new file mode 100644 index 0000000000..b13f7903b9 --- /dev/null +++ b/cura/Utils/NetworkingUtil.py @@ -0,0 +1,44 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import socket +from typing import Optional + +from PyQt5.QtCore import QObject, pyqtSlot + + +# +# This is a QObject because some of the functions can be used (and are useful) in QML. +# +class NetworkingUtil(QObject): + + def __init__(self, parent: Optional["QObject"] = None) -> None: + super().__init__(parent = parent) + + # Checks if the given string is a valid IPv4 address. + @pyqtSlot(str, result = bool) + def isIPv4(self, address: str) -> bool: + try: + socket.inet_pton(socket.AF_INET, address) + result = True + except: + result = False + return result + + # Checks if the given string is a valid IPv6 address. + @pyqtSlot(str, result = bool) + def isIPv6(self, address: str) -> bool: + try: + socket.inet_pton(socket.AF_INET6, address) + result = True + except: + result = False + return result + + # Checks if the given string is a valid IPv4 or IPv6 address. + @pyqtSlot(str, result = bool) + def isValidIP(self, address: str) -> bool: + return self.isIPv4(address) or self.isIPv6(address) + + +__all__ = ["NetworkingUtil"] diff --git a/cura/Utils/QtUtil.py b/cura/Utils/QtUtil.py deleted file mode 100644 index 60860fcd78..0000000000 --- a/cura/Utils/QtUtil.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2019 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -from typing import Optional - -from PyQt5.QtCore import QObject, pyqtSlot - -from . import networking - - -# -# Exposes the util functions to QML using a QObject. -# -class QtUtil(QObject): - - def __init__(self, parent: Optional["QObject"] = None) -> None: - super().__init__(parent = parent) - - @pyqtSlot(str, result = bool) - def isValidIP(self, address: str) -> bool: - return networking.isValidIP(address) diff --git a/cura/Utils/networking.py b/cura/Utils/networking.py deleted file mode 100644 index 0456191cee..0000000000 --- a/cura/Utils/networking.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2019 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -import socket - - -# Checks if the given string is a valid IPv4 address. -def isIPv4(address: str) -> bool: - try: - socket.inet_pton(socket.AF_INET, address) - result = True - except: - result = False - return result - - -# Checks if the given string is a valid IPv6 address. -def isIPv6(address: str) -> bool: - try: - socket.inet_pton(socket.AF_INET6, address) - result = True - except: - result = False - return result - - -# Checks if the given string is a valid IPv4 or IPv6 address. -def isValidIP(address: str) -> bool: - return isIPv4(address) or isIPv6(address) - - -__all__ = ["isIPv4", "isIPv6", "isValidIP"] diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 565fa325cb..0862727d6e 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -27,7 +27,7 @@ Item property var isPrinterDiscovered: discoveredPrinter != null // For validating IP address - property var util: Cura.QtUtil{} + property var networkingUtil: Cura.NetworkingUtil {} // Make sure to cancel the current request when this page closes. onVisibleChanged: @@ -137,7 +137,7 @@ Item onClicked: { const address = hostnameField.text - if (!util.isValidIP(address)) + if (!networkingUtil.isValidIP(address)) { hostnameField.invalidInputDetected() return From d6f4ddc322db3b3cbb6211bca17c4b03d77f9d70 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 12:45:58 +0200 Subject: [PATCH 08/30] Simplify GlobalStacksModel CURA-6483 --- cura/Machines/Models/GlobalStacksModel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Machines/Models/GlobalStacksModel.py b/cura/Machines/Models/GlobalStacksModel.py index f15fe1878b..e4f1cf1177 100644 --- a/cura/Machines/Models/GlobalStacksModel.py +++ b/cura/Machines/Models/GlobalStacksModel.py @@ -5,6 +5,7 @@ from PyQt5.QtCore import Qt, QTimer from UM.Qt.ListModel import ListModel from UM.i18n import i18nCatalog +from UM.Util import parseBool from cura.PrinterOutput.PrinterOutputDevice import ConnectionType from cura.Settings.CuraContainerRegistry import CuraContainerRegistry @@ -54,7 +55,6 @@ class GlobalStacksModel(ListModel): items = [] container_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine") - for container_stack in container_stacks: has_remote_connection = False @@ -62,7 +62,7 @@ class GlobalStacksModel(ListModel): has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] - if container_stack.getMetaDataEntry("hidden", False) in ["True", True]: + if parseBool(container_stack.getMetaDataEntry("hidden", False)): continue section_name = "Network enabled printers" if has_remote_connection else "Local printers" From d18c11a2c19f6df9964875382f9a541389b650fa Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 12:46:33 +0200 Subject: [PATCH 09/30] Fix filtering in switchPrinterType() CURA-6483 --- cura/Settings/MachineManager.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index c40b67c2e4..d1f5c4c884 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1360,7 +1360,10 @@ class MachineManager(QObject): # Get the definition id corresponding to this machine name machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId() # Try to find a machine with the same network key - new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey()}) + metadata_filter = {"group_id": self._global_container_stack.getMetaDataEntry("group_id"), + "um_network_key": self.activeMachineNetworkKey(), + } + new_machine = self.getMachine(machine_definition_id, metadata_filter = metadata_filter) # If there is no machine, then create a new one and set it to the non-hidden instance if not new_machine: new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id) @@ -1369,13 +1372,12 @@ class MachineManager(QObject): new_machine.setMetaDataEntry("group_id", self._global_container_stack.getMetaDataEntry("group_id")) new_machine.setMetaDataEntry("um_network_key", self.activeMachineNetworkKey()) new_machine.setMetaDataEntry("group_name", self.activeMachineNetworkGroupName) - new_machine.setMetaDataEntry("hidden", False) new_machine.setMetaDataEntry("connection_type", self._global_container_stack.getMetaDataEntry("connection_type")) else: Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey()) - new_machine.setMetaDataEntry("hidden", False) # Set the current printer instance to hidden (the metadata entry must exist) + new_machine.setMetaDataEntry("hidden", False) self._global_container_stack.setMetaDataEntry("hidden", True) self.setActiveMachine(new_machine.getId()) From e1766b72ece5675aa32217acf2a1b6ffef313618 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 12:47:17 +0200 Subject: [PATCH 10/30] Only highlight the currently active machine CURA-6483 --- resources/qml/PrinterSelector/MachineSelectorList.qml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 1043339ae6..9c52c15580 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -32,16 +32,7 @@ ListView width: listView.width outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - checked: - { - // If the machine has a remote connection - var result = Cura.MachineManager.activeMachineId == model.id - if (Cura.MachineManager.activeMachineHasRemoteConnection) - { - result |= Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["group_name"] - } - return result - } + checked: Cura.MachineManager.activeMachineId == model.id onClicked: { From 40d26bbb6ecff89540bcc2ccbe2797440339df4e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 12:48:49 +0200 Subject: [PATCH 11/30] NetworkingUtil is included in Cura 1.5 QML CURA-6483 --- cura/CuraApplication.py | 2 +- resources/qml/WelcomePages/AddPrinterByIpContent.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a43af578a1..29febf2610 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1030,7 +1030,7 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager) qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) - qmlRegisterType(NetworkingUtil, "Cura", 1, 0, "NetworkingUtil") + qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil") qmlRegisterType(WelcomePagesModel, "Cura", 1, 0, "WelcomePagesModel") qmlRegisterType(WhatsNewPagesModel, "Cura", 1, 0, "WhatsNewPagesModel") diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 0862727d6e..6fa7097814 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -6,7 +6,7 @@ import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import UM 1.3 as UM -import Cura 1.1 as Cura +import Cura 1.5 as Cura // From 0cc00efa7983f18de6aa472be2f24b9c802a2630 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 12:53:45 +0200 Subject: [PATCH 12/30] Add doc for group_id CURA-6483 --- cura/Settings/GlobalStack.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index a8e6f2f2c0..15c95c9394 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -35,6 +35,11 @@ class GlobalStack(CuraContainerStack): super().__init__(container_id) self.setMetaDataEntry("type", "machine") # For backward compatibility + + # TL;DR: If Cura is looking for printers that belong to the same group, it should use "group_id". + # Each GlobalStack by default belongs to a group which is identified via "group_id". This group_id is used to + # figure out which GlobalStacks are in the printer cluster for example without knowing the implementation + # details such as the um_network_key or some other identifier that's used by the underlying device plugin. self.setMetaDataEntry("group_id", str(uuid.uuid4())) # Assign a new GlobalStack to a unique group by default self._extruders = {} # type: Dict[str, "ExtruderStack"] From 3578482ea5fdb7109e5550097f826eb13c301483 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 15:26:02 +0200 Subject: [PATCH 13/30] Fix connect via network dialog CURA-6483 - Do not add printers that have already been discovered - Add IP address validation check --- .../resources/qml/DiscoverUM3Action.qml | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index 2788a35173..1e3da11e0a 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -1,8 +1,8 @@ -// Copyright (c) 2018 Ultimaker B.V. +// Copyright (c) 2019 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import UM 1.2 as UM -import Cura 1.0 as Cura +import Cura 1.5 as Cura import QtQuick 2.2 import QtQuick.Controls 1.1 @@ -14,9 +14,13 @@ Cura.MachineAction { id: base anchors.fill: parent; + property alias currentItemIndex: listview.currentIndex property var selectedDevice: null property bool completeProperties: true + // For validating IP addresses + property var networkingUtil: Cura.NetworkingUtil {} + function connectToPrinter() { if(base.selectedDevice && base.completeProperties) @@ -342,6 +346,17 @@ Cura.MachineAction } } + MessageDialog + { + id: invalidIPAddressMessageDialog + x: ((parent.width - width) / 2) | 0 + y: ((parent.height - height) / 2) | 0 + title: catalog.i18nc("@title:window", "Invalid IP address") + text: catalog.i18nc("@text", "Please enter a valid IP address.") + icon: StandardIcon.Warning + standardButtons: StandardButton.Ok + } + UM.Dialog { id: manualPrinterDialog @@ -404,6 +419,26 @@ Cura.MachineAction text: catalog.i18nc("@action:button", "OK") onClicked: { + // Validate the input first + if (!networkingUtil.isValidIP(manualPrinterDialog.addressText)) + { + invalidIPAddressMessageDialog.open() + return + } + + // if the entered IP address has already been discovered, switch the current item to that item + // and do nothing else. + for (var i = 0; i < manager.foundDevices.length; i++) + { + var device = manager.foundDevices[i] + if (device.address == manualPrinterDialog.addressText) + { + currentItemIndex = i + manualPrinterDialog.hide() + return + } + } + manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText) manualPrinterDialog.hide() } From 0e2815a7481aa1aff5cb9783cc683a6141f00a8f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 15:26:56 +0200 Subject: [PATCH 14/30] Fix back button on add printer page CURA-6483 --- .../qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml index 3a19e7807a..fcdfa9817c 100644 --- a/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml +++ b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml @@ -110,8 +110,8 @@ Item id: backButton anchors.left: parent.left anchors.bottom: parent.bottom - visible: base.currentItem.previous_page_button_text - text: base.currentItem.previous_page_button_text + visible: base.currentItem.previous_page_button_text ? true : false + text: base.currentItem.previous_page_button_text ? base.currentItem.previous_page_button_text : "" onClicked: { base.endWizard() From 383b474bd79e89a3aad458fbe721c88b819d5031 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 30 Apr 2019 15:27:27 +0200 Subject: [PATCH 15/30] Fix typo CURA-6483 --- resources/qml/WelcomePages/AddPrinterByIpContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 6fa7097814..663cd83de8 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -120,7 +120,7 @@ Item anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: parent.left visible: false - text: catalog.i18nc("@text", "Place enter a valid IP address.") + text: catalog.i18nc("@text", "Please enter a valid IP address.") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") renderType: Text.NativeRendering From 2b5d78a01af1ffb0c0ff58efdf81cbb39c1990d6 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 1 May 2019 14:24:58 +0200 Subject: [PATCH 16/30] Do not add unknown or non-host printers in add-by-ip CURA-6483 --- .../WelcomePages/AddPrinterByIpContent.qml | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 663cd83de8..13c87eccd8 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -24,7 +24,9 @@ Item property bool hasRequestFinished: false property var discoveredPrinter: null - property var isPrinterDiscovered: discoveredPrinter != null + property bool isPrinterDiscovered: discoveredPrinter != null + // A printer can only be added if it doesn't have an unknown type and it's the host of a group. + property bool canAddPrinter: isPrinterDiscovered && !discoveredPrinter.isUnknownMachineType && discoveredPrinter.isHostOfGroup // For validating IP address property var networkingUtil: Cura.NetworkingUtil {} @@ -188,6 +190,8 @@ Item Item { id: printerInfoLabels + anchors.left: parent.left + anchors.right: parent.right anchors.top: parent.top anchors.margins: UM.Theme.getSize("default_margin").width @@ -204,10 +208,24 @@ Item text: !addPrinterByIpScreen.isPrinterDiscovered ? "???" : addPrinterByIpScreen.discoveredPrinter.name } + Label + { + id: printerCannotBeAddedLabel + width: parent.width + anchors.top: printerNameLabel.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + text: catalog.i18nc("@label", "This printer cannot be added because it's an unknown printer or it's not the host of a group.") + visible: addPrinterByIpScreen.hasRequestFinished && !addPrinterByIpScreen.canAddPrinter + font: UM.Theme.getFont("default_bold") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + wrapMode: Text.WordWrap + } + GridLayout { id: printerInfoGrid - anchors.top: printerNameLabel.bottom + anchors.top: printerCannotBeAddedLabel ? printerCannotBeAddedLabel.bottom : printerNameLabel.bottom anchors.margins: UM.Theme.getSize("default_margin").width columns: 2 columnSpacing: UM.Theme.getSize("default_margin").width @@ -305,6 +323,6 @@ Item base.showNextPage() } - enabled: addPrinterByIpScreen.isPrinterDiscovered + enabled: addPrinterByIpScreen.canAddPrinter } } From 0575fd283e750fbc54575dd0ed07e135881a1e61 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 1 May 2019 15:11:00 +0200 Subject: [PATCH 17/30] Fix manual printer device not up-to-date CURA-6483 --- .../WelcomePages/AddPrinterByIpContent.qml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 13c87eccd8..6ee59f03f9 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -22,6 +22,7 @@ Item property bool hasRequestInProgress: CuraApplication.getDiscoveredPrintersModel().hasManualDeviceRequestInProgress // Indicates if a request has finished. property bool hasRequestFinished: false + property string currentRequestAddress: "" property var discoveredPrinter: null property bool isPrinterDiscovered: discoveredPrinter != null @@ -31,6 +32,26 @@ Item // For validating IP address property var networkingUtil: Cura.NetworkingUtil {} + // CURA-6483 + // For a manually added UM printer, the UM3OutputDevicePlugin will first create a LegacyUM device for it. Later, + // when it gets more info from the printer, it will first REMOVE the LegacyUM device and then add a ClusterUM device. + // The Add-by-IP page needs to make sure that the user do not add an unknown printer or a printer that's not the + // host of a group. Because of the device list change, this page needs to react upon DiscoveredPrintersChanged so + // it has the correct information. + Connections + { + target: CuraApplication.getDiscoveredPrintersModel() + onDiscoveredPrintersChanged: + { + if (hasRequestFinished && currentRequestAddress) + { + var printer = CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[currentRequestAddress] + printer = printer ? printer : null + discoveredPrinter = printer + } + } + } + // Make sure to cancel the current request when this page closes. onVisibleChanged: { @@ -149,9 +170,11 @@ Item if (CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[address]) { addPrinterByIpScreen.discoveredPrinter = CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[address] + addPrinterByIpScreen.hasRequestFinished = true return } + addPrinterByIpScreen.currentRequestAddress = address CuraApplication.getDiscoveredPrintersModel().checkManualDevice(address) } busy: addPrinterByIpScreen.hasRequestInProgress From 1d4ab98ddc12bd4ef76f96ac71ef7a97bff0bc84 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 2 May 2019 08:25:27 +0200 Subject: [PATCH 18/30] Fix binding loop CURA-6483 --- .../UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index 1e3da11e0a..ecec87ef02 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -349,8 +349,8 @@ Cura.MachineAction MessageDialog { id: invalidIPAddressMessageDialog - x: ((parent.width - width) / 2) | 0 - y: ((parent.height - height) / 2) | 0 + x: (parent.x + (parent.width) / 2) | 0 + y: (parent.y + (parent.height) / 2) | 0 title: catalog.i18nc("@title:window", "Invalid IP address") text: catalog.i18nc("@text", "Please enter a valid IP address.") icon: StandardIcon.Warning From b9d8b8911310251af965ec0387f515570c4061c3 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 2 May 2019 09:03:48 +0200 Subject: [PATCH 19/30] Fix number of extruders not being updated --- .../MachineSettingsPrinterTab.qml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml b/plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml index 007db41f2b..3b31a5de36 100644 --- a/plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml +++ b/plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml @@ -285,18 +285,30 @@ Item optionModel: ListModel { id: extruderCountModel + Component.onCompleted: { - extruderCountModel.clear() + update() + } + + function update() + { + clear() for (var i = 1; i <= Cura.MachineManager.activeMachine.maxExtruderCount; i++) { // Use String as value. JavaScript only has Number. PropertyProvider.setPropertyValue() // takes a QVariant as value, and Number gets translated into a float. This will cause problem // for integer settings such as "Number of Extruders". - extruderCountModel.append({ text: String(i), value: String(i) }) + append({ text: String(i), value: String(i) }) } } } + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: extruderCountModel.update() + } } } } From 5fee82bcb76a62865d67033593a0fc870c16fe2d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 2 May 2019 09:44:15 +0200 Subject: [PATCH 20/30] Fix machine settings page CURA-6495 --- cura/Machines/Models/FirstStartMachineActionsModel.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cura/Machines/Models/FirstStartMachineActionsModel.py b/cura/Machines/Models/FirstStartMachineActionsModel.py index aad5372cde..240571e6dc 100644 --- a/cura/Machines/Models/FirstStartMachineActionsModel.py +++ b/cura/Machines/Models/FirstStartMachineActionsModel.py @@ -35,6 +35,8 @@ class FirstStartMachineActionsModel(ListModel): self._application = application self._application.initializationFinished.connect(self._initialize) + self._previous_global_stack = None + def _initialize(self) -> None: self._application.getMachineManager().globalContainerChanged.connect(self._update) self._update() @@ -86,6 +88,12 @@ class FirstStartMachineActionsModel(ListModel): self.setItems([]) return + # Do not update if the machine has not been switched. This can cause the SettingProviders on the Machine + # Setting page to do a force update, but they can use potential outdated cached values. + if self._previous_global_stack is not None and global_stack.getId() == self._previous_global_stack.getId(): + return + self._previous_global_stack = global_stack + definition_id = global_stack.definition.getId() first_start_actions = self._application.getMachineActionManager().getFirstStartActions(definition_id) From d0cc2dc8fa08f8407b2ea0f752b2a3bf69090972 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 2 May 2019 12:47:46 +0200 Subject: [PATCH 21/30] Remove unneeded TODO CURA-6495 --- resources/qml/MachineSettings/PrintHeadMinMaxTextField.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/qml/MachineSettings/PrintHeadMinMaxTextField.qml b/resources/qml/MachineSettings/PrintHeadMinMaxTextField.qml index 236f9a7dd0..2eaaed4524 100644 --- a/resources/qml/MachineSettings/PrintHeadMinMaxTextField.qml +++ b/resources/qml/MachineSettings/PrintHeadMinMaxTextField.qml @@ -76,6 +76,4 @@ NumericTextFieldWithUnit forceUpdateOnChangeFunction() } } - - // TODO: add forceUpdateOnChangeFunction: } From 85822d6e73c21e54b643c56ca895756fd3fc8d5e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 2 May 2019 15:30:09 +0200 Subject: [PATCH 22/30] Fix add by IP validator regex CURA-6483 --- resources/qml/WelcomePages/AddPrinterByIpContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 6ee59f03f9..a2543d1fb8 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -125,7 +125,7 @@ Item validator: RegExpValidator { - regExp: /([a-zA-Z0-9.:]+)?/ + regExp: /([a-fA-F0-9.:]+)?/ } onTextEdited: invalidInputLabel.visible = false From 5b9c4e402e5c4dcdd3cf64666e0b1045e5e047e5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 2 May 2019 15:38:19 +0200 Subject: [PATCH 23/30] Change the Back button to be a secondary button Change also some texts in the More information dialog and the font type of one of the titles in the first use flow Contributes to CURA-6483. --- plugins/SliceInfoPlugin/MoreInfoWindow.qml | 6 +++--- resources/qml/WelcomePages/AddPrinterByIpContent.qml | 2 +- resources/qml/WelcomePages/CloudContent.qml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/SliceInfoPlugin/MoreInfoWindow.qml b/plugins/SliceInfoPlugin/MoreInfoWindow.qml index f19e2cce19..50276ec25c 100644 --- a/plugins/SliceInfoPlugin/MoreInfoWindow.qml +++ b/plugins/SliceInfoPlugin/MoreInfoWindow.qml @@ -71,7 +71,7 @@ Window left: parent.left right: parent.right } - text: catalog.i18nc("@text:window", "Ultimaker Cura collects anonymous data in order to improve the print quality and user experience. Below is an example of all the data that is sent:") + text: catalog.i18nc("@text:window", "Ultimaker Cura collects anonymous data in order to improve the print quality and user experience. Below is an example of all the data that is shared:") wrapMode: Text.WordWrap renderType: Text.NativeRendering } @@ -106,7 +106,7 @@ Window Cura.RadioButton { id: dontSendButton - text: catalog.i18nc("@text:window", "I don't want to send this data") + text: catalog.i18nc("@text:window", "I don't want to send anonymous data") onClicked: { baseDialog.allowSendData = !checked @@ -115,7 +115,7 @@ Window Cura.RadioButton { id: allowSendButton - text: catalog.i18nc("@text:window", "Allow sending this data to Ultimaker and help us improve Cura") + text: catalog.i18nc("@text:window", "Allow sending anonymous data") onClicked: { baseDialog.allowSendData = checked diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index a2543d1fb8..4aec5879c1 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -321,7 +321,7 @@ Item } } - Cura.PrimaryButton + Cura.SecondaryButton { id: backButton anchors.left: parent.left diff --git a/resources/qml/WelcomePages/CloudContent.qml b/resources/qml/WelcomePages/CloudContent.qml index b0b4d53cf1..e9b6df94e0 100644 --- a/resources/qml/WelcomePages/CloudContent.qml +++ b/resources/qml/WelcomePages/CloudContent.qml @@ -35,7 +35,7 @@ Item horizontalAlignment: Text.AlignHCenter text: catalog.i18nc("@label", "Ultimaker Cloud") color: UM.Theme.getColor("primary_button") - font: UM.Theme.getFont("large_bold") + font: UM.Theme.getFont("huge") renderType: Text.NativeRendering } From be3f194aaafecfd3659ed45211d3245629290f81 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 2 May 2019 16:00:17 +0200 Subject: [PATCH 24/30] Use a disabled rectangle instead of an empty space in the XRay view To keep consistency with what happens in the prepare page when a printer doesn't have material or variants. Contributes to CURA-6469. --- resources/qml/EmptyViewMenuComponent.qml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/resources/qml/EmptyViewMenuComponent.qml b/resources/qml/EmptyViewMenuComponent.qml index 8551d21998..10a50ea023 100644 --- a/resources/qml/EmptyViewMenuComponent.qml +++ b/resources/qml/EmptyViewMenuComponent.qml @@ -3,7 +3,26 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.0 // For the dropshadow +import UM 1.2 as UM // Empty placeholder -Item { } +Rectangle +{ + color: UM.Theme.getColor("disabled") + + DropShadow + { + id: shadow + // Don't blur the shadow + radius: 0 + anchors.fill: parent + source: parent + verticalOffset: 2 + visible: true + color: UM.Theme.getColor("action_button_shadow") + // Should always be drawn behind the background. + z: parent.z - 1 + } +} From 9e454935874b2538a3b19574a2c2966a661fdecd Mon Sep 17 00:00:00 2001 From: Mark Bernard Date: Thu, 2 May 2019 12:10:25 -0400 Subject: [PATCH 25/30] Create anet_a6.def.json Add Anet A6 printer to Cura printer definitions --- resources/definitions/anet_a6.def.json | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 resources/definitions/anet_a6.def.json diff --git a/resources/definitions/anet_a6.def.json b/resources/definitions/anet_a6.def.json new file mode 100644 index 0000000000..a9cd6f9a73 --- /dev/null +++ b/resources/definitions/anet_a6.def.json @@ -0,0 +1,44 @@ +{ + "version": 2, + "name": "Anet A6", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Mark", + "manufacturer": "Anet", + "file_formats": "text/x-gcode", + "platform": "aneta6_platform.stl", + "machine_extruder_trains": + { + "0": "anet_a6_extruder_0" + } + }, + + "overrides": { + "machine_name": { "default_value": "Anet A6" }, + "machine_heated_bed": { + "default_value": true + }, + "machine_width": { + "default_value": 220 + }, + "machine_height": { + "default_value": 250 + }, + "machine_depth": { + "default_value": 220 + }, + "machine_center_is_zero": { + "default_value": false + }, + "gantry_height": { + "default_value": 55 + }, + "machine_start_gcode": { + "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nM84 ;steppers off\nM0 S12 ;wait 12 seconds\nM17 ;turn steppers on\nG1 Z10.0 F300 ;move the platform down 10mm\nG92 E0 ;zero the extruded length\nG1 F200 E8 ;extrude 8mm of feed stock\nG92 E0 ;zero the extruded length again\nM0 S5 ;wait 5 seconds\nG1 F9000\nM117 Printing..." + }, + "machine_end_gcode": { + "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+4 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y210 F9000 ;move out to get part off\nM84 ;steppers off\nG90 ;absolute positioning" + } + } +} From c5b903b2c27b4900ebdc6a8e643415526814885a Mon Sep 17 00:00:00 2001 From: Mark Bernard Date: Thu, 2 May 2019 12:11:15 -0400 Subject: [PATCH 26/30] Create anet_a6_extruder_0.def.json Add Anet A6 extruder to Cura printer definitions --- resources/extruders/anet_a6_extruder_0.def.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 resources/extruders/anet_a6_extruder_0.def.json diff --git a/resources/extruders/anet_a6_extruder_0.def.json b/resources/extruders/anet_a6_extruder_0.def.json new file mode 100644 index 0000000000..704d3a55ca --- /dev/null +++ b/resources/extruders/anet_a6_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "anet_a6_extruder_0", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "anet_a6", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} From 7a010ead565f051856ad89d2849837d0cff16b25 Mon Sep 17 00:00:00 2001 From: Mark Bernard Date: Thu, 2 May 2019 12:12:31 -0400 Subject: [PATCH 27/30] Add files via upload Add Anet A6 platform to Cura printer definitions --- resources/meshes/aneta6_platform.stl | Bin 0 -> 25484 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/meshes/aneta6_platform.stl diff --git a/resources/meshes/aneta6_platform.stl b/resources/meshes/aneta6_platform.stl new file mode 100644 index 0000000000000000000000000000000000000000..735215878597771257a6abbfefc4554a8cffa2eb GIT binary patch literal 25484 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 I(H Date: Fri, 3 May 2019 08:48:35 +0200 Subject: [PATCH 28/30] Prevent auto selecting an invalid printer CURA-6483 --- .../Models/DiscoveredPrintersModel.py | 2 +- .../AddNetworkPrinterScrollView.qml | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cura/Machines/Models/DiscoveredPrintersModel.py b/cura/Machines/Models/DiscoveredPrintersModel.py index e639ee2d25..a2a1fac3f7 100644 --- a/cura/Machines/Models/DiscoveredPrintersModel.py +++ b/cura/Machines/Models/DiscoveredPrintersModel.py @@ -192,7 +192,7 @@ class DiscoveredPrintersModel(QObject): def discoveredPrintersByAddress(self) -> Dict[str, DiscoveredPrinter]: return self._discovered_printer_by_ip_dict - @pyqtProperty(list, notify = discoveredPrintersChanged) + @pyqtProperty("QVariantList", notify = discoveredPrintersChanged) def discoveredPrinters(self) -> List["DiscoveredPrinter"]: item_list = list( x for x in self._discovered_printer_by_ip_dict.values() if not parseBool(x.device.getProperty("temporary"))) diff --git a/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml b/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml index 5f3ceac116..6d8f75c3f5 100644 --- a/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml +++ b/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml @@ -76,15 +76,28 @@ Item Component.onCompleted: { - // Select the first one that's not "unknown" by default. + var toSelectIndex = -1 + // Select the first one that's not "unknown" and is the host a group by default. for (var i = 0; i < count; i++) { - if (!model[i].isUnknownMachineType) + if (!model[i].isUnknownMachineType && model[i].isHostOfGroup) { - currentIndex = i + toSelectIndex = i break } } + currentIndex = toSelectIndex + } + + // CURA-6483 For some reason currentIndex can be reset to 0. This check is here to prevent automatically + // selecting an unknown or non-host printer. + onCurrentIndexChanged: + { + var item = model[currentIndex] + if (!item || item.isUnknownMachineType || !item.isHostOfGroup) + { + currentIndex = -1 + } } Component From f89ba25faa3a1bef7a6e57bae138419a2071382a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 3 May 2019 09:21:52 +0200 Subject: [PATCH 29/30] enable prime tower when adhesion is none or skirt CURA-6492 --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 49dab571e4..636a918579 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5233,7 +5233,7 @@ "type": "bool", "enabled": "extruders_enabled_count > 1", "default_value": false, - "resolve": "any(extruderValues('prime_tower_enable'))", + "resolve": "any(extruderValues('prime_tower_enable')) or (adhesion_type in ('none', 'skirt'))", "settable_per_mesh": false, "settable_per_extruder": false }, From 4910bc006b761635b5a4480115e9efaf3c0ab299 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 3 May 2019 10:58:48 +0200 Subject: [PATCH 30/30] Move platform mesh a bit down This way it's outside of the build volume. Contributes to issue CURA-6505. --- resources/definitions/anet_a6.def.json | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/definitions/anet_a6.def.json b/resources/definitions/anet_a6.def.json index a9cd6f9a73..3643555e9e 100644 --- a/resources/definitions/anet_a6.def.json +++ b/resources/definitions/anet_a6.def.json @@ -8,6 +8,7 @@ "manufacturer": "Anet", "file_formats": "text/x-gcode", "platform": "aneta6_platform.stl", + "platform_offset": [0, -3.4, 0], "machine_extruder_trains": { "0": "anet_a6_extruder_0"