Remove AbstractMachine

Having a separate class for the AbstractMachine complicated things; it's behaviour was extremely similar to the GlobalStack so adding one more stack container type in addition to the many similar setting container types we already have adds complexity to the system. Having these different classes for machines and abstract machines also add complexity to the update script as the abstract machines were stored in a separate folder from the machine types.

Because of these reasons we decided to replace the AbstractMachine by a GlobalStack where the is_abstract_machine property metadata property is set to True.

CURA-9514, CURA-9277

Co-authored-by: joeydelarago <joeydelarago@gmail.com>
This commit is contained in:
c.lamboo 2022-08-26 10:45:45 +02:00
parent d843921c7a
commit 8b84db7059
6 changed files with 44 additions and 78 deletions

View file

@ -146,8 +146,6 @@ class CuraApplication(QtApplication):
DefinitionChangesContainer = Resources.UserType + 10 DefinitionChangesContainer = Resources.UserType + 10
SettingVisibilityPreset = Resources.UserType + 11 SettingVisibilityPreset = Resources.UserType + 11
IntentInstanceContainer = Resources.UserType + 12 IntentInstanceContainer = Resources.UserType + 12
AbstractMachineStack = Resources.UserType + 13
pyqtEnum(ResourceTypes) pyqtEnum(ResourceTypes)
@ -426,7 +424,6 @@ class CuraApplication(QtApplication):
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility") Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
Resources.addStorageType(self.ResourceTypes.IntentInstanceContainer, "intent") Resources.addStorageType(self.ResourceTypes.IntentInstanceContainer, "intent")
Resources.addStorageType(self.ResourceTypes.AbstractMachineStack, "abstract_machine_instances")
self._container_registry.addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality") self._container_registry.addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
self._container_registry.addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes") self._container_registry.addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
@ -437,7 +434,6 @@ class CuraApplication(QtApplication):
self._container_registry.addResourceType(self.ResourceTypes.MachineStack, "machine") self._container_registry.addResourceType(self.ResourceTypes.MachineStack, "machine")
self._container_registry.addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") self._container_registry.addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
self._container_registry.addResourceType(self.ResourceTypes.IntentInstanceContainer, "intent") self._container_registry.addResourceType(self.ResourceTypes.IntentInstanceContainer, "intent")
self._container_registry.addResourceType(self.ResourceTypes.AbstractMachineStack, "abstract_machine")
Resources.addType(self.ResourceTypes.QmlFiles, "qml") Resources.addType(self.ResourceTypes.QmlFiles, "qml")
Resources.addType(self.ResourceTypes.Firmware, "firmware") Resources.addType(self.ResourceTypes.Firmware, "firmware")
@ -486,7 +482,6 @@ class CuraApplication(QtApplication):
("variant", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.VariantInstanceContainer, "application/x-uranium-instancecontainer"), ("variant", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.VariantInstanceContainer, "application/x-uranium-instancecontainer"),
("setting_visibility", SettingVisibilityPresetsModel.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.SettingVisibilityPreset, "application/x-uranium-preferences"), ("setting_visibility", SettingVisibilityPresetsModel.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.SettingVisibilityPreset, "application/x-uranium-preferences"),
("machine", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer"), ("machine", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer"),
("abstract_machine", 1): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer"),
("extruder", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer") ("extruder", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer")
} }
) )

View file

