From 372e9026e4a1eaf5c09ecbbdfa1e5ff1bba257fd Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 25 Apr 2019 08:16:44 +0200 Subject: [PATCH 1/9] Move add by ip device discovery into DiscoveredPrintersModel CURA-6483 --- cura/CuraApplication.py | 2 +- .../Models/DiscoveredPrintersModel.py | 76 ++++++++++-- .../src/UM3OutputDevicePlugin.py | 59 +++++++--- .../WelcomePages/AddPrinterByIpContent.qml | 109 +++++++----------- 4 files changed, 150 insertions(+), 96 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9dd1168135..76efba20ab 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -216,7 +216,7 @@ class CuraApplication(QtApplication): self._machine_settings_manager = MachineSettingsManager(self, parent = self) - self._discovered_printer_model = DiscoveredPrintersModel(parent = self) + self._discovered_printer_model = DiscoveredPrintersModel(self, parent = self) self._first_start_machine_actions_model = FirstStartMachineActionsModel(self, parent = self) self._welcome_pages_model = WelcomePagesModel(self, parent = self) self._add_printer_pages_model = AddPrinterPagesModel(self, parent = self) diff --git a/cura/Machines/Models/DiscoveredPrintersModel.py b/cura/Machines/Models/DiscoveredPrintersModel.py index 7e0721fcbe..205b3be785 100644 --- a/cura/Machines/Models/DiscoveredPrintersModel.py +++ b/cura/Machines/Models/DiscoveredPrintersModel.py @@ -8,10 +8,11 @@ from PyQt5.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject from UM.i18n import i18nCatalog from UM.Logger import Logger from UM.Util import parseBool +from UM.OutputDevice.OutputDeviceManager import ManualDeviceAdditionAttempt if TYPE_CHECKING: from PyQt5.QtCore import QObject - + from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice @@ -45,6 +46,10 @@ class DiscoveredPrinter(QObject): self._name = name self.nameChanged.emit() + @pyqtProperty(str, constant = True) + def address(self) -> str: + return self._ip_address + machineTypeChanged = pyqtSignal() @pyqtProperty(str, notify = machineTypeChanged) @@ -94,13 +99,72 @@ class DiscoveredPrinter(QObject): # class DiscoveredPrintersModel(QObject): - def __init__(self, parent: Optional["QObject"] = None) -> None: + def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None: super().__init__(parent) + self._application = application self._discovered_printer_by_ip_dict = dict() # type: Dict[str, DiscoveredPrinter] + self._plugin_for_manual_device = None + self._manual_device_address = "" + discoveredPrintersChanged = pyqtSignal() + @pyqtSlot(str) + def checkManualDevice(self, address: str) -> None: + if self.hasManualDeviceRequestInProgress: + Logger.log("i", "A manual device request for address [%s] is still in progress, do nothing", + self._manual_device_address) + return + + priority_order = [ + ManualDeviceAdditionAttempt.PRIORITY, + ManualDeviceAdditionAttempt.POSSIBLE, + ] # type: List[ManualDeviceAdditionAttempt] + + all_plugins_dict = self._application.getOutputDeviceManager().getAllOutputDevicePlugins() + + can_add_manual_plugins = [item for item in filter( + lambda plugin_item: plugin_item.canAddManualDevice(address) in priority_order, + all_plugins_dict.values())] + + if not can_add_manual_plugins: + Logger.log("d", "Could not find a plugin to accept adding %s manually via address.", address) + return + + plugin = max(can_add_manual_plugins, key = lambda p: priority_order.index(p.canAddManualDevice(address))) + self._plugin_for_manual_device = plugin + self._plugin_for_manual_device.addManualDevice(address, callback = self._onManualDeviceRequestFinished) + self._manual_device_address = address + self.hasManualDeviceRequestInProgressChanged.emit() + + @pyqtSlot() + def cancelCurrentManualDeviceRequest(self) -> None: + if self._manual_device_address: + self._plugin_for_manual_device.removeManualDevice(self._manual_device_address, address = self._manual_device_address) + self._manual_device_address = "" + self._plugin_for_manual_device = None + self.hasManualDeviceRequestInProgressChanged.emit() + self.manualDeviceRequestFinished.emit(False) + + hasManualDeviceRequestInProgressChanged = pyqtSignal() + + @pyqtProperty(bool, notify = hasManualDeviceRequestInProgressChanged) + def hasManualDeviceRequestInProgress(self) -> bool: + return self._manual_device_address != "" + + manualDeviceRequestFinished = pyqtSignal(bool, arguments = ["success"]) + + def _onManualDeviceRequestFinished(self, success: bool, address: str) -> None: + if address == self._manual_device_address: + self._manual_device_address = "" + self.hasManualDeviceRequestInProgressChanged.emit() + self.manualDeviceRequestFinished.emit(success) + + @pyqtProperty("QVariantMap", notify = discoveredPrintersChanged) + def discoveredPrintersByAddress(self) -> Dict[str, DiscoveredPrinter]: + return self._discovered_printer_by_ip_dict + @pyqtProperty(list, notify = discoveredPrintersChanged) def discoveredPrinters(self) -> List["DiscoveredPrinter"]: item_list = list( @@ -157,11 +221,3 @@ class DiscoveredPrintersModel(QObject): @pyqtSlot("QVariant") def createMachineFromDiscoveredPrinter(self, discovered_printer: "DiscoveredPrinter") -> None: discovered_printer.create_callback(discovered_printer.getKey()) - - @pyqtSlot(str) - def createMachineFromDiscoveredPrinterAddress(self, ip_address: str) -> None: - if ip_address not in self._discovered_printer_by_ip_dict: - Logger.log("i", "Key [%s] does not exist in the discovered printers list.", ip_address) - return - - self.createMachineFromDiscoveredPrinter(self._discovered_printer_by_ip_dict[ip_address]) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 0c205b7b3c..c08b71993b 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -5,7 +5,7 @@ import os from queue import Queue from threading import Event, Thread from time import time -from typing import Optional, TYPE_CHECKING, Dict +from typing import Optional, TYPE_CHECKING, Dict, Callable from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo @@ -39,6 +39,19 @@ if TYPE_CHECKING: i18n_catalog = i18nCatalog("cura") +# +# Represents a request for adding a manual printer. It has the following fields: +# - address: The string of the (IP) address of the manual printer +# - callback: (Optional) Once the HTTP request to the printer to get printer information is done, whether successful +# or not, this callback will be invoked to notify about the result. The callback must have a signature of +# func(success: bool, address: str) -> None +# +class ManualPrinterRequest: + def __init__(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None: + self.address = address + self.callback = callback + + ## This plugin handles the connection detection & creation of output device objects for the UM3 printer. # Zero-Conf is used to detect printers, which are saved in a dict. # If we discover a printer that has the same key as the active machine instance a connection is made. @@ -84,10 +97,12 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._preferences.addPreference("um3networkprinting/manual_instances", "") # A comma-separated list of ip adresses or hostnames - self._manual_instances = self._preferences.getValue("um3networkprinting/manual_instances").split(",") + manual_instances = self._preferences.getValue("um3networkprinting/manual_instances").split(",") + self._manual_instances = {address: ManualPrinterRequest(address) + for address in manual_instances} # type: Dict[str, ManualPrinterRequest] # Store the last manual entry key - self._last_manual_entry_key = "" # type: str + self._last_manual_entry_key = "" # type: str # The zero-conf service changed requests are handled in a separate thread, so we can re-schedule the requests # which fail to get detailed service info. @@ -185,8 +200,6 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.checkCloudFlowIsPossible(None) else: self.getOutputDeviceManager().removeOutputDevice(key) - if key.startswith("manual:"): - self.removeManualDeviceSignal.emit(self.getPluginId(), key, self._discovered_devices[key].address) def stop(self): if self._zero_conf is not None: @@ -198,7 +211,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): # This plugin should always be the fallback option (at least try it): return ManualDeviceAdditionAttempt.POSSIBLE - def removeManualDevice(self, key, address = None): + def removeManualDevice(self, key: str, address: Optional[str] = None) -> None: + if key not in self._discovered_devices and address is not None: + key = "manual:%s" % address + if key in self._discovered_devices: if not address: address = self._discovered_devices[key].ipAddress @@ -206,15 +222,19 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.resetLastManualDevice() if address in self._manual_instances: - self._manual_instances.remove(address) - self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances)) + manual_printer_request = self._manual_instances.pop(address) + self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances.keys())) - self.removeManualDeviceSignal.emit(self.getPluginId(), key, address) + if manual_printer_request.callback is not None: + self._application.callLater(manual_printer_request.callback, False, address) - def addManualDevice(self, address): - if address not in self._manual_instances: - self._manual_instances.append(address) - self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances)) + def addManualDevice(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None: + if address in self._manual_instances: + Logger.log("i", "Manual printer with address [%s] has already been added, do nothing", address) + return + + self._manual_instances[address] = ManualPrinterRequest(address, callback = callback) + self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances.keys())) instance_name = "manual:%s" % address properties = { @@ -319,6 +339,11 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): Logger.log("e", "Something went wrong converting the JSON.") return + if address in self._manual_instances: + manual_printer_request = self._manual_instances[address] + if manual_printer_request.callback is not None: + self._application.callLater(manual_printer_request.callback, True, address) + has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version instance_name = "manual:%s" % address properties = { @@ -362,10 +387,6 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._onRemoveDevice(instance_name) self._onAddDevice(instance_name, address, properties) - if device and address in self._manual_instances: - self.getOutputDeviceManager().addOutputDevice(device) - self.addManualDeviceSignal.emit(self.getPluginId(), device.getId(), address, properties) - def _onRemoveDevice(self, device_id: str) -> None: device = self._discovered_devices.pop(device_id, None) if device: @@ -401,7 +422,9 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties) else: device = LegacyUM3OutputDevice.LegacyUM3OutputDevice(name, address, properties) - self._application.getDiscoveredPrintersModel().addDiscoveredPrinter(address, device.getId(), name, self._createMachineFromDiscoveredPrinter, properties[b"printer_type"].decode("utf-8"), device) + self._application.getDiscoveredPrintersModel().addDiscoveredPrinter( + address, device.getId(), properties[b"name"].decode("utf-8"), self._createMachineFromDiscoveredPrinter, + properties[b"printer_type"].decode("utf-8"), device) self._discovered_devices[device.getId()] = device self.discoveredDevicesChanged.emit() diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index f1b315697a..663d2fd12a 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -18,12 +18,13 @@ Item id: addPrinterByIpScreen - // Whether an IP address is currently being resolved. - property bool hasSentRequest: false - // Whether the IP address user entered can be resolved as a recognizable printer. - property bool haveConnection: false - // True when a request comes back, but the device hasn't responded. - property bool deviceUnresponsive: false + // If there's a manual address resolve request in progress. + property bool hasRequestInProgress: CuraApplication.getDiscoveredPrintersModel().hasManualDeviceRequestInProgress + // Indicates if a request has finished. + property bool hasRequestFinished: false + + property var discoveredPrinter: null + property var isPrinterDiscovered: discoveredPrinter != null Label { @@ -88,7 +89,7 @@ Item regExp: /[a-fA-F0-9\.\:]*/ } - enabled: { ! (addPrinterByIpScreen.hasSentRequest || addPrinterByIpScreen.haveConnection) } + enabled: { ! (addPrinterByIpScreen.hasRequestInProgress || addPrinterByIpScreen.isPrinterDiscovered) } onAccepted: addPrinterButton.clicked() } @@ -99,22 +100,25 @@ Item anchors.left: hostnameField.right anchors.leftMargin: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Add") + enabled: !addPrinterByIpScreen.hasRequestInProgress && !addPrinterByIpScreen.hasRequestFinished onClicked: { - if (hostnameField.text.trim() != "") + const address = hostnameField.text.trim() + if (address == "") { - enabled = false; - addPrinterByIpScreen.deviceUnresponsive = false; - UM.OutputDeviceManager.addManualDevice(hostnameField.text, hostnameField.text); + return } - } - busy: !enabled && !addPrinterByIpScreen.hasSentRequest && !addPrinterByIpScreen.haveConnection - Connections - { - target: UM.OutputDeviceManager - onManualDeviceChanged: { addPrinterButton.enabled = ! UM.OutputDeviceManager.hasManualDevice } + // This address is already in the discovered printer model, no need to add a manual discovery. + if (CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[address]) + { + addPrinterByIpScreen.discoveredPrinter = CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[address] + return + } + + CuraApplication.getDiscoveredPrintersModel().checkManualDevice(address) } + busy: addPrinterByIpScreen.hasRequestInProgress } } @@ -133,14 +137,10 @@ Item color: UM.Theme.getColor("text") renderType: Text.NativeRendering - visible: - { - (addPrinterByIpScreen.hasSentRequest && ! addPrinterByIpScreen.haveConnection) - || addPrinterByIpScreen.deviceUnresponsive - } + visible: addPrinterByIpScreen.hasRequestInProgress || (addPrinterByIpScreen.hasRequestFinished && !addPrinterByIpScreen.isPrinterDiscovered) text: { - if (addPrinterByIpScreen.deviceUnresponsive) + if (addPrinterByIpScreen.hasRequestFinished) { catalog.i18nc("@label", "Could not connect to device.") } @@ -157,7 +157,7 @@ Item anchors.top: parent.top anchors.margins: UM.Theme.getSize("default_margin").width - visible: addPrinterByIpScreen.haveConnection && ! addPrinterByIpScreen.deviceUnresponsive + visible: addPrinterByIpScreen.isPrinterDiscovered Label { @@ -167,7 +167,7 @@ Item color: UM.Theme.getColor("text") renderType: Text.NativeRendering - text: "???" + text: !addPrinterByIpScreen.isPrinterDiscovered ? "???" : addPrinterByIpScreen.discoveredPrinter.name } GridLayout @@ -188,7 +188,7 @@ Item Label { id: typeText - text: "?" + text: !addPrinterByIpScreen.isPrinterDiscovered ? "?" : addPrinterByIpScreen.discoveredPrinter.readableMachineType font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") renderType: Text.NativeRendering @@ -204,7 +204,7 @@ Item Label { id: firmwareText - text: "0.0.0.0" + text: !addPrinterByIpScreen.isPrinterDiscovered ? "0.0.0.0" : addPrinterByIpScreen.discoveredPrinter.device.getProperty("firmware_version") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") renderType: Text.NativeRendering @@ -220,52 +220,25 @@ Item Label { id: addressText - text: "0.0.0.0" + text: !addPrinterByIpScreen.isPrinterDiscovered ? "0.0.0.0" : addPrinterByIpScreen.discoveredPrinter.address font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") renderType: Text.NativeRendering } - - Connections - { - target: UM.OutputDeviceManager - onManualDeviceChanged: - { - if (UM.OutputDeviceManager.hasManualDevice) - { - const type_id = UM.OutputDeviceManager.manualDeviceProperty("printer_type") - var readable_type = Cura.MachineManager.getMachineTypeNameFromId(type_id) - readable_type = (readable_type != "") ? readable_type : catalog.i18nc("@label", "Unknown") - typeText.text = readable_type - firmwareText.text = UM.OutputDeviceManager.manualDeviceProperty("firmware_version") - addressText.text = UM.OutputDeviceManager.manualDeviceProperty("address") - } - else - { - typeText.text = "" - firmwareText.text = "" - addressText.text = "" - } - } - } } Connections { - target: UM.OutputDeviceManager - onManualDeviceChanged: + target: CuraApplication.getDiscoveredPrintersModel() + onManualDeviceRequestFinished: { - if (UM.OutputDeviceManager.hasManualDevice) + var discovered_printers_model = CuraApplication.getDiscoveredPrintersModel() + var printer = discovered_printers_model.discoveredPrintersByAddress[hostnameField.text] + if (printer) { - printerNameLabel.text = UM.OutputDeviceManager.manualDeviceProperty("name") - addPrinterByIpScreen.haveConnection = true - } - else - { - addPrinterByIpScreen.hasSentRequest = false - addPrinterByIpScreen.haveConnection = false - addPrinterByIpScreen.deviceUnresponsive = true + addPrinterByIpScreen.discoveredPrinter = printer } + addPrinterByIpScreen.hasRequestFinished = true } } } @@ -279,7 +252,11 @@ Item anchors.left: parent.left anchors.bottom: parent.bottom text: catalog.i18nc("@button", "Back") - onClicked: base.showPreviousPage() + onClicked: + { + CuraApplication.getDiscoveredPrintersModel().cancelCurrentManualDeviceRequest() + base.showPreviousPage() + } } Cura.PrimaryButton @@ -290,12 +267,10 @@ Item text: catalog.i18nc("@button", "Connect") onClicked: { - CuraApplication.getDiscoveredPrintersModel().createMachineFromDiscoveredPrinterAddress( - UM.OutputDeviceManager.manualDeviceProperty("address")) - UM.OutputDeviceManager.setActiveDevice(UM.OutputDeviceManager.manualDeviceProperty("device_id")) + CuraApplication.getDiscoveredPrintersModel().createMachineFromDiscoveredPrinter(discoveredPrinter) base.showNextPage() } - enabled: addPrinterByIpScreen.haveConnection + enabled: addPrinterByIpScreen.hasRequestFinished && addPrinterByIpScreen.isPrinterDiscovered } } From 9868dee474a2fdaed481b18129481619c2041e61 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 25 Apr 2019 08:57:54 +0200 Subject: [PATCH 2/9] Fix typing CURA-6483 --- cura/Machines/Models/DiscoveredPrintersModel.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cura/Machines/Models/DiscoveredPrintersModel.py b/cura/Machines/Models/DiscoveredPrintersModel.py index 205b3be785..3c7ffe1388 100644 --- a/cura/Machines/Models/DiscoveredPrintersModel.py +++ b/cura/Machines/Models/DiscoveredPrintersModel.py @@ -12,6 +12,7 @@ from UM.OutputDevice.OutputDeviceManager import ManualDeviceAdditionAttempt if TYPE_CHECKING: from PyQt5.QtCore import QObject + from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice @@ -105,7 +106,7 @@ class DiscoveredPrintersModel(QObject): self._application = application self._discovered_printer_by_ip_dict = dict() # type: Dict[str, DiscoveredPrinter] - self._plugin_for_manual_device = None + self._plugin_for_manual_device = None # type: Optional[OutputDevicePlugin] self._manual_device_address = "" discoveredPrintersChanged = pyqtSignal() @@ -141,7 +142,8 @@ class DiscoveredPrintersModel(QObject): @pyqtSlot() def cancelCurrentManualDeviceRequest(self) -> None: if self._manual_device_address: - self._plugin_for_manual_device.removeManualDevice(self._manual_device_address, address = self._manual_device_address) + if self._plugin_for_manual_device is not None: + self._plugin_for_manual_device.removeManualDevice(self._manual_device_address, address = self._manual_device_address) self._manual_device_address = "" self._plugin_for_manual_device = None self.hasManualDeviceRequestInProgressChanged.emit() From b22073e1707c96945d09b51922424f483b60119a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 25 Apr 2019 11:54:30 +0200 Subject: [PATCH 3/9] Add timeout for manual IP requests CURA-6483 --- cura/Machines/Models/DiscoveredPrintersModel.py | 16 +++++++++++++++- .../qml/WelcomePages/AddPrinterByIpContent.qml | 9 +++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/cura/Machines/Models/DiscoveredPrintersModel.py b/cura/Machines/Models/DiscoveredPrintersModel.py index 3c7ffe1388..3d2ca546f3 100644 --- a/cura/Machines/Models/DiscoveredPrintersModel.py +++ b/cura/Machines/Models/DiscoveredPrintersModel.py @@ -3,7 +3,7 @@ from typing import Callable, Dict, List, Optional, TYPE_CHECKING -from PyQt5.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject +from PyQt5.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject, QTimer from UM.i18n import i18nCatalog from UM.Logger import Logger @@ -109,6 +109,12 @@ class DiscoveredPrintersModel(QObject): self._plugin_for_manual_device = None # type: Optional[OutputDevicePlugin] self._manual_device_address = "" + self._manual_device_request_timeout_in_seconds = 5 # timeout for adding a manual device in seconds + self._manual_device_request_timer = QTimer() + self._manual_device_request_timer.setInterval(self._manual_device_request_timeout_in_seconds * 1000) + self._manual_device_request_timer.setSingleShot(True) + self._manual_device_request_timer.timeout.connect(self._onManualRequestTimeout) + discoveredPrintersChanged = pyqtSignal() @pyqtSlot(str) @@ -137,10 +143,13 @@ class DiscoveredPrintersModel(QObject): self._plugin_for_manual_device = plugin self._plugin_for_manual_device.addManualDevice(address, callback = self._onManualDeviceRequestFinished) self._manual_device_address = address + self._manual_device_request_timer.start() self.hasManualDeviceRequestInProgressChanged.emit() @pyqtSlot() def cancelCurrentManualDeviceRequest(self) -> None: + self._manual_device_request_timer.stop() + if self._manual_device_address: if self._plugin_for_manual_device is not None: self._plugin_for_manual_device.removeManualDevice(self._manual_device_address, address = self._manual_device_address) @@ -149,6 +158,10 @@ class DiscoveredPrintersModel(QObject): self.hasManualDeviceRequestInProgressChanged.emit() self.manualDeviceRequestFinished.emit(False) + def _onManualRequestTimeout(self) -> None: + Logger.log("w", "Manual printer [%s] request timed out. Cancel the current request.", self._manual_device_address) + self.cancelCurrentManualDeviceRequest() + hasManualDeviceRequestInProgressChanged = pyqtSignal() @pyqtProperty(bool, notify = hasManualDeviceRequestInProgressChanged) @@ -158,6 +171,7 @@ class DiscoveredPrintersModel(QObject): manualDeviceRequestFinished = pyqtSignal(bool, arguments = ["success"]) def _onManualDeviceRequestFinished(self, success: bool, address: str) -> None: + self._manual_device_request_timer.stop() if address == self._manual_device_address: self._manual_device_address = "" self.hasManualDeviceRequestInProgressChanged.emit() diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 663d2fd12a..402d56ab22 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -26,6 +26,15 @@ Item property var discoveredPrinter: null property var isPrinterDiscovered: discoveredPrinter != null + // Make sure to cancel the current request when this page closes. + onVisibleChanged: + { + if (!visible) + { + CuraApplication.getDiscoveredPrintersModel().cancelCurrentManualDeviceRequest() + } + } + Label { id: titleLabel From 4f05ea578c8c009105ecfeced3a85307a9a6ec5c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 25 Apr 2019 11:55:47 +0200 Subject: [PATCH 4/9] Cancel the network request when cancel the manual ip request CURA-6483 --- .../src/UM3OutputDevicePlugin.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index c08b71993b..f820b6244c 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -9,7 +9,7 @@ from typing import Optional, TYPE_CHECKING, Dict, Callable from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo -from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager +from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager from PyQt5.QtCore import QUrl from PyQt5.QtGui import QDesktopServices @@ -45,11 +45,14 @@ i18n_catalog = i18nCatalog("cura") # - callback: (Optional) Once the HTTP request to the printer to get printer information is done, whether successful # or not, this callback will be invoked to notify about the result. The callback must have a signature of # func(success: bool, address: str) -> None +# - network_reply: This is the QNetworkReply instance for this request if the request has been issued and still in +# progress. It is kept here so we can cancel a request when needed. # class ManualPrinterRequest: def __init__(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None: self.address = address self.callback = callback + self.network_reply = None # type: Optional["QNetworkReply"] ## This plugin handles the connection detection & creation of output device objects for the UM3 printer. @@ -225,6 +228,9 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): manual_printer_request = self._manual_instances.pop(address) self._preferences.setValue("um3networkprinting/manual_instances", ",".join(self._manual_instances.keys())) + if manual_printer_request.network_reply is not None: + manual_printer_request.network_reply.abort() + if manual_printer_request.callback is not None: self._application.callLater(manual_printer_request.callback, False, address) @@ -250,7 +256,8 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._onAddDevice(instance_name, address, properties) self._last_manual_entry_key = instance_name - self._checkManualDevice(address) + reply = self._checkManualDevice(address) + self._manual_instances[address].network_reply = reply def _createMachineFromDiscoveredPrinter(self, key: str) -> None: discovered_device = self._discovered_devices.get(key) @@ -307,13 +314,13 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.refreshConnections() - def _checkManualDevice(self, address: str) -> None: + def _checkManualDevice(self, address: str) -> "QNetworkReply": # Check if a UM3 family device exists at this address. # If a printer responds, it will replace the preliminary printer created above # origin=manual is for tracking back the origin of the call url = QUrl("http://" + address + self._api_prefix + "system") name_request = QNetworkRequest(url) - self._network_manager.get(name_request) + return self._network_manager.get(name_request) def _onNetworkRequestFinished(self, reply: "QNetworkReply") -> None: reply_url = reply.url().toString() @@ -341,6 +348,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): if address in self._manual_instances: manual_printer_request = self._manual_instances[address] + manual_printer_request.network_reply = None if manual_printer_request.callback is not None: self._application.callLater(manual_printer_request.callback, True, address) From dbd8d4e74c4e8e19519de761705bc441aef96d52 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 26 Apr 2019 12:48:28 +0200 Subject: [PATCH 5/9] Use IPv4 and IPv6 regex to validate IP input CURA-6483 --- resources/qml/WelcomePages/AddPrinterByIpContent.qml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 402d56ab22..20a68ebd65 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -95,9 +95,11 @@ Item validator: RegExpValidator { - regExp: /[a-fA-F0-9\.\:]*/ + 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*$))?/ } + placeholderText: catalog.i18nc("@text", "Place enter your printer's IP address.") + enabled: { ! (addPrinterByIpScreen.hasRequestInProgress || addPrinterByIpScreen.isPrinterDiscovered) } onAccepted: addPrinterButton.clicked() } @@ -109,14 +111,10 @@ Item anchors.left: hostnameField.right anchors.leftMargin: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Add") - enabled: !addPrinterByIpScreen.hasRequestInProgress && !addPrinterByIpScreen.hasRequestFinished + enabled: !addPrinterByIpScreen.hasRequestInProgress && !addPrinterByIpScreen.hasRequestFinished && (hostnameField.state != "invalid" && hostnameField.text != "") onClicked: { - const address = hostnameField.text.trim() - if (address == "") - { - return - } + const address = hostnameField.text // This address is already in the discovered printer model, no need to add a manual discovery. if (CuraApplication.getDiscoveredPrintersModel().discoveredPrintersByAddress[address]) From 278d8ee587c6d90e93639c6cd73a4edcbdd4aa7b Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 26 Apr 2019 13:30:24 +0200 Subject: [PATCH 6/9] Fix button enabled flags CURA-6483 --- resources/qml/WelcomePages/AddPrinterByIpContent.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 20a68ebd65..de9562908c 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -111,7 +111,7 @@ Item anchors.left: hostnameField.right anchors.leftMargin: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Add") - enabled: !addPrinterByIpScreen.hasRequestInProgress && !addPrinterByIpScreen.hasRequestFinished && (hostnameField.state != "invalid" && hostnameField.text != "") + enabled: !addPrinterByIpScreen.hasRequestInProgress && !addPrinterByIpScreen.isPrinterDiscovered && (hostnameField.state != "invalid" && hostnameField.text != "") onClicked: { const address = hostnameField.text @@ -278,6 +278,6 @@ Item base.showNextPage() } - enabled: addPrinterByIpScreen.hasRequestFinished && addPrinterByIpScreen.isPrinterDiscovered + enabled: addPrinterByIpScreen.isPrinterDiscovered } } From c7bee73af6a20466d3a434498365714b026192e3 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Apr 2019 13:49:55 +0200 Subject: [PATCH 7/9] Add cross cursor to the draggable expandable component. Contributes to CURA-6478. --- resources/qml/ExpandableComponent.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 53f9f85341..ba8bed067a 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -258,6 +258,7 @@ Item MouseArea { id: dragRegion + cursorShape: Qt.SizeAllCursor anchors { top: parent.top From 5ab2f9784d6ef75f91be11f494120b8681b6cdbe Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 26 Apr 2019 13:55:10 +0200 Subject: [PATCH 8/9] Fix unit test CURA-6483 --- tests/Machines/Models/TestDiscoveredPrintersModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Machines/Models/TestDiscoveredPrintersModel.py b/tests/Machines/Models/TestDiscoveredPrintersModel.py index 28be2536a0..8d9a770c2a 100644 --- a/tests/Machines/Models/TestDiscoveredPrintersModel.py +++ b/tests/Machines/Models/TestDiscoveredPrintersModel.py @@ -7,7 +7,7 @@ from cura.Machines.Models.DiscoveredPrintersModel import DiscoveredPrintersModel @pytest.fixture() def discovered_printer_model(application) -> DiscoveredPrintersModel: - return DiscoveredPrintersModel() + return DiscoveredPrintersModel(application) def test_discoveredPrinters(discovered_printer_model): From 17c06f200fb9039bd6f7d617aa5f3c76e8732d8c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Apr 2019 14:04:46 +0200 Subject: [PATCH 9/9] Change the copy of the account benefits As per @mahtDFR request --- resources/qml/Account/GeneralOperations.qml | 2 +- resources/qml/WelcomePages/CloudContent.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml index f6bd7ce588..f01b9538bd 100644 --- a/resources/qml/Account/GeneralOperations.qml +++ b/resources/qml/Account/GeneralOperations.qml @@ -48,7 +48,7 @@ Column anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignLeft renderType: Text.NativeRendering - text: catalog.i18nc("@text", "- Send print jobs to Ultimaker printers outside your local network\n- Store your Ultimaker Cura settings in the cloud for use anywhere\n- Get exclusive access to material profiles from leading brands") + text: catalog.i18nc("@text", "- Send print jobs to Ultimaker printers outside your local network\n- Store your Ultimaker Cura settings in the cloud for use anywhere\n- Get exclusive access to print profiles from leading brands") lineHeight: 1.4 font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") diff --git a/resources/qml/WelcomePages/CloudContent.qml b/resources/qml/WelcomePages/CloudContent.qml index bb1498ebd5..b0b4d53cf1 100644 --- a/resources/qml/WelcomePages/CloudContent.qml +++ b/resources/qml/WelcomePages/CloudContent.qml @@ -97,7 +97,7 @@ Item t = catalog.i18nc("@text", "- Store your Ultimaker Cura settings in the cloud for use anywhere") full_text += "

" + t + "

" - t = catalog.i18nc("@text", "- Get exclusive access to material profiles from leading brands") + t = catalog.i18nc("@text", "- Get exclusive access to print profiles from leading brands") full_text += "

" + t + "

" return full_text