diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 53390ca88d..15e2c67c33 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -527,6 +527,12 @@ class MachineManager(QObject): return self._global_container_stack.getMetaDataEntry("um_network_key", "") return "" + @pyqtProperty(str, notify=printerConnectedStatusChanged) + def activeMachineCloudKey(self) -> str: + if self._global_container_stack: + return self._global_container_stack.getMetaDataEntry("um_cloud_cluster_id", "") + return "" + @pyqtProperty(str, notify = printerConnectedStatusChanged) def activeMachineNetworkGroupName(self) -> str: if self._global_container_stack: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index adc670ad1e..c91944fe4d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -288,8 +288,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): for updated_job_id in set(current_jobs).intersection(remote_jobs): self._updateUM3PrintJobOutputModel(current_jobs[updated_job_id], remote_jobs[updated_job_id]) - # TODO: properly handle removed and updated printers - self.printJobsChanged.emit() + # We only have to update when jobs are added or removed + # updated jobs push their changes via their outputmodel + if len(removed_job_ids) > 0 or len(new_job_ids) > 0: + self.printJobsChanged.emit() def _addPrintJob(self, job: CloudClusterPrintJob) -> None: try: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 22e2d57b05..6ab72d8ee3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,7 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from threading import Timer -from typing import Dict, List +from typing import Dict, List, Optional + +from PyQt5.QtCore import QTimer from UM import i18nCatalog from UM.Logger import Logger @@ -9,7 +10,6 @@ from UM.Message import Message from UM.Signal import Signal from cura.CuraApplication import CuraApplication from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient - from .CloudOutputDevice import CloudOutputDevice from .Models import CloudCluster, CloudErrorObject @@ -39,23 +39,27 @@ class CloudOutputDeviceManager: self._account = application.getCuraAPI().account self._account.loginStateChanged.connect(self._getRemoteClusters) self._api = CloudApiClient(self._account, self._onApiError) - + # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._connectToActiveMachine) self._on_cluster_received = Signal() self._on_cluster_received.connect(self._getRemoteClusters) + self.update_timer = QTimer(CuraApplication.getInstance()) + self.update_timer.setInterval(self.CHECK_CLUSTER_INTERVAL * 1000) + self.update_timer.setSingleShot(False) + self.update_timer.timeout.connect(self._on_cluster_received.emit) + ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: Logger.log("i", "Retrieving remote clusters") if self._account.isLoggedIn: self._api.getClusters(self._onGetRemoteClustersFinished) - # Only start the polling thread after the user is authenticated + # Only start the polling timer after the user is authenticated # The first call to _getRemoteClusters comes from self._account.loginStateChanged - timer = Timer(5.0, self._on_cluster_received.emit) - timer.start() + self.update_timer.start() ## Callback for when the request for getting the clusters. is finished. def _onGetRemoteClustersFinished(self, clusters: List[CloudCluster]) -> None: @@ -85,7 +89,7 @@ class CloudOutputDeviceManager: self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device device.connect() # TODO: remove this - self._connectToActiveMachine() + self._connectToActiveMachine(cluster.cluster_id, cluster.host_name) ## Remove a CloudOutputDevice # \param cluster: The cluster that was removed @@ -94,11 +98,16 @@ class CloudOutputDeviceManager: del self._remote_clusters[cluster.cluster_id] ## Callback for when the active machine was changed by the user. - def _connectToActiveMachine(self) -> None: + def _connectToActiveMachine(self, cluster_id: Optional[str] = None, host_name: Optional[str] = None) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return - + + # TODO: Remove this once correct pairing has been added (see below). + if cluster_id: + active_machine.setMetaDataEntry("um_cloud_cluster_id", cluster_id) + active_machine.setMetaDataEntry("connect_group_name", host_name) + # Check if the stored cluster_id for the active machine is in our list of remote clusters. stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") if stored_cluster_id in self._remote_clusters.keys(): diff --git a/resources/definitions/alfawise_u20.def.json b/resources/definitions/alfawise_u20.def.json index 87726fec3d..de8525fa4d 100644 --- a/resources/definitions/alfawise_u20.def.json +++ b/resources/definitions/alfawise_u20.def.json @@ -7,7 +7,7 @@ "author": "Samuel Pinches", "manufacturer": "Alfawise", "file_formats": "text/x-gcode", - "preferred_quality_type": "fine", + "preferred_quality_type": "fast", "machine_extruder_trains": { "0": "alfawise_u20_extruder_0" @@ -53,9 +53,6 @@ "material_bed_temperature": { "default_value": 50 }, - "layer_height": { - "default_value": 0.15 - }, "layer_height_0": { "default_value": 0.2 }, diff --git a/resources/definitions/jgaurora_a1.def.json b/resources/definitions/jgaurora_a1.def.json index 4fd2eb4994..b9a921c311 100644 --- a/resources/definitions/jgaurora_a1.def.json +++ b/resources/definitions/jgaurora_a1.def.json @@ -7,7 +7,7 @@ "author": "Samuel Pinches", "manufacturer": "JGAurora", "file_formats": "text/x-gcode", - "preferred_quality_type": "fine", + "preferred_quality_type": "fast", "machine_extruder_trains": { "0": "jgaurora_a1_extruder_0" @@ -53,9 +53,6 @@ "material_bed_temperature": { "default_value": 67 }, - "layer_height": { - "default_value": 0.15 - }, "layer_height_0": { "default_value": 0.12 }, diff --git a/resources/definitions/jgaurora_a5.def.json b/resources/definitions/jgaurora_a5.def.json index 02d9a9db4f..d84a8440e6 100644 --- a/resources/definitions/jgaurora_a5.def.json +++ b/resources/definitions/jgaurora_a5.def.json @@ -9,7 +9,7 @@ "file_formats": "text/x-gcode", "platform": "jgaurora_a5.stl", "platform_offset": [-242, -101, 273], - "preferred_quality_type": "fine", + "preferred_quality_type": "fast", "machine_extruder_trains": { "0": "jgaurora_a5_extruder_0" @@ -55,9 +55,6 @@ "material_bed_temperature": { "default_value": 67 }, - "layer_height": { - "default_value": 0.15 - }, "layer_height_0": { "default_value": 0.12 }, diff --git a/resources/definitions/jgaurora_z_603s.def.json b/resources/definitions/jgaurora_z_603s.def.json index 59e0ff129c..3a78585240 100644 --- a/resources/definitions/jgaurora_z_603s.def.json +++ b/resources/definitions/jgaurora_z_603s.def.json @@ -7,7 +7,7 @@ "author": "Samuel Pinches", "manufacturer": "JGAurora", "file_formats": "text/x-gcode", - "preferred_quality_type": "fine", + "preferred_quality_type": "fast", "machine_extruder_trains": { "0": "jgaurora_z_603s_extruder_0" @@ -53,9 +53,6 @@ "material_bed_temperature": { "default_value": 55 }, - "layer_height": { - "default_value": 0.15 - }, "layer_height_0": { "default_value": 0.2 }, diff --git a/resources/qml/Menus/CloudPrinterMenu.qml b/resources/qml/Menus/CloudPrinterMenu.qml new file mode 100644 index 0000000000..bd03890642 --- /dev/null +++ b/resources/qml/Menus/CloudPrinterMenu.qml @@ -0,0 +1,26 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. +import QtQuick 2.2 +import QtQuick.Controls 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Instantiator { + + model: UM.ContainerStacksModel { + filter: {"type": "machine", "um_cloud_cluster_id": "*"} + } + + MenuItem { + // iconSource: UM.Theme.getIcon("printer_single") TODO: use cloud icon here + text: model.metadata["connect_group_name"] + checkable: true + checked: true // cloud printers are only listed if they are actually online + exclusiveGroup: group; + onTriggered: Cura.MachineManager.setActiveMachine(model.id); + } + + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) +} diff --git a/resources/qml/Menus/PrinterMenu.qml b/resources/qml/Menus/PrinterMenu.qml index 741d927c13..a924b0e589 100644 --- a/resources/qml/Menus/PrinterMenu.qml +++ b/resources/qml/Menus/PrinterMenu.qml @@ -37,6 +37,23 @@ Menu visible: networkPrinterMenu.count > 0 } + MenuItem + { + text: catalog.i18nc("@label:category menu label", "Cloud enabled printers") + enabled: false + visible: cloudPrinterMenu.count > 0 + } + + CloudPrinterMenu + { + id: cloudPrinterMenu + } + + MenuSeparator + { + visible: cloudPrinterMenu.count > 0 + } + MenuItem { text: catalog.i18nc("@label:category menu label", "Local printers") diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index e319069502..1b964cad0c 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -115,15 +115,16 @@ UM.PreferencesPage currentIndex: { + var idx = -1; for(var i = 0; i < settingVisibilityPresetsModel.items.length; ++i) { if(settingVisibilityPresetsModel.items[i].presetId == settingVisibilityPresetsModel.activePreset) { - currentIndex = i; - return; + idx = i; + break; } } - return -1 + return idx; } onActivated: diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 5ef04b7351..26c703fddd 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -50,6 +50,46 @@ Column } } + Label + { + text: catalog.i18nc("@label", "Cloud connected printers") + visible: cloudPrintersModel.items.length > 0 + leftPadding: UM.Theme.getSize("default_margin").width + height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + renderType: Text.NativeRendering + font: UM.Theme.getFont("medium") + color: UM.Theme.getColor("text_medium") + verticalAlignment: Text.AlignVCenter + } + + Repeater + { + id: cloudPrinters + + model: UM.ContainerStacksModel + { + id: cloudPrintersModel + filter: + { + "type": "machine", + "um_cloud_cluster_id": "*" + } + } + + delegate: MachineSelectorButton + { + text: model.metadata["connect_group_name"] + checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + + Connections + { + target: Cura.MachineManager + onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + } + } + } + Label { text: catalog.i18nc("@label", "Preset printers") @@ -71,7 +111,9 @@ Column id: virtualPrintersModel filter: { - "type": "machine", "um_network_key": null + "type": "machine", + "um_network_key": null, + "um_cloud_cluster_id": null } }