@ -8,9 +8,8 @@ from UM.Settings.ContainerStack import ContainerStack
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Util import parseBool from UM.Util import parseBool
from cura.Settings.AbstractMachine import AbstractMachine
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack, getMachinesWithDefinition
class MachineListModel(ListModel): class MachineListModel(ListModel):
@ -19,8 +18,8 @@ class MachineListModel(ListModel):
HasRemoteConnectionRole = Qt.ItemDataRole.UserRole + 3 HasRemoteConnectionRole = Qt.ItemDataRole.UserRole + 3
MetaDataRole = Qt.ItemDataRole.UserRole + 4 MetaDataRole = Qt.ItemDataRole.UserRole + 4
IsOnlineRole = Qt.ItemDataRole.UserRole + 5 IsOnlineRole = Qt.ItemDataRole.UserRole + 5
MachineTypeRole = Qt.ItemDataRole.UserRole + 6 MachineCountRole = Qt.ItemDataRole.UserRole + 6
MachineCountRole = Qt.ItemDataRole.UserRole + 7 IsAbstractMachine = Qt.ItemDataRole.UserRole + 7
def __init__(self, parent=None) -> None: def __init__(self, parent=None) -> None:
super().__init__(parent) super().__init__(parent)
@ -32,8 +31,8 @@ class MachineListModel(ListModel):
self.addRoleName(self.HasRemoteConnectionRole, "hasRemoteConnection") self.addRoleName(self.HasRemoteConnectionRole, "hasRemoteConnection")
self.addRoleName(self.MetaDataRole, "metadata") self.addRoleName(self.MetaDataRole, "metadata")
self.addRoleName(self.IsOnlineRole, "isOnline") self.addRoleName(self.IsOnlineRole, "isOnline")
self.addRoleName(self.MachineTypeRole, "machineType")
self.addRoleName(self.MachineCountRole, "machineCount") self.addRoleName(self.MachineCountRole, "machineCount")
self.addRoleName(self.IsAbstractMachine, "isAbstractMachine")
self._change_timer = QTimer() self._change_timer = QTimer()
self._change_timer.setInterval(200) self._change_timer.setInterval(200)
@ -61,14 +60,16 @@ class MachineListModel(ListModel):
other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine") other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine")
abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type = "abstract_machine") 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:
online_machine_stacks = AbstractMachine.getMachines(abstract_machine, online_only = True) definition_id = abstract_machine.definition.getId()
online_machine_stacks = getMachinesWithDefinition(definition_id, online_only = True)
# Create a list item for abstract machine # Create a list item for abstract machine
self.addItem(abstract_machine, len(online_machine_stacks)) self.addItem(abstract_machine, len(online_machine_stacks))
other_machine_stacks.remove(abstract_machine)
# Create list of machines that are children of the abstract machine # Create list of machines that are children of the abstract machine
for stack in online_machine_stacks: for stack in online_machine_stacks:
@ -87,6 +88,6 @@ class MachineListModel(ListModel):
"id": container_stack.getId(), "id": container_stack.getId(),
"metadata": container_stack.getMetaData().copy(), "metadata": container_stack.getMetaData().copy(),
"isOnline": parseBool(container_stack.getMetaDataEntry("is_online", False)), "isOnline": parseBool(container_stack.getMetaDataEntry("is_online", False)),
"machineType": container_stack.getMetaDataEntry("type"), "isAbstractMachine": parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)),
"machineCount": machine_count, "machineCount": machine_count,
}) })

View file

@ -1,52 +0,0 @@
from typing import List
from UM.Settings.ContainerStack import ContainerStack
from UM.Util import parseBool
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from cura.Settings.GlobalStack import GlobalStack
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from UM.Settings.ContainerRegistry import ContainerRegistry
class AbstractMachine(GlobalStack):
""" Represents a group of machines of the same type. This allows the user to select settings before selecting a printer. """
def __init__(self, container_id: str) -> None:
super().__init__(container_id)
self.setMetaDataEntry("type", "abstract_machine")
@classmethod
def getMachines(cls, abstract_machine: ContainerStack, online_only = False) -> List[ContainerStack]:
""" Fetches all container stacks that match definition_id with an abstract machine.
:param abstractMachine: The abstract machine stack.
:return: A list of Containers or an empty list if abstract_machine is not an "abstract_machine"
"""
if not abstract_machine.getMetaDataEntry("type") == "abstract_machine":
return []
from cura.CuraApplication import CuraApplication # In function to avoid circular import
application = CuraApplication.getInstance()
registry = application.getContainerRegistry()
machines = registry.findContainerStacks(type="machine")
# Filter machines that match definition
machines = filter(lambda machine: machine.definition.id == abstract_machine.definition.getId(), machines)
# Filter only LAN and Cloud printers
machines = filter(lambda machine: ConnectionType.CloudConnection in machine.configuredConnectionTypes or ConnectionType.NetworkConnection in machine.configuredConnectionTypes, machines)
if online_only:
# LAN printers have is_online = False but should still be included
machines = filter(lambda machine: parseBool(machine.getMetaDataEntry("is_online", False) or ConnectionType.NetworkConnection in machine.configuredConnectionTypes), machines)
return list(machines)
## private:
_abstract_machine_mime = MimeType(
name = "application/x-cura-abstract-machine",
comment = "Cura Abstract Machine",
suffixes = ["global.cfg"]
)
MimeTypeDatabase.addMimeType(_abstract_machine_mime)
ContainerRegistry.addContainerTypeByName(AbstractMachine, "abstract_machine", _abstract_machine_mime.name)

View file

