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
This commit is contained in:
Remco Burema 2022-09-02 17:49:39 +02:00
parent ccdbc3e06f
commit a56a21cf93
5 changed files with 151 additions and 9 deletions

View File

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

View File

@ -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]
})

View File

@ -5,10 +5,13 @@
# online cloud connected printers are represented within this ListModel. Additional information such as the number of # online cloud connected printers are represented within this ListModel. Additional information such as the number of
# connected printers for each printer type is gathered. # 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.Qt.ListModel import ListModel
from UM.Settings.ContainerStack import ContainerStack from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.Interfaces import ContainerInterface
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Util import parseBool from UM.Util import parseBool
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
@ -27,7 +30,7 @@ class MachineListModel(ListModel):
IsAbstractMachineRole = Qt.ItemDataRole.UserRole + 7 IsAbstractMachineRole = Qt.ItemDataRole.UserRole + 7
ComponentTypeRole = Qt.ItemDataRole.UserRole + 8 ComponentTypeRole = Qt.ItemDataRole.UserRole + 8
def __init__(self, parent=None) -> None: def __init__(self, parent: Optional[QObject] = None) -> None:
super().__init__(parent) super().__init__(parent)
self._show_cloud_printers = False self._show_cloud_printers = False
@ -66,7 +69,7 @@ class MachineListModel(ListModel):
self._updateDelayed() self._updateDelayed()
self.showCloudPrintersChanged.emit(show_cloud_printers) 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""" """Handler for container added/removed events from registry"""
# We only need to update when the added / removed container GlobalStack # We only need to update when the added / removed container GlobalStack
@ -79,14 +82,15 @@ class MachineListModel(ListModel):
def _update(self) -> None: def _update(self) -> None:
self.clear() self.clear()
from cura.CuraApplication import CuraApplication
machines_manager = CuraApplication.getInstance().getMachineManager()
other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine") other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine")
abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True") abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True")
abstract_machine_stacks.sort(key = lambda machine: machine.getName(), reverse = True) abstract_machine_stacks.sort(key = lambda machine: machine.getName(), reverse = True)
for abstract_machine in abstract_machine_stacks: for abstract_machine in abstract_machine_stacks:
definition_id = abstract_machine.definition.getId() 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) online_machine_stacks = machines_manager.getMachinesWithDefinition(definition_id, online_only = True)
# Create a list item for abstract machine # Create a list item for abstract machine

View File

@ -1,11 +1,16 @@
from time import time from time import time
from typing import List from typing import List, Optional
from PyQt6.QtCore import QObject from PyQt6.QtCore import QObject
from PyQt6.QtNetwork import QNetworkReply from PyQt6.QtNetwork import QNetworkReply
from UM import i18nCatalog from UM import i18nCatalog
from UM.Logger import Logger 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.NetworkedPrinterOutputDevice import AuthState
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from .CloudApiClient import CloudApiClient from .CloudApiClient import CloudApiClient
@ -31,6 +36,8 @@ class AbstractCloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
parent=parent parent=parent
) )
self._on_print_dialog: Optional[QObject] = None
self._setInterfaceElements() self._setInterfaceElements()
def connect(self) -> None: def connect(self) -> None:
@ -84,4 +91,26 @@ class AbstractCloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
self._updatePrinters(all_configurations) self._updatePrinters(all_configurations)
def _onError(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None: def _onError(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None:
# TODO!
pass 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())

View File

@ -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
}
}
}
}