Browse Source

W.I.P.: Press print on abstract cloud printer. User should see dialog.

Start of implementation. When printing on an abstract printer, a user should see a dialog with the matching concrete cloud printers to pick from to actually print. Names are not final. Very much a work in progress. Very not finished also.

start of implementation for CURA-9278
Remco Burema 2 years ago
parent
commit
a56a21cf93

+ 2 - 0
cura/CuraApplication.py

@@ -115,6 +115,7 @@ from . import CuraActions
 from . import PlatformPhysics
 from . import PrintJobPreviewImageProvider
 from .AutoSave import AutoSave
+from .Machines.Models.CompatibleMachineModel import CompatibleMachineModel
 from .Machines.Models.MachineListModel import MachineListModel
 from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
 from .Machines.Models.IntentSelectionModel import IntentSelectionModel
@@ -1191,6 +1192,7 @@ class CuraApplication(QtApplication):
         qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
         qmlRegisterType(GlobalStacksModel, "Cura", 1, 0, "GlobalStacksModel")
         qmlRegisterType(MachineListModel, "Cura", 1, 0, "MachineListModel")
+        qmlRegisterType(CompatibleMachineModel, "Cura", 1, 0, "CompatibleMachineModel")
 
         self.processEvents()
         qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel")

+ 73 - 0
cura/Machines/Models/CompatibleMachineModel.py

@@ -0,0 +1,73 @@
+# Copyright (c) 2022 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+# TODO?: documentation
+
+from typing import Optional
+
+from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSlot, pyqtProperty, pyqtSignal
+
+from UM.Qt.ListModel import ListModel
+from UM.Settings.ContainerStack import ContainerStack
+from UM.i18n import i18nCatalog
+from UM.Util import parseBool
+
+from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
+from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
+
+
+class CompatibleMachineModel(ListModel):
+    NameRole = Qt.ItemDataRole.UserRole + 1
+    IdRole = Qt.ItemDataRole.UserRole + 2
+    ExtrudersRole = Qt.ItemDataRole.UserRole + 3
+
+    def __init__(self, parent: Optional[QObject] = None) -> None:
+        super().__init__(parent)
+
+        self._filter_on_definition_id: Optional[str] = None
+
+        self._catalog = i18nCatalog("cura")
+
+        self.addRoleName(self.NameRole, "name")
+        self.addRoleName(self.IdRole, "id")
+        self.addRoleName(self.ExtrudersRole, "extruders")
+
+    filterChanged = pyqtSignal(str)
+
+    @pyqtSlot(str)
+    def setFilter(self, abstract_machine_id: str) -> None:
+        # TODO??: defensive coding; check if machine is abstract & abort/log if not
+        self._filter_on_definition_id = abstract_machine_id
+
+        # Don't need a delayed update, since it's fire once on user click (either on 'print to cloud' or 'refresh').
+        # So, no signals that could come in (too) quickly.
+        self.filterChanged.emit(self._filter_on_definition_id)
+        self._update()
+
+    @pyqtProperty(str, fset=setFilter, notify=filterChanged)
+    def filter(self) -> str:
+        return self._filter_on_definition_id
+
+    def _update(self) -> None:
+        self.clear()
+        if not self._filter_on_definition_id or self._filter_on_definition_id == "":
+            # TODO?: log
+            return
+
+        from cura.CuraApplication import CuraApplication
+        machine_manager = CuraApplication.getInstance().getMachineManager()
+        compatible_machines = machine_manager.getMachinesWithDefinition(self._filter_on_definition_id, online_only = True)
+        # TODO: Handle 0 compatible machines -> option to close window? Message in card? (remember  the design has a refresh button!)
+
+        for container_stack in compatible_machines:
+            if parseBool(container_stack.getMetaDataEntry("hidden", False)) or parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)):
+                continue
+            self.addItem(container_stack)
+
+    def addItem(self, container_stack: ContainerStack, machine_count: int = 0) -> None:
+        extruders = CuraContainerRegistry.getInstance().findContainerStacks(type="extruder_train", machine=container_stack.getId())
+        self.appendItem({
+                         "name": container_stack.getName(),
+                         "id": container_stack.getId(),
+                         "extruders": [extruder.getMetaData().copy() for extruder in extruders]
+                        })

+ 12 - 8
cura/Machines/Models/MachineListModel.py

@@ -5,10 +5,13 @@
 # online cloud connected printers are represented within this ListModel. Additional information such as the number of
 # connected printers for each printer type is gathered.
 
-from PyQt6.QtCore import Qt, QTimer, pyqtSlot, pyqtProperty, pyqtSignal
+from typing import Optional
+
+from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSlot, pyqtProperty, pyqtSignal
 
 from UM.Qt.ListModel import ListModel
 from UM.Settings.ContainerStack import ContainerStack
+from UM.Settings.Interfaces import ContainerInterface
 from UM.i18n import i18nCatalog
 from UM.Util import parseBool
 from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