@ -9,7 +9,6 @@ from UM.Settings.Interfaces import DefinitionContainerInterface
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
from cura.Machines.ContainerTree import ContainerTree from cura.Machines.ContainerTree import ContainerTree
from .AbstractMachine import AbstractMachine
from .GlobalStack import GlobalStack from .GlobalStack import GlobalStack
from .ExtruderStack import ExtruderStack from .ExtruderStack import ExtruderStack
@ -268,21 +267,21 @@ class CuraStackBuilder:
return definition_changes_container return definition_changes_container
@classmethod @classmethod
def createAbstractMachine(cls, definition_id: str) -> Optional[AbstractMachine]: def createAbstractMachine(cls, definition_id: str) -> Optional[GlobalStack]:
"""Create a new instance of an abstract machine. """Create a new instance of an abstract machine.
:param definition_id: The ID of the machine definition to use. :param definition_id: The ID of the machine definition to use.
:return: The new Abstract Machine or None if an error occurred. :return: The new Abstract Machine or None if an error occurred.
""" """
abstract_machine_id = definition_id + "_abstract_machine" abstract_machine_id = f"{definition_id}_abstract_machine"
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
application = CuraApplication.getInstance() application = CuraApplication.getInstance()
registry = application.getContainerRegistry() registry = application.getContainerRegistry()
container_tree = ContainerTree.getInstance() container_tree = ContainerTree.getInstance()
if registry.findContainerStacks(type = "abstract_machine", id = abstract_machine_id): if registry.findContainerStacks(is_abstract_machine = "True", id = abstract_machine_id):
# This abstract machine already exists # This abstract machine already exists
return None return None
@ -296,7 +295,8 @@ class CuraStackBuilder:
machine_node = container_tree.machines[machine_definition.getId()] machine_node = container_tree.machines[machine_definition.getId()]
name = machine_definition.getName() name = machine_definition.getName()
stack = AbstractMachine(abstract_machine_id) stack = GlobalStack(abstract_machine_id)
stack.setMetaDataEntry("is_abstract_machine", True)
stack.setMetaDataEntry("is_online", True) stack.setMetaDataEntry("is_online", True)
stack.setDefinition(machine_definition) stack.setDefinition(machine_definition)
cls.createUserContainer( cls.createUserContainer(

View file

@ -1,4 +1,4 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from collections import defaultdict from collections import defaultdict
@ -8,10 +8,9 @@ import uuid
from PyQt6.QtCore import pyqtProperty, pyqtSlot, pyqtSignal from PyQt6.QtCore import pyqtProperty, pyqtSlot, pyqtSignal
from UM.Decorators import deprecated, override from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from UM.Settings.ContainerStack import ContainerStack from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.SettingInstance import InstanceState
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import PropertyEvaluationContext from UM.Settings.Interfaces import PropertyEvaluationContext
from UM.Logger import Logger from UM.Logger import Logger
@ -344,13 +343,36 @@ class GlobalStack(CuraContainerStack):
def getName(self) -> str: def getName(self) -> str:
return self._metadata.get("group_name", self._metadata.get("name", "")) return self._metadata.get("group_name", self._metadata.get("name", ""))
def setName(self, name: "str") -> None: def setName(self, name: str) -> None:
super().setName(name) super().setName(name)
nameChanged = pyqtSignal() nameChanged = pyqtSignal()
name = pyqtProperty(str, fget=getName, fset=setName, notify=nameChanged) name = pyqtProperty(str, fget=getName, fset=setName, notify=nameChanged)
def getMachinesWithDefinition(definition_id: str, online_only = False) -> List[ContainerStack]:
""" Fetches all container stacks that match definition_id.
:param definition_id: The id of the machine definition.
:return: A list of Containers that match definition_id
"""
from cura.CuraApplication import CuraApplication # In function to avoid circular import
application = CuraApplication.getInstance()
registry = application.getContainerRegistry()
machines = registry.findContainerStacks(type="machine")
# Filter machines that match definition
machines = filter(lambda machine: machine.definition.id == definition_id, machines)
# Filter only LAN and Cloud printers
machines = filter(lambda machine: ConnectionType.CloudConnection in machine.configuredConnectionTypes or
ConnectionType.NetworkConnection in machine.configuredConnectionTypes, machines)
if online_only:
# LAN printers can have is_online = False but should still be included, their online status is only checked when
# they are the active printer.
machines = filter(lambda machine: parseBool(machine.getMetaDataEntry("is_online", False) or
ConnectionType.NetworkConnection in machine.configuredConnectionTypes), machines)
return list(machines)
## private: ## private:
global_stack_mime = MimeType( global_stack_mime = MimeType(

View file

@ -30,8 +30,8 @@ Button
height: UM.Theme.getSize("medium_button").height height: UM.Theme.getSize("medium_button").height
width: UM.Theme.getSize("medium_button").width width: UM.Theme.getSize("medium_button").width
color: UM.Theme.getColor("machine_selector_printer_icon") color: UM.Theme.getColor("machine_selector_printer_icon")
visible: model.machineType == "abstract_machine" || !model.isOnline visible: model.isAbstractMachine || !model.isOnline
source: model.machineType == "abstract_machine" ? UM.Theme.getIcon("PrinterTriple", "medium") : UM.Theme.getIcon("Printer", "medium") source: model.isAbstractMachine ? UM.Theme.getIcon("PrinterTriple", "medium") : UM.Theme.getIcon("Printer", "medium")
anchors anchors
{ {
@ -51,7 +51,7 @@ Button
leftMargin: UM.Theme.getSize("default_margin").width leftMargin: UM.Theme.getSize("default_margin").width
} }
text: machineListButton.text text: machineListButton.text
font: model.machineType == "abstract_machine" ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium") font: model.isAbstractMachine ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")
visible: text != "" visible: text != ""
elide: Text.ElideRight elide: Text.ElideRight
} }
@ -68,7 +68,7 @@ Button
top: buttonText.top top: buttonText.top
bottom: buttonText.bottom bottom: buttonText.bottom
} }
visible: model.machineType == "abstract_machine" visible: model.isAbstractMachine
UM.Label UM.Label
{ {