@@ -27,7 +30,7 @@ class MachineListModel(ListModel):
     IsAbstractMachineRole = Qt.ItemDataRole.UserRole + 7
     ComponentTypeRole = Qt.ItemDataRole.UserRole + 8
 
-    def __init__(self, parent=None) -> None:
+    def __init__(self, parent: Optional[QObject] = None) -> None:
         super().__init__(parent)
 
         self._show_cloud_printers = False
@@ -66,7 +69,7 @@ class MachineListModel(ListModel):
         self._updateDelayed()
         self.showCloudPrintersChanged.emit(show_cloud_printers)
 
-    def _onContainerChanged(self, container) -> None:
+    def _onContainerChanged(self, container: ContainerInterface) -> None:
         """Handler for container added/removed events from registry"""
 
         # We only need to update when the added / removed container GlobalStack
@@ -79,14 +82,15 @@ class MachineListModel(ListModel):
     def _update(self) -> None:
         self.clear()
 
+        from cura.CuraApplication import CuraApplication
+        machines_manager = CuraApplication.getInstance().getMachineManager()
+
         other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine")
 
         abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True")
         abstract_machine_stacks.sort(key = lambda machine: machine.getName(), reverse = True)
         for abstract_machine in abstract_machine_stacks:
             definition_id = abstract_machine.definition.getId()
-            from cura.CuraApplication import CuraApplication
-            machines_manager = CuraApplication.getInstance().getMachineManager()
             online_machine_stacks = machines_manager.getMachinesWithDefinition(definition_id, online_only = True)
 
             # Create a list item for abstract machine
@@ -132,11 +136,11 @@ class MachineListModel(ListModel):
             has_connection |= connection_type in container_stack.configuredConnectionTypes
 
         self.appendItem({
-                        "componentType": "MACHINE",
-                        "name": container_stack.getName(),
+                         "componentType": "MACHINE",
+                         "name": container_stack.getName(),
                          "id": container_stack.getId(),
                          "metadata": container_stack.getMetaData().copy(),
                          "isOnline": parseBool(container_stack.getMetaDataEntry("is_online", False)) and has_connection,
                          "isAbstractMachine": parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)),
                          "machineCount": machine_count,
-                         })
+                        })

+ 30 - 1
plugins/UM3NetworkPrinting/src/Cloud/AbstractCloudOutputDevice.py

@@ -1,11 +1,16 @@
 from time import time
-from typing import List
+from typing import List, Optional
 
 from PyQt6.QtCore import QObject
 from PyQt6.QtNetwork import QNetworkReply
 
 from UM import i18nCatalog
 from UM.Logger import Logger
+from UM.FileHandler.FileHandler import FileHandler
+from UM.Resources import Resources
+from UM.Scene.SceneNode import SceneNode
+
+from cura.CuraApplication import CuraApplication
 from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState
 from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
 from .CloudApiClient import CloudApiClient
@@ -31,6 +36,8 @@ class AbstractCloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
             parent=parent
         )
 
+        self._on_print_dialog: Optional[QObject] = None
+
         self._setInterfaceElements()
 
     def connect(self) -> None:
@@ -84,4 +91,26 @@ class AbstractCloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
         self._updatePrinters(all_configurations)
 
     def _onError(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None:
+        # TODO!
         pass
+
+    def _openChoosePrinterDialog(self, machine_filter_id: str) -> None:
+        if self._on_print_dialog is None:
+            qml_path = Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Dialogs", "ChoosePrinterDialog.qml")
+            self._on_print_dialog = CuraApplication.getInstance().createQmlComponent(qml_path, {})
+        if self._on_print_dialog is None:  # Failed to load QML file.
+            return
+        self._on_print_dialog.setProperty("machine_id_filter", machine_filter_id)
+        self._on_print_dialog.show()
+
+    def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs) -> None:
+
+        # TODO:
+        #  - Prettify (and make usable) dialog.
+        #    (Including extruders... their metadata is already in the model. Is that enough though. Does that contain configurations as well?)
+        #  - On button clicked, fetch/push to here selected printer, hide dialog
+        #  - Find correct output-device for selected printer maybe via `CuraApplication.getInstance().getOutputDeviceManager().getOutputDevices()`
+        #    Call 'requestWrite' of the selected output-device.
+
+        self._openChoosePrinterDialog(CuraApplication.getInstance().getGlobalContainerStack().definition.getId())
+

+ 34 - 0
resources/qml/Dialogs/ChoosePrinterDialog.qml

@@ -0,0 +1,34 @@
+// Copyright (c) 2022 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.2
+import QtQuick.Controls 2.9
+
+import UM 1.5 as UM
+import Cura 1.0 as Cura
+
+UM.Dialog
+{
+    id: base
+
+    property string machine_id_filter: ""
+
+    Column
+    {
+        anchors.fill: parent
+
+        Repeater
+        {
+            id: contents
+
+            model: Cura.CompatibleMachineModel
+            {
+                filter: machine_id_filter
+            }
+            delegate: UM.Label
+            {
+                text: model.name
+            }
+        }
+    }
+}