Merge branch 'feature_intent_container_tree' of github.com:Ultimaker/Cura into feature_intent_interface

This commit is contained in:
Jaime van Kessel 2019-08-29 16:28:26 +02:00
commit b00b8c8c58
27 changed files with 603 additions and 239 deletions

View file

@ -85,6 +85,7 @@ from cura.Machines.Models.FirstStartMachineActionsModel import FirstStartMachine
from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel
from cura.Machines.Models.GlobalStacksModel import GlobalStacksModel from cura.Machines.Models.GlobalStacksModel import GlobalStacksModel
from cura.Machines.Models.MaterialBrandsModel import MaterialBrandsModel from cura.Machines.Models.MaterialBrandsModel import MaterialBrandsModel
from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel
from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel
from cura.Machines.Models.NozzleModel import NozzleModel from cura.Machines.Models.NozzleModel import NozzleModel
from cura.Machines.Models.QualityManagementModel import QualityManagementModel from cura.Machines.Models.QualityManagementModel import QualityManagementModel
@ -220,7 +221,9 @@ class CuraApplication(QtApplication):
self._cura_scene_controller = None self._cura_scene_controller = None
self._machine_error_checker = None self._machine_error_checker = None
self._machine_settings_manager = MachineSettingsManager(self, parent = self) self._machine_settings_manager = MachineSettingsManager(self, parent=self)
self._material_management_model = MaterialManagementModel()
self._quality_management_model = None
self._discovered_printer_model = DiscoveredPrintersModel(self, parent = self) self._discovered_printer_model = DiscoveredPrintersModel(self, parent = self)
self._first_start_machine_actions_model = FirstStartMachineActionsModel(self, parent = self) self._first_start_machine_actions_model = FirstStartMachineActionsModel(self, parent = self)
@ -918,12 +921,12 @@ class CuraApplication(QtApplication):
# Can't deprecate this function since the deprecation marker collides with pyqtSlot! # Can't deprecate this function since the deprecation marker collides with pyqtSlot!
@pyqtSlot(result = QObject) @pyqtSlot(result = QObject)
def getMaterialManager(self, *args) -> "MaterialManager": def getMaterialManager(self, *args) -> cura.Machines.MaterialManager.MaterialManager:
return cura.Machines.MaterialManager.MaterialManager.getInstance() return cura.Machines.MaterialManager.MaterialManager.getInstance()
# Can't deprecate this function since the deprecation marker collides with pyqtSlot! # Can't deprecate this function since the deprecation marker collides with pyqtSlot!
@pyqtSlot(result = QObject) @pyqtSlot(result = QObject)
def getQualityManager(self, *args) -> "QualityManager": def getQualityManager(self, *args) -> cura.Machines.QualityManager.QualityManager:
return cura.Machines.QualityManager.QualityManager.getInstance() return cura.Machines.QualityManager.QualityManager.getInstance()
def getIntentManager(self, *args) -> IntentManager: def getIntentManager(self, *args) -> IntentManager:
@ -975,6 +978,16 @@ class CuraApplication(QtApplication):
def getMachineActionManager(self, *args): def getMachineActionManager(self, *args):
return self._machine_action_manager return self._machine_action_manager
@pyqtSlot(result = QObject)
def getMaterialManagementModel(self):
return self._material_management_model
@pyqtSlot(result=QObject)
def getQualityManagementModel(self):
if not self._quality_management_model:
self._quality_management_model = QualityManagementModel()
return self._quality_management_model
def getSimpleModeSettingsManager(self, *args): def getSimpleModeSettingsManager(self, *args):
if self._simple_mode_settings_manager is None: if self._simple_mode_settings_manager is None:
self._simple_mode_settings_manager = SimpleModeSettingsManager() self._simple_mode_settings_manager = SimpleModeSettingsManager()
@ -1053,7 +1066,8 @@ class CuraApplication(QtApplication):
qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel") qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel")
qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel") qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel")
qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel") qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel")
qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel") qmlRegisterSingletonType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel", self.getQualityManagementModel)
qmlRegisterSingletonType(MaterialManagementModel, "Cura", 1, 5, "MaterialManagementModel", self.getMaterialManagementModel)
qmlRegisterType(DiscoveredPrintersModel, "Cura", 1, 0, "DiscoveredPrintersModel") qmlRegisterType(DiscoveredPrintersModel, "Cura", 1, 0, "DiscoveredPrintersModel")

View file

@ -4,6 +4,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.Machines.ContainerNode import ContainerNode from cura.Machines.ContainerNode import ContainerNode
if TYPE_CHECKING: if TYPE_CHECKING:
@ -16,5 +17,4 @@ class IntentNode(ContainerNode):
def __init__(self, container_id: str, quality: "QualityNode") -> None: def __init__(self, container_id: str, quality: "QualityNode") -> None:
super().__init__(container_id) super().__init__(container_id)
self.quality = quality self.quality = quality
my_metadata = ContainerRegistry.getInstance().findContainersMetadata(id=container_id)[0] self.intent_category = ContainerRegistry.getInstance().findContainersMetadata(id = container_id)[0].get("intent_category", "default")
self.intent_category = my_metadata.get("intent_category", "default")

View file

@ -36,7 +36,7 @@ class MachineNode(ContainerNode):
self.has_variants = parseBool(my_metadata.get("has_variants", "false")) self.has_variants = parseBool(my_metadata.get("has_variants", "false"))
self.has_machine_materials = parseBool(my_metadata.get("has_machine_materials", "false")) self.has_machine_materials = parseBool(my_metadata.get("has_machine_materials", "false"))
self.has_machine_quality = parseBool(my_metadata.get("has_machine_quality", "false")) self.has_machine_quality = parseBool(my_metadata.get("has_machine_quality", "false"))
self.quality_definition = my_metadata.get("quality_definition", container_id) self.quality_definition = my_metadata.get("quality_definition", container_id) if self.has_machine_quality else "fdmprinter"
self.exclude_materials = my_metadata.get("exclude_materials", []) self.exclude_materials = my_metadata.get("exclude_materials", [])
self.preferred_variant_name = my_metadata.get("preferred_variant_name", "") self.preferred_variant_name = my_metadata.get("preferred_variant_name", "")
self.preferred_material = my_metadata.get("preferred_material", "") self.preferred_material = my_metadata.get("preferred_material", "")
@ -62,7 +62,7 @@ class MachineNode(ContainerNode):
Logger.log("e", "The number of extruders in the list of variants (" + str(len(variant_names)) + ") is not equal to the number of extruders in the list of materials (" + str(len(material_bases)) + ") or the list of enabled extruders (" + str(len(extruder_enabled)) + ").") Logger.log("e", "The number of extruders in the list of variants (" + str(len(variant_names)) + ") is not equal to the number of extruders in the list of materials (" + str(len(material_bases)) + ") or the list of enabled extruders (" + str(len(extruder_enabled)) + ").")
return {} return {}
# For each extruder, find which quality profiles are available. Later we'll intersect the quality types. # For each extruder, find which quality profiles are available. Later we'll intersect the quality types.
qualities_per_type_per_extruder = [{} for _ in range(len(variant_names))] # type: List[Dict[str, QualityNode]] qualities_per_type_per_extruder = [{}] * len(variant_names) # type: List[Dict[str, QualityNode]]
for extruder_nr, variant_name in enumerate(variant_names): for extruder_nr, variant_name in enumerate(variant_names):
if not extruder_enabled[extruder_nr]: if not extruder_enabled[extruder_nr]:
continue # No qualities are available in this extruder. It'll get skipped when calculating the available quality types. continue # No qualities are available in this extruder. It'll get skipped when calculating the available quality types.
@ -77,6 +77,9 @@ class MachineNode(ContainerNode):
# Create the quality group for each available type. # Create the quality group for each available type.
quality_groups = {} quality_groups = {}
for quality_type, global_quality_node in self.global_qualities.items(): for quality_type, global_quality_node in self.global_qualities.items():
if not global_quality_node.container:
Logger.log("w", "Node {0} doesn't have a container.".format(global_quality_node.container_id))
continue
quality_groups[quality_type] = QualityGroup(name = global_quality_node.container.getMetaDataEntry("name", "Unnamed profile"), quality_type = quality_type) quality_groups[quality_type] = QualityGroup(name = global_quality_node.container.getMetaDataEntry("name", "Unnamed profile"), quality_type = quality_type)
quality_groups[quality_type].node_for_global = global_quality_node quality_groups[quality_type].node_for_global = global_quality_node
for extruder, qualities_per_type in enumerate(qualities_per_type_per_extruder): for extruder, qualities_per_type in enumerate(qualities_per_type_per_extruder):
@ -116,7 +119,7 @@ class MachineNode(ContainerNode):
def getQualityChangesGroups(self, variant_names: List[str], material_bases: List[str], extruder_enabled: List[bool]) -> List[QualityChangesGroup]: def getQualityChangesGroups(self, variant_names: List[str], material_bases: List[str], extruder_enabled: List[bool]) -> List[QualityChangesGroup]:
machine_quality_changes = ContainerRegistry.getInstance().findContainersMetadata(type = "quality_changes", definition = self.quality_definition) # All quality changes for each extruder. machine_quality_changes = ContainerRegistry.getInstance().findContainersMetadata(type = "quality_changes", definition = self.quality_definition) # All quality changes for each extruder.
groups_by_name = {} # Group quality changes profiles by their display name. The display name must be unique for quality changes. This finds profiles that belong together in a group. groups_by_name = {} #type: Dict[str, QualityChangesGroup] # Group quality changes profiles by their display name. The display name must be unique for quality changes. This finds profiles that belong together in a group.
for quality_changes in machine_quality_changes: for quality_changes in machine_quality_changes:
name = quality_changes["name"] name = quality_changes["name"]
if name not in groups_by_name: if name not in groups_by_name:
@ -143,7 +146,7 @@ class MachineNode(ContainerNode):
# quality is taken. # quality is taken.
# If there are no global qualities, an empty quality is returned. # If there are no global qualities, an empty quality is returned.
def preferredGlobalQuality(self) -> QualityNode: def preferredGlobalQuality(self) -> QualityNode:
return self.global_qualities.get(self.preferred_quality_type, next(iter(self.global_qualities))) return self.global_qualities.get(self.preferred_quality_type, next(iter(self.global_qualities.values())))
## (Re)loads all variants under this printer. ## (Re)loads all variants under this printer.
def _loadAll(self): def _loadAll(self):

View file

@ -1,5 +1,6 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 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
import copy import copy
import uuid import uuid
@ -7,19 +8,16 @@ from typing import Dict, Optional, TYPE_CHECKING, Any, List, cast
from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Decorators import deprecated from UM.Decorators import deprecated
from UM.Logger import Logger from UM.Logger import Logger
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.SettingFunction import SettingFunction
from UM.Util import parseBool from UM.Util import parseBool
import cura.CuraApplication #Imported like this to prevent circular imports. import cura.CuraApplication # Imported like this to prevent circular imports.
from cura.Machines.ContainerTree import ContainerTree from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
from .MaterialNode import MaterialNode from .MaterialNode import MaterialNode
from .MaterialGroup import MaterialGroup from .MaterialGroup import MaterialGroup
from .VariantType import VariantType
if TYPE_CHECKING: if TYPE_CHECKING:
from UM.Settings.DefinitionContainer import DefinitionContainer from UM.Settings.DefinitionContainer import DefinitionContainer
@ -134,7 +132,7 @@ class MaterialManager(QObject):
# Fetch the available materials (ContainerNode) for the current active machine and extruder setup. # Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
materials = self.getAvailableMaterials(machine.definition.getId(), nozzle_name) materials = self.getAvailableMaterials(machine.definition.getId(), nozzle_name)
compatible_material_diameter = str(round(extruder_stack.getCompatibleMaterialDiameter())) compatible_material_diameter = str(round(extruder_stack.getCompatibleMaterialDiameter()))
result = {key: material for key, material in materials.items() if material.container.getMetaDataEntry("approximate_diameter") == compatible_material_diameter} result = {key: material for key, material in materials.items() if material.container and material.container.getMetaDataEntry("approximate_diameter") == compatible_material_diameter}
return result return result
# #
@ -248,7 +246,7 @@ class MaterialManager(QObject):
def removeMaterialByRootId(self, root_material_id: str): def removeMaterialByRootId(self, root_material_id: str):
container_registry = CuraContainerRegistry.getInstance() container_registry = CuraContainerRegistry.getInstance()
results = container_registry.findContainers(id=root_material_id) results = container_registry.findContainers(id = root_material_id)
if not results: if not results:
container_registry.addWrongContainerId(root_material_id) container_registry.addWrongContainerId(root_material_id)
@ -260,8 +258,8 @@ class MaterialManager(QObject):
# Check if the material is active in any extruder train. In that case, the material shouldn't be removed! # Check if the material is active in any extruder train. In that case, the material shouldn't be removed!
# In the future we might enable this again, but right now, it's causing a ton of issues if we do (since it # In the future we might enable this again, but right now, it's causing a ton of issues if we do (since it
# corrupts the configuration) # corrupts the configuration)
root_material_id = material_node.container.getMetaDataEntry("base_file") root_material_id = material_node.base_file
ids_to_remove = [metadata.get("id", "") for metadata in CuraContainerRegistry.getInstance().findInstanceContainersMetadata(base_file=root_material_id)] ids_to_remove = {metadata.get("id", "") for metadata in CuraContainerRegistry.getInstance().findInstanceContainersMetadata(base_file = root_material_id)}
for extruder_stack in CuraContainerRegistry.getInstance().findContainerStacks(type = "extruder_train"): for extruder_stack in CuraContainerRegistry.getInstance().findContainerStacks(type = "extruder_train"):
if extruder_stack.material.getId() in ids_to_remove: if extruder_stack.material.getId() in ids_to_remove:
@ -270,6 +268,8 @@ class MaterialManager(QObject):
@pyqtSlot("QVariant", str) @pyqtSlot("QVariant", str)
def setMaterialName(self, material_node: "MaterialNode", name: str) -> None: def setMaterialName(self, material_node: "MaterialNode", name: str) -> None:
if material_node.container is None:
return
root_material_id = material_node.container.getMetaDataEntry("base_file") root_material_id = material_node.container.getMetaDataEntry("base_file")
if root_material_id is None: if root_material_id is None:
return return
@ -281,6 +281,8 @@ class MaterialManager(QObject):
@pyqtSlot("QVariant") @pyqtSlot("QVariant")
def removeMaterial(self, material_node: "MaterialNode") -> None: def removeMaterial(self, material_node: "MaterialNode") -> None:
if material_node.container is None:
return
root_material_id = material_node.container.getMetaDataEntry("base_file") root_material_id = material_node.container.getMetaDataEntry("base_file")
if root_material_id is not None: if root_material_id is not None:
self.removeMaterialByRootId(root_material_id) self.removeMaterialByRootId(root_material_id)
@ -300,7 +302,6 @@ class MaterialManager(QObject):
# Create a new ID & container to hold the data. # Create a new ID & container to hold the data.
new_containers = [] new_containers = []
container_registry = CuraContainerRegistry.getInstance()
if new_base_id is None: if new_base_id is None:
new_base_id = container_registry.uniqueName(base_container.getId()) new_base_id = container_registry.uniqueName(base_container.getId())
new_base_container = copy.deepcopy(base_container) new_base_container = copy.deepcopy(base_container)
@ -336,15 +337,19 @@ class MaterialManager(QObject):
# if the duplicated material was favorite then the new material should also be added to favorite. # if the duplicated material was favorite then the new material should also be added to favorite.
if root_material_id in self.getFavorites(): if root_material_id in self.getFavorites():
self.addFavorite(new_base_id) cura.CuraApplication.CuraApplication.getInstance().getMaterialManagementModel().addFavorite(new_base_id)
return new_base_id return new_base_id
# #
# Creates a duplicate of a material, which has the same GUID and base_file metadata. # Creates a duplicate of a material, which has the same GUID and base_file metadata.
# Returns the root material ID of the duplicated material if successful. # Returns the root material ID of the duplicated material if successful.
# #
@pyqtSlot("QVariant", result = str) @pyqtSlot("QVariant", result = str)
def duplicateMaterial(self, material_node: MaterialNode, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]: def duplicateMaterial(self, material_node: MaterialNode, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
if material_node.container is None:
Logger.log("e", "Material node {0} doesn't have container.".format(material_node.container_id))
return "ERROR"
root_material_id = cast(str, material_node.container.getMetaDataEntry("base_file", "")) root_material_id = cast(str, material_node.container.getMetaDataEntry("base_file", ""))
return self.duplicateMaterialByRootId(root_material_id, new_base_id, new_metadata) return self.duplicateMaterialByRootId(root_material_id, new_base_id, new_metadata)
@ -361,7 +366,11 @@ class MaterialManager(QObject):
machine_manager = application.getMachineManager() machine_manager = application.getMachineManager()
extruder_stack = machine_manager.activeStack extruder_stack = machine_manager.activeStack
machine_definition = application.getGlobalContainerStack().definition global_stack = application.getGlobalContainerStack()
if global_stack is None:
Logger.log("e", "Global stack not present!")
return "ERROR"
machine_definition = global_stack.definition
root_material_id = machine_definition.getMetaDataEntry("preferred_material", default = "generic_pla") root_material_id = machine_definition.getMetaDataEntry("preferred_material", default = "generic_pla")
approximate_diameter = str(extruder_stack.approximateMaterialDiameter) approximate_diameter = str(extruder_stack.approximateMaterialDiameter)

View file

@ -62,15 +62,16 @@ class MaterialNode(ContainerNode):
qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter") qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter")
else: else:
# Need to find the qualities that specify a material profile with the same material type. # Need to find the qualities that specify a material profile with the same material type.
my_material_type = self.material_type qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition, variant = self.variant.variant_name, material = self.container_id) # First try by exact material ID.
qualities = [] if not qualities:
qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition, variant = self.variant.variant_name) my_material_type = self.material_type
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type): qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition, variant = self.variant.variant_name)
qualities.extend((quality for quality in qualities_any_material if quality["material"] == material_metadata["id"])) for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type):
if not qualities: # No quality profiles found. Go by GUID then.
my_guid = self.guid
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", guid = my_guid):
qualities.extend((quality for quality in qualities_any_material if quality["material"] == material_metadata["id"])) qualities.extend((quality for quality in qualities_any_material if quality["material"] == material_metadata["id"]))
if not qualities: # No quality profiles found. Go by GUID then.
my_guid = self.guid
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", guid = my_guid):
qualities.extend((quality for quality in qualities_any_material if quality["material"] == material_metadata["id"]))
for quality in qualities: for quality in qualities:
quality_id = quality["id"] quality_id = quality["id"]

View file

@ -31,8 +31,13 @@ class BaseMaterialsModel(ListModel):
self._container_registry = self._application.getInstance().getContainerRegistry() self._container_registry = self._application.getInstance().getContainerRegistry()
self._machine_manager = self._application.getMachineManager() self._machine_manager = self._application.getMachineManager()
self._extruder_position = 0
self._extruder_stack = None
self._enabled = True
# Update the stack and the model data when the machine changes # Update the stack and the model data when the machine changes
self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack) self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack)
self._updateExtruderStack()
# Update this model when switching machines, when adding materials or changing their metadata. # Update this model when switching machines, when adding materials or changing their metadata.
self._machine_manager.activeStackChanged.connect(self._update) self._machine_manager.activeStackChanged.connect(self._update)
@ -55,12 +60,8 @@ class BaseMaterialsModel(ListModel):
self.addRoleName(Qt.UserRole + 15, "container_node") self.addRoleName(Qt.UserRole + 15, "container_node")
self.addRoleName(Qt.UserRole + 16, "is_favorite") self.addRoleName(Qt.UserRole + 16, "is_favorite")
self._extruder_position = 0
self._extruder_stack = None
self._available_materials = None # type: Optional[Dict[str, MaterialNode]] self._available_materials = None # type: Optional[Dict[str, MaterialNode]]
self._favorite_ids = set() # type: Set[str] self._favorite_ids = set() # type: Set[str]
self._enabled = True
def _updateExtruderStack(self): def _updateExtruderStack(self):
global_stack = self._machine_manager.activeMachine global_stack = self._machine_manager.activeMachine
@ -95,7 +96,7 @@ class BaseMaterialsModel(ListModel):
self._update() self._update()
self.enabledChanged.emit() self.enabledChanged.emit()
@pyqtProperty(bool, fset=setEnabled, notify=enabledChanged) @pyqtProperty(bool, fset = setEnabled, notify = enabledChanged)
def enabled(self): def enabled(self):
return self._enabled return self._enabled
@ -107,7 +108,10 @@ class BaseMaterialsModel(ListModel):
return return
if material.variant.container_id != self._extruder_stack.variant.getId(): if material.variant.container_id != self._extruder_stack.variant.getId():
return return
if material.variant.machine.container_id != cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack().definition.getId(): global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack:
return
if material.variant.machine.container_id != global_stack.definition.getId():
return return
self._update() self._update()
@ -167,4 +171,3 @@ class BaseMaterialsModel(ListModel):
"is_favorite": root_material_id in self._favorite_ids "is_favorite": root_material_id in self._favorite_ids
} }
return item return item

View file

@ -19,11 +19,7 @@ class CustomQualityProfilesDropDownMenuModel(QualityProfilesDropDownMenuModel):
Logger.log("d", "No active GlobalStack, set %s as empty.", self.__class__.__name__) Logger.log("d", "No active GlobalStack, set %s as empty.", self.__class__.__name__)
return return
variant_names = [extruder.variant.getName() for extruder in active_global_stack.extruders.values()] quality_changes_list = ContainerTree.getInstance().getCurrentQualityChangesGroups()
material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in active_global_stack.extruders.values()]
extruder_enabled = [extruder.isEnabled for extruder in active_global_stack.extruders.values()]
machine_node = ContainerTree.getInstance().machines[active_global_stack.definition.getId()]
quality_changes_list = machine_node.getQualityChangesGroups(variant_names, material_bases, extruder_enabled)
item_list = [] item_list = []
for quality_changes_group in sorted(quality_changes_list, key = lambda qgc: qgc.name.lower()): for quality_changes_group in sorted(quality_changes_list, key = lambda qgc: qgc.name.lower()):

View file

@ -33,6 +33,8 @@ class IntentModel(ListModel):
self._intent_category = "engineering" self._intent_category = "engineering"
machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
machine_manager.globalContainerChanged.connect(self._update)
ContainerRegistry.getInstance().containerAdded.connect(self._onChanged) ContainerRegistry.getInstance().containerAdded.connect(self._onChanged)
ContainerRegistry.getInstance().containerRemoved.connect(self._onChanged) ContainerRegistry.getInstance().containerRemoved.connect(self._onChanged)
self._layer_height_unit = "" # This is cached self._layer_height_unit = "" # This is cached

View file

@ -0,0 +1,169 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import copy # To duplicate materials.
from PyQt5.QtCore import QObject, pyqtSlot # To allow the preference page proxy to be used from the actual preferences page.
from typing import Any, Dict, Optional, TYPE_CHECKING
import uuid # To generate new GUIDs for new materials.
from UM.i18n import i18nCatalog
from UM.Logger import Logger
import cura.CuraApplication # Imported like this to prevent circular imports.
from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To find the sets of materials belonging to each other, and currently loaded extruder stacks.
if TYPE_CHECKING:
from cura.Machines.MaterialNode import MaterialNode
catalog = i18nCatalog("cura")
## Proxy class to the materials page in the preferences.
#
# This class handles the actions in that page, such as creating new materials,
# renaming them, etc.
class MaterialManagementModel(QObject):
## Can a certain material be deleted, or is it still in use in one of the
# container stacks anywhere?
#
# We forbid the user from deleting a material if it's in use in any stack.
# Deleting it while it's in use can lead to corrupted stacks. In the
# future we might enable this functionality again (deleting the material
# from those stacks) but for now it is easier to prevent the user from
# doing this.
# \param material_node The ContainerTree node of the material to check.
# \return Whether or not the material can be removed.
@pyqtSlot("QVariant", result = bool)
def canMaterialBeRemoved(self, material_node: "MaterialNode"):
container_registry = CuraContainerRegistry.getInstance()
ids_to_remove = {metadata.get("id", "") for metadata in container_registry.findInstanceContainersMetadata(base_file = material_node.base_file)}
for extruder_stack in container_registry.findContainerStacks(type = "extruder_train"):
if extruder_stack.material.getId() in ids_to_remove:
return False
return True
## Change the user-visible name of a material.
# \param material_node The ContainerTree node of the material to rename.
# \param name The new name for the material.
@pyqtSlot("QVariant", str)
def setMaterialName(self, material_node: "MaterialNode", name: str) -> None:
container_registry = CuraContainerRegistry.getInstance()
root_material_id = material_node.base_file
if container_registry.isReadOnly(root_material_id):
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
return
return container_registry.findContainers(id = root_material_id)[0].setName(name)
## Deletes a material from Cura.
#
# This function does not do any safety checking any more. Please call this
# function only if:
# - The material is not read-only.
# - The material is not used in any stacks.
# If the material was not lazy-loaded yet, this will fully load the
# container. When removing this material node, all other materials with
# the same base fill will also be removed.
# \param material_node The material to remove.
@pyqtSlot("QVariant")
def removeMaterial(self, material_node: "MaterialNode") -> None:
container_registry = CuraContainerRegistry.getInstance()
materials_this_base_file = container_registry.findContainersMetadata(base_file = material_node.base_file)
for material_metadata in materials_this_base_file:
container_registry.removeContainer(material_metadata["id"])
## Creates a duplicate of a material with the same GUID and base_file
# metadata.
# \param material_node The node representing the material to duplicate.
# \param new_base_id A new material ID for the base material. The IDs of
# the submaterials will be based off this one. If not provided, a material
# ID will be generated automatically.
# \param new_metadata Metadata for the new material. If not provided, this
# will be duplicated from the original material.
# \return The root material ID of the duplicate material.
@pyqtSlot("QVariant", result = str)
def duplicateMaterial(self, material_node: "MaterialNode", new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
container_registry = CuraContainerRegistry.getInstance()
root_materials = container_registry.findContainers(id = material_node.base_file)
if not root_materials:
Logger.log("i", "Unable to duplicate the root material with ID {root_id}, because it doesn't exist.".format(root_id = material_node.base_file))
return None
root_material = root_materials[0]
# Ensure that all settings are saved.
application = cura.CuraApplication.CuraApplication.getInstance()
application.saveSettings()
# Create a new ID and container to hold the data.
if new_base_id is None:
new_base_id = container_registry.uniqueName(root_material.getId())
new_root_material = copy.deepcopy(root_material)
new_root_material.getMetaData()["id"] = new_base_id
new_root_material.getMetaData()["base_file"] = new_base_id
if new_metadata is not None:
new_root_material.getMetaData().update(new_metadata)
new_containers = [new_root_material]
# Clone all submaterials.
for container_to_copy in container_registry.findInstanceContainers(base_file = material_node.base_file):
if container_to_copy.getId() == material_node.base_file:
continue # We already have that one. Skip it.
new_id = new_base_id
definition = container_to_copy.getMetaDataEntry("definition")
if definition != "fdmprinter":
new_id += "_" + definition
variant_name = container_to_copy.getMetaDataEntry("variant_name")
if variant_name:
new_id += "_" + variant_name.replace(" ", "_")
new_container = copy.deepcopy(container_to_copy)
new_container.getMetaData()["id"] = new_id
new_container.getMetaData()["base_file"] = new_base_id
if new_metadata is not None:
new_container.getMetaData().update(new_metadata)
new_containers.append(new_container)
for container_to_add in new_containers:
container_to_add.setDirty(True)
container_registry.addContainer(container_to_add)
# If the duplicated material was favorite then the new material should also be added to the favorites.
favorites_set = set(application.getPreferences().getValue("cura/favorite_materials").split(";"))
if material_node.base_file in favorites_set:
favorites_set.add(new_base_id)
application.getPreferences().setValue("cura/favorite_materials", ";".join(favorites_set))
return new_base_id
## Create a new material by cloning the preferred material for the current
# material diameter and generate a new GUID.
#
# The material type is explicitly left to be the one from the preferred
# material, since this allows the user to still have SOME profiles to work
# with.
# \return The ID of the newly created material.
@pyqtSlot(result = str)
def createMaterial(self) -> str:
# Ensure all settings are saved.
application = cura.CuraApplication.CuraApplication.getInstance()
application.saveSettings()
# Find the preferred material.
extruder_stack = application.getMachineManager().activeStack
active_variant_name = extruder_stack.variant.getName()
approximate_diameter = int(extruder_stack.approximateMaterialDiameter)
global_container_stack = application.getGlobalContainerStack()
if not global_container_stack:
return ""
machine_node = ContainerTree.getInstance().machines[global_container_stack.definition.getId()]
preferred_material_node = machine_node.variants[active_variant_name].preferredMaterial(approximate_diameter)
# Create a new ID & new metadata for the new material.
new_id = CuraContainerRegistry.getInstance().uniqueName("custom_material")
new_metadata = {"name": catalog.i18nc("@label", "Custom Material"),
"brand": catalog.i18nc("@label", "Custom"),
"GUID": str(uuid.uuid4()),
}
self.duplicateMaterial(preferred_material_node, new_base_id = new_id, new_metadata = new_metadata)
return new_id

View file

@ -1,11 +1,22 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 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 PyQt5.QtCore import Qt, pyqtSlot from typing import Any, Dict, Optional, TYPE_CHECKING
from PyQt5.QtCore import pyqtSlot, QObject, Qt
from UM.Logger import Logger from UM.Logger import Logger
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
from UM.Settings.InstanceContainer import InstanceContainer # To create new profiles.
import cura.CuraApplication # Imported this way to prevent circular imports.
from cura.Machines.ContainerTree import ContainerTree from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_quality_changes_container
if TYPE_CHECKING:
from UM.Settings.Interfaces import ContainerInterface
from cura.Machines.QualityChangesGroup import QualityChangesGroup
from cura.Settings.ExtruderStack import ExtruderStack
from cura.Settings.GlobalStack import GlobalStack
# #
# This the QML model for the quality management page. # This the QML model for the quality management page.
@ -24,17 +35,136 @@ class QualityManagementModel(ListModel):
self.addRoleName(self.QualityGroupRole, "quality_group") self.addRoleName(self.QualityGroupRole, "quality_group")
self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group") self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group")
from cura.CuraApplication import CuraApplication application = cura.CuraApplication.CuraApplication.getInstance()
self._container_registry = CuraApplication.getInstance().getContainerRegistry() container_registry = application.getContainerRegistry()
self._machine_manager = CuraApplication.getInstance().getMachineManager() self._machine_manager = application.getMachineManager()
self._extruder_manager = CuraApplication.getInstance().getExtruderManager() self._extruder_manager = application.getExtruderManager()
self._quality_manager = CuraApplication.getInstance().getQualityManager()
self._machine_manager.globalContainerChanged.connect(self._update) self._machine_manager.globalContainerChanged.connect(self._update)
self._quality_manager.qualitiesUpdated.connect(self._update) container_registry.containerAdded.connect(self._qualityChangesListChanged)
container_registry.containerRemoved.connect(self._qualityChangesListChanged)
container_registry.containerMetaDataChanged.connect(self._qualityChangesListChanged)
self._update() self._update()
## Deletes a custom profile. It will be gone forever.
# \param quality_changes_group The quality changes group representing the
# profile to delete.
@pyqtSlot(QObject)
def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
Logger.log("i", "Removing quality changes group {group_name}".format(group_name = quality_changes_group.name))
removed_quality_changes_ids = set()
container_registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry()
for metadata in [quality_changes_group.metadata_for_global] + list(quality_changes_group.metadata_per_extruder.values()):
container_id = metadata["id"]
container_registry.removeContainer(container_id)
removed_quality_changes_ids.add(container_id)
# Reset all machines that have activated this custom profile.
for global_stack in container_registry.findContainerStacks(type = "machine"):
if global_stack.qualityChanges.getId() in removed_quality_changes_ids:
global_stack.qualityChanges = empty_quality_changes_container
for extruder_stack in container_registry.findContainerStacks(type = "extruder_train"):
if extruder_stack.qualityChanges.getId() in removed_quality_changes_ids:
extruder_stack.qualityChanges = empty_quality_changes_container
## Rename a custom profile.
#
# Because the names must be unique, the new name may not actually become
# the name that was given. The actual name is returned by this function.
# \param quality_changes_group The custom profile that must be renamed.
# \param new_name The desired name for the profile.
# \return The actual new name of the profile, after making the name
# unique.
@pyqtSlot(QObject, str, result = str)
def renameQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", new_name: str) -> str:
Logger.log("i", "Renaming QualityChangesGroup {old_name} to {new_name}.".format(old_name = quality_changes_group.name, new_name = new_name))
if new_name == quality_changes_group.name:
Logger.log("i", "QualityChangesGroup name {name} unchanged.".format(name = quality_changes_group.name))
return new_name
application = cura.CuraApplication.CuraApplication.getInstance()
new_name = application.getContainerRegistry().uniqueName(new_name)
for node in quality_changes_group.getAllNodes():
container = node.container
if container:
container.setName(new_name)
quality_changes_group.name = new_name
application.getMachineManager().activeQualityChanged.emit()
application.getMachineManager().activeQualityGroupChanged.emit()
return new_name
## Duplicates a given quality profile OR quality changes profile.
# \param new_name The desired name of the new profile. This will be made
# unique, so it might end up with a different name.
# \param quality_model_item The item of this model to duplicate, as
# dictionary. See the descriptions of the roles of this list model.
@pyqtSlot(str, "QVariantMap")
def duplicateQualityChanges(self, new_name: str, quality_model_item: Dict[str, Any]) -> None:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack:
Logger.log("i", "No active global stack, cannot duplicate quality (changes) profile.")
return
container_registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry()
new_name = container_registry.uniqueName(new_name)
quality_group = quality_model_item["quality_group"]
quality_changes_group = quality_model_item["quality_changes_group"]
if quality_changes_group is None:
# Create global quality changes only.
new_quality_changes = self._createQualityChanges(quality_group.quality_type, new_name, global_stack, extruder_stack = None)
container_registry.addContainer(new_quality_changes)
else:
for metadata in [quality_changes_group.metadata_for_global] + quality_changes_group.metadata_per_extruder.values():
containers = container_registry.findContainers(id = metadata["id"])
if not containers:
continue
container = containers[0]
new_id = container_registry.uniqueName(container.getId())
container_registry.addContainer(container.duplicate(new_id, new_name))
## Create a quality changes container with the given set-up.
# \param quality_type The quality type of the new container.
# \param new_name The name of the container. This name must be unique.
# \param machine The global stack to create the profile for.
# \param extruder_stack The extruder stack to create the profile for. If
# not provided, only a global container will be created.
def _createQualityChanges(self, quality_type: str, new_name: str, machine: "GlobalStack", extruder_stack: Optional["ExtruderStack"]) -> "InstanceContainer":
container_registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry()
base_id = machine.definition.getId() if extruder_stack is None else extruder_stack.getId()
new_id = base_id + "_" + new_name
new_id = new_id.lower().replace(" ", "_")
new_id = container_registry.uniqueName(new_id)
# Create a new quality_changes container for the quality.
quality_changes = InstanceContainer(new_id)
quality_changes.setName(new_name)
quality_changes.setMetaDataEntry("type", "quality_changes")
quality_changes.setMetaDataEntry("quality_type", quality_type)
# If we are creating a container for an extruder, ensure we add that to the container.
if extruder_stack is not None:
quality_changes.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
# If the machine specifies qualities should be filtered, ensure we match the current criteria.
machine_definition_id = ContainerTree.getInstance().machines[machine.definition.getId()].quality_definition
quality_changes.setDefinition(machine_definition_id)
quality_changes.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.getInstance().SettingVersion)
return quality_changes
## Triggered when any container changed.
#
# This filters the updates to the container manager: When it applies to
# the list of quality changes, we need to update our list.
def _qualityChangesListChanged(self, container: "ContainerInterface") -> None:
if container.getMetaDataEntry("type") == "quality_changes":
self._update()
def _update(self): def _update(self):
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
@ -43,12 +173,13 @@ class QualityManagementModel(ListModel):
self.setItems([]) self.setItems([])
return return
quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups() container_tree = ContainerTree.getInstance()
quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(global_stack) quality_group_dict = container_tree.getCurrentQualityGroups()
quality_changes_group_list = container_tree.getCurrentQualityChangesGroups()
available_quality_types = set(quality_type for quality_type, quality_group in quality_group_dict.items() available_quality_types = set(quality_type for quality_type, quality_group in quality_group_dict.items()
if quality_group.is_available) if quality_group.is_available)
if not available_quality_types and not quality_changes_group_dict: if not available_quality_types and not quality_changes_group_list:
# Nothing to show # Nothing to show
self.setItems([]) self.setItems([])
return return
@ -69,7 +200,7 @@ class QualityManagementModel(ListModel):
# Create quality_changes group items # Create quality_changes group items
quality_changes_item_list = [] quality_changes_item_list = []
for quality_changes_group in quality_changes_group_dict.values(): for quality_changes_group in quality_changes_group_list:
quality_group = quality_group_dict.get(quality_changes_group.quality_type) quality_group = quality_group_dict.get(quality_changes_group.quality_type)
item = {"name": quality_changes_group.name, item = {"name": quality_changes_group.name,
"is_read_only": False, "is_read_only": False,

View file

@ -1,9 +1,9 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2019 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 PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt
from UM.Application import Application import cura.CuraApplication
from UM.Logger import Logger from UM.Logger import Logger
from UM.Qt.ListModel import ListModel from UM.Qt.ListModel import ListModel
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
@ -35,15 +35,13 @@ class QualitySettingsModel(ListModel):
self.addRoleName(self.CategoryRole, "category") self.addRoleName(self.CategoryRole, "category")
self._container_registry = ContainerRegistry.getInstance() self._container_registry = ContainerRegistry.getInstance()
self._application = Application.getInstance() self._application = cura.CuraApplication.CuraApplication.getInstance()
self._quality_manager = self._application.getQualityManager() self._application.getMachineManager().activeStackChanged.connect(self._update)
self._selected_position = self.GLOBAL_STACK_POSITION #Must be either GLOBAL_STACK_POSITION or an extruder position (0, 1, etc.) self._selected_position = self.GLOBAL_STACK_POSITION #Must be either GLOBAL_STACK_POSITION or an extruder position (0, 1, etc.)
self._selected_quality_item = None # The selected quality in the quality management page self._selected_quality_item = None # The selected quality in the quality management page
self._i18n_catalog = None self._i18n_catalog = None
self._quality_manager.qualitiesUpdated.connect(self._update)
self._update() self._update()
selectedPositionChanged = pyqtSignal() selectedPositionChanged = pyqtSignal()
@ -93,21 +91,33 @@ class QualitySettingsModel(ListModel):
quality_node = quality_group.nodes_for_extruders.get(str(self._selected_position)) quality_node = quality_group.nodes_for_extruders.get(str(self._selected_position))
settings_keys = quality_group.getAllKeys() settings_keys = quality_group.getAllKeys()
quality_containers = [] quality_containers = []
if quality_node is not None and quality_node.getContainer() is not None: if quality_node is not None and quality_node.container is not None:
quality_containers.append(quality_node.getContainer()) quality_containers.append(quality_node.container)
# Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch # Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch
# the settings in that quality_changes_group. # the settings in that quality_changes_group.
if quality_changes_group is not None: if quality_changes_group is not None:
if self._selected_position == self.GLOBAL_STACK_POSITION: container_registry = ContainerRegistry.getInstance()
quality_changes_node = quality_changes_group.node_for_global global_containers = container_registry.findContainers(id = quality_changes_group.metadata_for_global["id"])
global_container = None if len(global_containers) == 0 else global_containers[0]
extruders_containers = {pos: container_registry.findContainers(id = quality_changes_group.metadata_per_extruder[pos]["id"]) for pos in quality_changes_group.metadata_per_extruder}
extruders_container = {pos: None if not containers else containers[0] for pos, containers in extruders_containers.items()}
if self._selected_position == self.GLOBAL_STACK_POSITION and global_container:
quality_changes_metadata = global_container.getMetaData()
else: else:
quality_changes_node = quality_changes_group.nodes_for_extruders.get(str(self._selected_position)) quality_changes_metadata = extruders_container.get(str(self._selected_position))
if quality_changes_node is not None and quality_changes_node.container is not None: # it can be None if number of extruders are changed during runtime if quality_changes_metadata is not None: # It can be None if number of extruders are changed during runtime.
quality_containers.insert(0, quality_changes_node.container) container = container_registry.findContainers(id = quality_changes_metadata["id"])
settings_keys.update(quality_changes_group.getAllKeys()) if container:
quality_containers.insert(0, container[0])
# We iterate over all definitions instead of settings in a quality/qualtiy_changes group is because in the GUI, if global_container:
settings_keys.update(global_container.getAllKeys())
for container in extruders_container.values():
if container:
settings_keys.update(container.getAllKeys())
# We iterate over all definitions instead of settings in a quality/quality_changes group is because in the GUI,
# the settings are grouped together by categories, and we had to go over all the definitions to figure out # the settings are grouped together by categories, and we had to go over all the definitions to figure out
# which setting belongs in which category. # which setting belongs in which category.
current_category = "" current_category = ""

View file

@ -16,7 +16,7 @@ class QualityChangesGroup(QObject):
self.quality_type = quality_type self.quality_type = quality_type
self.intent_category = intent_category self.intent_category = intent_category
self.is_available = False self.is_available = False
self.metadata_for_global = None # type: Optional[str] self.metadata_for_global = {} # type: Dict[str, Any]
self.metadata_per_extruder = {} # type: Dict[int, Dict[str, Any]] self.metadata_per_extruder = {} # type: Dict[int, Dict[str, Any]]
def __str__(self) -> str: def __str__(self) -> str:

View file

@ -5,6 +5,7 @@ from typing import Dict, Optional, List, Set
from PyQt5.QtCore import QObject, pyqtSlot from PyQt5.QtCore import QObject, pyqtSlot
from UM.Logger import Logger
from UM.Util import parseBool from UM.Util import parseBool
from cura.Machines.ContainerNode import ContainerNode from cura.Machines.ContainerNode import ContainerNode
@ -60,6 +61,9 @@ class QualityGroup(QObject):
self.node_for_global = node self.node_for_global = node
# Update is_experimental flag # Update is_experimental flag
if not node.container:
Logger.log("w", "Node {0} doesn't have a container.".format(node.container_id))
return
is_experimental = parseBool(node.container.getMetaDataEntry("is_experimental", False)) is_experimental = parseBool(node.container.getMetaDataEntry("is_experimental", False))
self.is_experimental |= is_experimental self.is_experimental |= is_experimental
@ -67,5 +71,8 @@ class QualityGroup(QObject):
self.nodes_for_extruders[position] = node self.nodes_for_extruders[position] = node
# Update is_experimental flag # Update is_experimental flag
if not node.container:
Logger.log("w", "Node {0} doesn't have a container.".format(node.container_id))
return
is_experimental = parseBool(node.container.getMetaDataEntry("is_experimental", False)) is_experimental = parseBool(node.container.getMetaDataEntry("is_experimental", False))
self.is_experimental |= is_experimental self.is_experimental |= is_experimental

View file

@ -1,7 +1,7 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 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 typing import TYPE_CHECKING, Optional, Dict from typing import Any, Dict, List, Optional, TYPE_CHECKING
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
@ -14,6 +14,7 @@ import cura.CuraApplication
from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.ExtruderStack import ExtruderStack
from cura.Machines.ContainerTree import ContainerTree # The implementation that replaces this manager, to keep the deprecated interface working. from cura.Machines.ContainerTree import ContainerTree # The implementation that replaces this manager, to keep the deprecated interface working.
from .QualityChangesGroup import QualityChangesGroup
from .QualityGroup import QualityGroup from .QualityGroup import QualityGroup
from .QualityNode import QualityNode from .QualityNode import QualityNode
@ -75,7 +76,7 @@ class QualityManager(QObject):
return return
# Returns a dict of "custom profile name" -> QualityChangesGroup # Returns a dict of "custom profile name" -> QualityChangesGroup
def getQualityChangesGroups(self, machine: "GlobalStack") -> dict: def getQualityChangesGroups(self, machine: "GlobalStack") -> List[QualityChangesGroup]:
variant_names = [extruder.variant.getName() for extruder in machine.extruders.values()] variant_names = [extruder.variant.getName() for extruder in machine.extruders.values()]
material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in machine.extruders.values()] material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in machine.extruders.values()]
extruder_enabled = [extruder.isEnabled for extruder in machine.extruders.values()] extruder_enabled = [extruder.isEnabled for extruder in machine.extruders.values()]
@ -111,12 +112,10 @@ class QualityManager(QObject):
# Iterate over all quality_types in the machine node # Iterate over all quality_types in the machine node
quality_group_dict = dict() quality_group_dict = dict()
for node in nodes_to_check: for node in nodes_to_check:
if node and node.quality_type_map: if node and node.quality_type:
for quality_type, quality_node in node.quality_type_map.items(): quality_group = QualityGroup(node.getMetaDataEntry("name", ""), node.quality_type)
quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type) quality_group.setGlobalNode(node)
quality_group.setGlobalNode(quality_node) quality_group_dict[node.quality_type] = quality_group
quality_group_dict[quality_type] = quality_group
break
return quality_group_dict return quality_group_dict
@ -142,76 +141,33 @@ class QualityManager(QObject):
# Methods for GUI # Methods for GUI
# #
# ## Deletes a custom profile. It will be gone forever.
# Remove the given quality changes group. # \param quality_changes_group The quality changes group representing the
# # profile to delete.
@pyqtSlot(QObject) @pyqtSlot(QObject)
def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None: def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name) return cura.CuraApplication.CuraApplication.getInstance().getQualityManagementModel().removeQualityChangesGroup(quality_changes_group)
removed_quality_changes_ids = set()
for node in quality_changes_group.getAllNodes():
container_id = node.container_id
self._container_registry.removeContainer(container_id)
removed_quality_changes_ids.add(container_id)
# Reset all machines that have activated this quality changes to empty.
for global_stack in self._container_registry.findContainerStacks(type = "machine"):
if global_stack.qualityChanges.getId() in removed_quality_changes_ids:
global_stack.qualityChanges = self._empty_quality_changes_container
for extruder_stack in self._container_registry.findContainerStacks(type = "extruder_train"):
if extruder_stack.qualityChanges.getId() in removed_quality_changes_ids:
extruder_stack.qualityChanges = self._empty_quality_changes_container
## Rename a custom profile.
# #
# Rename a set of quality changes containers. Returns the new name. # Because the names must be unique, the new name may not actually become
# # the name that was given. The actual name is returned by this function.
# \param quality_changes_group The custom profile that must be renamed.
# \param new_name The desired name for the profile.
# \return The actual new name of the profile, after making the name
# unique.
@pyqtSlot(QObject, str, result = str) @pyqtSlot(QObject, str, result = str)
def renameQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", new_name: str) -> str: def renameQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", new_name: str) -> str:
Logger.log("i", "Renaming QualityChangesGroup[%s] to [%s]", quality_changes_group.name, new_name) return cura.CuraApplication.CuraApplication.getInstance().getQualityManagementModel().removeQualityChangesGroup(quality_changes_group, new_name)
if new_name == quality_changes_group.name:
Logger.log("i", "QualityChangesGroup name [%s] unchanged.", quality_changes_group.name)
return new_name
new_name = self._container_registry.uniqueName(new_name) ## Duplicates a given quality profile OR quality changes profile.
for node in quality_changes_group.getAllNodes(): # \param new_name The desired name of the new profile. This will be made
container = node.container # unique, so it might end up with a different name.
if container: # \param quality_model_item The item of this model to duplicate, as
container.setName(new_name) # dictionary. See the descriptions of the roles of this list model.
quality_changes_group.name = new_name
application = cura.CuraApplication.CuraApplication.getInstance()
application.getMachineManager().activeQualityChanged.emit()
application.getMachineManager().activeQualityGroupChanged.emit()
return new_name
#
# Duplicates the given quality.
#
@pyqtSlot(str, "QVariantMap") @pyqtSlot(str, "QVariantMap")
def duplicateQualityChanges(self, quality_changes_name: str, quality_model_item) -> None: def duplicateQualityChanges(self, quality_changes_name: str, quality_model_item: Dict[str, Any]) -> None:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() return cura.CuraApplication.CuraApplication.getInstance().getQualityManagementModel().duplicateQualityChanges(quality_changes_name, quality_model_item)
if not global_stack:
Logger.log("i", "No active global stack, cannot duplicate quality changes.")
return
quality_group = quality_model_item["quality_group"]
quality_changes_group = quality_model_item["quality_changes_group"]
if quality_changes_group is None:
# create global quality changes only
new_name = self._container_registry.uniqueName(quality_changes_name)
new_quality_changes = self._createQualityChanges(quality_group.quality_type, new_name,
global_stack, None)
self._container_registry.addContainer(new_quality_changes)
else:
new_name = self._container_registry.uniqueName(quality_changes_name)
for node in quality_changes_group.getAllNodes():
container = node.container
if not container:
continue
new_id = self._container_registry.uniqueName(container.getId())
self._container_registry.addContainer(container.duplicate(new_id, new_name))
## Create quality changes containers from the user containers in the active stacks. ## Create quality changes containers from the user containers in the active stacks.
# #

View file

@ -14,6 +14,7 @@ if TYPE_CHECKING:
from typing import Dict from typing import Dict
from cura.Machines.MachineNode import MachineNode from cura.Machines.MachineNode import MachineNode
## This class represents an extruder variant in the container tree. ## This class represents an extruder variant in the container tree.
# #
# The subnodes of these nodes are materials. # The subnodes of these nodes are materials.
@ -33,10 +34,11 @@ class VariantNode(ContainerNode):
container_registry = ContainerRegistry.getInstance() container_registry = ContainerRegistry.getInstance()
self.variant_name = container_registry.findContainersMetadata(id = container_id)[0]["name"] # Store our own name so that we can filter more easily. self.variant_name = container_registry.findContainersMetadata(id = container_id)[0]["name"] # Store our own name so that we can filter more easily.
container_registry.containerAdded.connect(self._materialAdded) container_registry.containerAdded.connect(self._materialAdded)
container_registry.containerRemoved.connect(self._materialRemoved)
self._loadAll() self._loadAll()
## (Re)loads all materials under this variant. ## (Re)loads all materials under this variant.
def _loadAll(self): def _loadAll(self) -> None:
container_registry = ContainerRegistry.getInstance() container_registry = ContainerRegistry.getInstance()
if not self.machine.has_materials: if not self.machine.has_materials:
@ -53,12 +55,10 @@ class VariantNode(ContainerNode):
materials_per_base_file = {material["base_file"]: material for material in all_materials} materials_per_base_file = {material["base_file"]: material for material in all_materials}
materials_per_base_file.update({material["base_file"]: material for material in printer_specific_materials}) # Printer-specific profiles override global ones. materials_per_base_file.update({material["base_file"]: material for material in printer_specific_materials}) # Printer-specific profiles override global ones.
materials_per_base_file.update({material["base_file"]: material for material in variant_specific_materials}) # Variant-specific profiles override all of those. materials_per_base_file.update({material["base_file"]: material for material in variant_specific_materials}) # Variant-specific profiles override all of those.
materials = materials_per_base_file.values() materials = list(materials_per_base_file.values())
filtered_materials = [] # Filter materials based on the exclude_materials property.
for material in materials: filtered_materials = [material for material in materials if material["id"] not in self.machine.exclude_materials]
if material["id"] not in self.machine.exclude_materials:
filtered_materials.append(material)
for material in filtered_materials: for material in filtered_materials:
base_file = material["base_file"] base_file = material["base_file"]
@ -79,7 +79,7 @@ class VariantNode(ContainerNode):
# material. # material.
# \return The node for the preferred material, or any arbitrary material # \return The node for the preferred material, or any arbitrary material
# if there is no match. # if there is no match.
def preferredMaterial(self, approximate_diameter) -> MaterialNode: def preferredMaterial(self, approximate_diameter: int) -> MaterialNode:
for base_material, material_node in self.materials.items(): for base_material, material_node in self.materials.items():
if self.machine.preferred_material in base_material and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")): if self.machine.preferred_material in base_material and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")):
return material_node return material_node
@ -94,7 +94,7 @@ class VariantNode(ContainerNode):
## When a material gets added to the set of profiles, we need to update our ## When a material gets added to the set of profiles, we need to update our
# tree here. # tree here.
def _materialAdded(self, container: ContainerInterface): def _materialAdded(self, container: ContainerInterface) -> None:
if container.getMetaDataEntry("type") != "material": if container.getMetaDataEntry("type") != "material":
return # Not interested. return # Not interested.
if not self.machine.has_materials: if not self.machine.has_materials:
@ -128,3 +128,33 @@ class VariantNode(ContainerNode):
del self.materials["empty_material"] del self.materials["empty_material"]
self.materials[base_file] = MaterialNode(container.getId(), variant = self) self.materials[base_file] = MaterialNode(container.getId(), variant = self)
self.materials[base_file].materialChanged.connect(self.materialsChanged) self.materials[base_file].materialChanged.connect(self.materialsChanged)
self.materialsChanged.emit(self.materials[base_file])
def _materialRemoved(self, container: ContainerInterface) -> None:
if container.getMetaDataEntry("type") != "material":
return # Only interested in materials.
base_file = container.getMetaDataEntry("base_file")
if base_file not in self.materials:
return # We don't track this material anyway. No need to remove it.
original_node = self.materials[base_file]
del self.materials[base_file]
self.materialsChanged.emit(original_node)
# Now a different material from the same base file may have been hidden because it was not as specific as the one we deleted.
# Search for any submaterials from that base file that are still left.
materials_same_base_file = ContainerRegistry.getInstance().findContainersMetadata(base_file = base_file)
if materials_same_base_file:
most_specific_submaterial = materials_same_base_file[0]
for submaterial in materials_same_base_file:
if submaterial["definition"] == self.machine.container_id:
if most_specific_submaterial["definition"] == "fdmprinter":
most_specific_submaterial = submaterial
if most_specific_submaterial.get("variant", "empty") == "empty" and submaterial.get("variant", "empty") == self.variant_name:
most_specific_submaterial = submaterial
self.materials[base_file] = MaterialNode(most_specific_submaterial["id"], variant = self)
self.materialsChanged.emit(self.materials[base_file])
if not self.materials: # The last available material just got deleted and there is nothing with the same base file to replace it.
self.materials["empty_material"] = MaterialNode("empty_material", variant = self)
self.materialsChanged.emit(self.materials["empty_material"])

View file

@ -83,6 +83,9 @@ class ContainerManager(QObject):
# Update: In order for QML to use objects and sub objects, those (sub) objects must all be QObject. Is that what we want? # Update: In order for QML to use objects and sub objects, those (sub) objects must all be QObject. Is that what we want?
@pyqtSlot("QVariant", str, str) @pyqtSlot("QVariant", str, str)
def setContainerMetaDataEntry(self, container_node: "ContainerNode", entry_name: str, entry_value: str) -> bool: def setContainerMetaDataEntry(self, container_node: "ContainerNode", entry_name: str, entry_value: str) -> bool:
if container_node.container is None:
Logger.log("w", "Container node {0} doesn't have a container.".format(container_node.container_id))
return False
root_material_id = container_node.container.getMetaDataEntry("base_file", "") root_material_id = container_node.container.getMetaDataEntry("base_file", "")
if cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry().isReadOnly(root_material_id): if cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry().isReadOnly(root_material_id):
Logger.log("w", "Cannot set metadata of read-only container %s.", root_material_id) Logger.log("w", "Cannot set metadata of read-only container %s.", root_material_id)
@ -341,6 +344,9 @@ class ContainerManager(QObject):
@pyqtSlot("QVariant") @pyqtSlot("QVariant")
def unlinkMaterial(self, material_node: "MaterialNode") -> None: def unlinkMaterial(self, material_node: "MaterialNode") -> None:
# Get the material group # Get the material group
if material_node.container is None:
Logger.log("w", "Material node {0} doesn't have a container.".format(material_node.container_id))
return
material_group = MaterialManager.getInstance().getMaterialGroup(material_node.container.getMetaDataEntry("base_file", "")) material_group = MaterialManager.getInstance().getMaterialGroup(material_node.container.getMetaDataEntry("base_file", ""))
if material_group is None: if material_group is None:

View file

@ -28,7 +28,7 @@ from . import GlobalStack
import cura.CuraApplication import cura.CuraApplication
from cura.Settings.cura_empty_instance_containers import empty_quality_container from cura.Settings.cura_empty_instance_containers import empty_quality_container
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch from cura.Machines.ContainerTree import ContainerTree
from cura.ReaderWriters.ProfileReader import NoProfileException, ProfileReader from cura.ReaderWriters.ProfileReader import NoProfileException, ProfileReader
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
@ -177,6 +177,7 @@ class CuraContainerRegistry(ContainerRegistry):
global_stack = Application.getInstance().getGlobalContainerStack() global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack: if not global_stack:
return {"status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Can't import profile from <filename>{0}</filename> before a printer is added.", file_name)} return {"status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Can't import profile from <filename>{0}</filename> before a printer is added.", file_name)}
container_tree = ContainerTree.getInstance()
machine_extruders = [] machine_extruders = []
for position in sorted(global_stack.extruders): for position in sorted(global_stack.extruders):
@ -226,7 +227,7 @@ class CuraContainerRegistry(ContainerRegistry):
# Make sure we have a profile_definition in the file: # Make sure we have a profile_definition in the file:
if profile_definition is None: if profile_definition is None:
break break
machine_definitions = self.findDefinitionContainers(id = profile_definition) machine_definitions = self.findContainers(id = profile_definition)
if not machine_definitions: if not machine_definitions:
Logger.log("e", "Incorrect profile [%s]. Unknown machine type [%s]", file_name, profile_definition) Logger.log("e", "Incorrect profile [%s]. Unknown machine type [%s]", file_name, profile_definition)
return {"status": "error", return {"status": "error",
@ -236,8 +237,8 @@ class CuraContainerRegistry(ContainerRegistry):
# Get the expected machine definition. # Get the expected machine definition.
# i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode... # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
profile_definition = getMachineDefinitionIDForQualitySearch(machine_definition) profile_definition = container_tree.machines[machine_definition.getId()].quality_definition
expected_machine_definition = getMachineDefinitionIDForQualitySearch(global_stack.definition) expected_machine_definition = container_tree.machines[global_stack.definition.getId()].quality_definition
# And check if the profile_definition matches either one (showing error if not): # And check if the profile_definition matches either one (showing error if not):
if profile_definition != expected_machine_definition: if profile_definition != expected_machine_definition:
@ -380,14 +381,13 @@ class CuraContainerRegistry(ContainerRegistry):
global_stack = Application.getInstance().getGlobalContainerStack() global_stack = Application.getInstance().getGlobalContainerStack()
if global_stack is None: if global_stack is None:
return None return None
definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition) definition_id = ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_definition
profile.setDefinition(definition_id) profile.setDefinition(definition_id)
# Check to make sure the imported profile actually makes sense in context of the current configuration. # Check to make sure the imported profile actually makes sense in context of the current configuration.
# This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as
# successfully imported but then fail to show up. # successfully imported but then fail to show up.
quality_manager = cura.CuraApplication.CuraApplication.getInstance()._quality_manager quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups()
quality_group_dict = quality_manager.getQualityGroupsForMachineDefinition(global_stack)
# "not_supported" profiles can be imported. # "not_supported" profiles can be imported.
if quality_type != empty_quality_container.getMetaDataEntry("quality_type") and quality_type not in quality_group_dict: if quality_type != empty_quality_container.getMetaDataEntry("quality_type") and quality_type not in quality_group_dict:
return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type)

View file

@ -24,8 +24,8 @@ from UM.Signal import postponeSignals, CompressTechnique
import cura.CuraApplication # Imported like this to prevent circular references. import cura.CuraApplication # Imported like this to prevent circular references.
from cura.Machines.ContainerNode import ContainerNode
from cura.Machines.ContainerTree import ContainerTree from cura.Machines.ContainerTree import ContainerTree
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch, QualityManager
from cura.Machines.MaterialManager import MaterialManager from cura.Machines.MaterialManager import MaterialManager
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice, ConnectionType from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice, ConnectionType
@ -37,7 +37,7 @@ from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.ExtruderStack import ExtruderStack
from cura.Settings.cura_empty_instance_containers import (empty_definition_changes_container, empty_variant_container, from cura.Settings.cura_empty_instance_containers import (empty_definition_changes_container, empty_variant_container,
empty_material_container, empty_quality_container, empty_material_container, empty_quality_container,
empty_quality_changes_container) empty_quality_changes_container, empty_intent_container)
from .CuraStackBuilder import CuraStackBuilder from .CuraStackBuilder import CuraStackBuilder
@ -600,6 +600,8 @@ class MachineManager(QObject):
global_container_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() global_container_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_container_stack: if not global_container_stack:
return False return False
if not self.activeQualityGroup:
return False
return self.activeQualityGroup.is_available return self.activeQualityGroup.is_available
@pyqtProperty(bool, notify = activeQualityGroupChanged) @pyqtProperty(bool, notify = activeQualityGroupChanged)
@ -703,9 +705,10 @@ class MachineManager(QObject):
# \returns DefinitionID (string) if found, empty string otherwise # \returns DefinitionID (string) if found, empty string otherwise
@pyqtProperty(str, notify = globalContainerChanged) @pyqtProperty(str, notify = globalContainerChanged)
def activeQualityDefinitionId(self) -> str: def activeQualityDefinitionId(self) -> str:
if self._global_container_stack: global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
return getMachineDefinitionIDForQualitySearch(self._global_container_stack.definition) if not global_stack:
return "" return ""
return ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_definition
## Gets how the active definition calls variants ## Gets how the active definition calls variants
# Caveat: per-definition-variant-title is currently not translated (though the fallback is) # Caveat: per-definition-variant-title is currently not translated (though the fallback is)
@ -1128,12 +1131,11 @@ class MachineManager(QObject):
self.activeQualityChangesGroupChanged.emit() self.activeQualityChangesGroupChanged.emit()
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group: "QualityChangesGroup") -> None: def _fixQualityChangesGroupToNotSupported(self, quality_changes_group: "QualityChangesGroup") -> None:
nodes = [quality_changes_group.node_for_global] + list(quality_changes_group.nodes_for_extruders.values()) metadatas = [quality_changes_group.metadata_for_global] + list(quality_changes_group.metadata_per_extruder.values())
containers = [n.container for n in nodes if n is not None] for metadata in metadatas:
for container in containers: metadata["quality_type"] = "not_supported" # This actually changes the metadata of the container since they are stored by reference!
if container:
container.setMetaDataEntry("quality_type", "not_supported")
quality_changes_group.quality_type = "not_supported" quality_changes_group.quality_type = "not_supported"
quality_changes_group.intent_category = "default"
def _setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None: def _setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
if self._global_container_stack is None: if self._global_container_stack is None:
@ -1142,16 +1144,22 @@ class MachineManager(QObject):
# A custom quality can be created based on "not supported". # A custom quality can be created based on "not supported".
# In that case, do not set quality containers to empty. # In that case, do not set quality containers to empty.
quality_group = None quality_group = None
if quality_type != "not_supported": if quality_type != "not_supported": # Find the quality group that the quality changes was based on.
quality_group_dict = QualityManager.getInstance().getQualityGroups(self._global_container_stack) quality_group = ContainerTree.getInstance().getCurrentQualityGroups().get(quality_type)
quality_group = quality_group_dict.get(quality_type)
if quality_group is None: if quality_group is None:
self._fixQualityChangesGroupToNotSupported(quality_changes_group) self._fixQualityChangesGroupToNotSupported(quality_changes_group)
container_registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry()
quality_changes_container = empty_quality_changes_container quality_changes_container = empty_quality_changes_container
quality_container = empty_quality_container # type: Optional[InstanceContainer] quality_container = empty_quality_container # type: Optional[InstanceContainer]
if quality_changes_group.node_for_global and quality_changes_group.node_for_global.container: if quality_changes_group.metadata_for_global:
quality_changes_container = cast(InstanceContainer, quality_changes_group.node_for_global.container) global_containers = container_registry.findContainers(id = quality_changes_group.metadata_for_global["id"])
if global_containers:
quality_changes_container = global_containers[0]
if quality_changes_group.metadata_for_global:
containers = container_registry.findContainers(id = quality_changes_group.metadata_for_global["id"])
if containers:
quality_changes_container = cast(InstanceContainer, containers[0])
if quality_group is not None and quality_group.node_for_global and quality_group.node_for_global.container: if quality_group is not None and quality_group.node_for_global and quality_group.node_for_global.container:
quality_container = quality_group.node_for_global.container quality_container = quality_group.node_for_global.container
@ -1159,21 +1167,25 @@ class MachineManager(QObject):
self._global_container_stack.qualityChanges = quality_changes_container self._global_container_stack.qualityChanges = quality_changes_container
for position, extruder in self._global_container_stack.extruders.items(): for position, extruder in self._global_container_stack.extruders.items():
quality_changes_node = quality_changes_group.nodes_for_extruders.get(position)
quality_node = None quality_node = None
if quality_group is not None: if quality_group is not None:
quality_node = quality_group.nodes_for_extruders.get(position) quality_node = quality_group.nodes_for_extruders.get(position)
quality_changes_container = empty_quality_changes_container quality_changes_container = empty_quality_changes_container
quality_container = empty_quality_container quality_container = empty_quality_container
if quality_changes_node and quality_changes_node.container: quality_changes_metadata = quality_changes_group.metadata_per_extruder.get(position)
quality_changes_container = cast(InstanceContainer, quality_changes_node.container) if quality_changes_metadata:
containers = container_registry.findContainers(id = quality_changes_metadata["id"])
if containers:
quality_changes_container = cast(InstanceContainer, containers[0])
if quality_node and quality_node.container: if quality_node and quality_node.container:
quality_container = quality_node.container quality_container = quality_node.container
extruder.quality = quality_container extruder.quality = quality_container
extruder.qualityChanges = quality_changes_container extruder.qualityChanges = quality_changes_container
self.setIntentByCategory(quality_changes_group.intent_category)
self.activeQualityGroupChanged.emit() self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit() self.activeQualityChangesGroupChanged.emit()
@ -1193,7 +1205,7 @@ class MachineManager(QObject):
def _setMaterial(self, position: str, material_node: Optional["MaterialNode"] = None) -> None: def _setMaterial(self, position: str, material_node: Optional["MaterialNode"] = None) -> None:
if self._global_container_stack is None: if self._global_container_stack is None:
return return
if material_node: if material_node and material_node.container:
material_container = material_node.container material_container = material_node.container
self._global_container_stack.extruders[position].material = material_container self._global_container_stack.extruders[position].material = material_container
root_material_id = material_container.getMetaDataEntry("base_file", None) root_material_id = material_container.getMetaDataEntry("base_file", None)
@ -1247,6 +1259,9 @@ class MachineManager(QObject):
# The current quality type is not available so we use the preferred quality type if it's available, # The current quality type is not available so we use the preferred quality type if it's available,
# otherwise use one of the available quality types. # otherwise use one of the available quality types.
quality_type = sorted(list(available_quality_types))[0] quality_type = sorted(list(available_quality_types))[0]
if self._global_container_stack is None:
Logger.log("e", "Global stack not present!")
return
preferred_quality_type = self._global_container_stack.getMetaDataEntry("preferred_quality_type") preferred_quality_type = self._global_container_stack.getMetaDataEntry("preferred_quality_type")
if preferred_quality_type in available_quality_types: if preferred_quality_type in available_quality_types:
quality_type = preferred_quality_type quality_type = preferred_quality_type
@ -1470,9 +1485,7 @@ class MachineManager(QObject):
if self._global_container_stack is None: if self._global_container_stack is None:
return return
# Get all the quality groups for this global stack and filter out by quality_type # Get all the quality groups for this global stack and filter out by quality_type
quality_group_dict = self._application.getQualityManager().getQualityGroups(self._global_container_stack) self.setQualityGroup(ContainerTree.getInstance().getCurrentQualityGroups()[quality_type])
quality_group = quality_group_dict[quality_type]
self.setQualityGroup(quality_group)
## Optionally provide global_stack if you want to use your own ## Optionally provide global_stack if you want to use your own
# The active global_stack is treated differently. # The active global_stack is treated differently.
@ -1503,6 +1516,32 @@ class MachineManager(QObject):
if not no_dialog and self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1: if not no_dialog and self.hasUserSettings and self._application.getPreferences().getValue("cura/active_mode") == 1:
self._application.discardOrKeepProfileChanges() self._application.discardOrKeepProfileChanges()
## Change the intent category of the current printer.
#
# All extruders can change their profiles. If an intent profile is
# available with the desired intent category, that one will get chosen.
# Otherwise the intent profile will be left to the empty profile, which
# represents the "default" intent category.
# \param intent_category The intent category to change to.
def setIntentByCategory(self, intent_category: str) -> None:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if global_stack is None:
return
container_tree = ContainerTree.getInstance()
for extruder in global_stack.extruderList:
definition_id = global_stack.definition.getId()
variant_name = extruder.variant.getName()
material_base_file = extruder.material.getMetaDataEntry("base_file")
quality_id = extruder.quality.getId()
quality_node = container_tree.machines[definition_id].variants[variant_name].materials[material_base_file].qualities[quality_id]
for intent_node in quality_node.intents.values():
if intent_node.intent_category == intent_category: # Found an intent with the correct category.
extruder.intent = intent_node.container
break
else: # No intent had the correct category.
extruder.intent = empty_intent_container
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged) @pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
def activeQualityGroup(self) -> Optional["QualityGroup"]: def activeQualityGroup(self) -> Optional["QualityGroup"]:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
@ -1557,7 +1596,7 @@ class MachineManager(QObject):
@pyqtProperty(bool, notify = activeQualityGroupChanged) @pyqtProperty(bool, notify = activeQualityGroupChanged)
def hasNotSupportedQuality(self) -> bool: def hasNotSupportedQuality(self) -> bool:
global_container_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() global_container_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
return global_container_stack and global_container_stack.quality == empty_quality_container and global_container_stack.qualityChanges == empty_quality_changes_container return (not global_container_stack is None) and global_container_stack.quality == empty_quality_container and global_container_stack.qualityChanges == empty_quality_changes_container
def _updateUponMaterialMetadataChange(self) -> None: def _updateUponMaterialMetadataChange(self) -> None:
if self._global_container_stack is None: if self._global_container_stack is None:

View file

@ -19,12 +19,12 @@ from UM.Scene.SceneNode import SceneNode #For typing.
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from cura.Scene.CuraSceneNode import CuraSceneNode from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
from cura.Scene.ZOffsetDecorator import ZOffsetDecorator from cura.Scene.ZOffsetDecorator import ZOffsetDecorator
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
try: try:
@ -131,7 +131,7 @@ class ThreeMFReader(MeshReader):
um_node.callDecoration("setActiveExtruder", default_stack.getId()) um_node.callDecoration("setActiveExtruder", default_stack.getId())
# Get the definition & set it # Get the definition & set it
definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack.definition) definition_id = ContainerTree.getInstance().machines[global_container_stack.definition.getId()].quality_definition
um_node.callDecoration("getStack").getTop().setDefinition(definition_id) um_node.callDecoration("getStack").getTop().setDefinition(definition_id)
setting_container = um_node.callDecoration("getStack").getTop() setting_container = um_node.callDecoration("getStack").getTop()

View file

@ -23,6 +23,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from UM.Job import Job from UM.Job import Job
from UM.Preferences import Preferences from UM.Preferences import Preferences
from cura.Machines.ContainerTree import ContainerTree
from cura.Machines.VariantType import VariantType from cura.Machines.VariantType import VariantType
from cura.Settings.CuraStackBuilder import CuraStackBuilder from cura.Settings.CuraStackBuilder import CuraStackBuilder
@ -746,8 +747,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._machine_info.quality_changes_info.name) self._machine_info.quality_changes_info.name)
# Get the correct extruder definition IDs for quality changes # Get the correct extruder definition IDs for quality changes
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch machine_definition_id_for_quality = ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_definition
machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(global_stack.definition)
machine_definition_for_quality = self._container_registry.findDefinitionContainers(id = machine_definition_id_for_quality)[0] machine_definition_for_quality = self._container_registry.findDefinitionContainers(id = machine_definition_id_for_quality)[0]
quality_changes_info = self._machine_info.quality_changes_info quality_changes_info = self._machine_info.quality_changes_info
@ -992,8 +992,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
def _updateActiveMachine(self, global_stack): def _updateActiveMachine(self, global_stack):
# Actually change the active machine. # Actually change the active machine.
machine_manager = Application.getInstance().getMachineManager() machine_manager = Application.getInstance().getMachineManager()
material_manager = Application.getInstance().getMaterialManager() container_tree = ContainerTree.getInstance()
quality_manager = Application.getInstance().getQualityManager()
machine_manager.setActiveMachine(global_stack.getId()) machine_manager.setActiveMachine(global_stack.getId())
@ -1003,21 +1002,20 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
global_stack.setMetaDataEntry(key, value) global_stack.setMetaDataEntry(key, value)
if self._quality_changes_to_apply: if self._quality_changes_to_apply:
quality_changes_group_dict = quality_manager.getQualityChangesGroups(global_stack) quality_changes_group_list = container_tree.getCurrentQualityChangesGroups()
if self._quality_changes_to_apply not in quality_changes_group_dict: quality_changes_group = next((qcg for qcg in quality_changes_group_list if qcg.name == self._quality_changes_to_apply), None)
if not quality_changes_group:
Logger.log("e", "Could not find quality_changes [%s]", self._quality_changes_to_apply) Logger.log("e", "Could not find quality_changes [%s]", self._quality_changes_to_apply)
return return
quality_changes_group = quality_changes_group_dict[self._quality_changes_to_apply]
machine_manager.setQualityChangesGroup(quality_changes_group, no_dialog = True) machine_manager.setQualityChangesGroup(quality_changes_group, no_dialog = True)
else: else:
self._quality_type_to_apply = self._quality_type_to_apply.lower() self._quality_type_to_apply = self._quality_type_to_apply.lower()
quality_group_dict = quality_manager.getQualityGroups(global_stack) quality_group_dict = container_tree.getCurrentQualityGroups()
if self._quality_type_to_apply in quality_group_dict: if self._quality_type_to_apply in quality_group_dict:
quality_group = quality_group_dict[self._quality_type_to_apply] quality_group = quality_group_dict[self._quality_type_to_apply]
else: else:
Logger.log("i", "Could not find quality type [%s], switch to default", self._quality_type_to_apply) Logger.log("i", "Could not find quality type [%s], switch to default", self._quality_type_to_apply)
preferred_quality_type = global_stack.getMetaDataEntry("preferred_quality_type") preferred_quality_type = global_stack.getMetaDataEntry("preferred_quality_type")
quality_group_dict = quality_manager.getQualityGroups(global_stack)
quality_group = quality_group_dict.get(preferred_quality_type) quality_group = quality_group_dict.get(preferred_quality_type)
if quality_group is None: if quality_group is None:
Logger.log("e", "Could not get preferred quality type [%s]", preferred_quality_type) Logger.log("e", "Could not get preferred quality type [%s]", preferred_quality_type)

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2019 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.
import re # For escaping characters in the settings. import re # For escaping characters in the settings.
@ -9,8 +9,7 @@ from UM.Mesh.MeshWriter import MeshWriter
from UM.Logger import Logger from UM.Logger import Logger
from UM.Application import Application from UM.Application import Application
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
from cura.Machines.ContainerTree import ContainerTree
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
@ -139,7 +138,7 @@ class GCodeWriter(MeshWriter):
flat_global_container.setMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal")) flat_global_container.setMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal"))
# Get the machine definition ID for quality profiles # Get the machine definition ID for quality profiles
machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(stack.definition) machine_definition_id_for_quality = ContainerTree.getInstance().machines[stack.definition.getId()].quality_definition
flat_global_container.setMetaDataEntry("definition", machine_definition_id_for_quality) flat_global_container.setMetaDataEntry("definition", machine_definition_id_for_quality)
serialized = flat_global_container.serialize() serialized = flat_global_container.serialize()

View file

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Toolbox is released under the terms of the LGPLv3 or higher. # Toolbox is released under the terms of the LGPLv3 or higher.
import json import json
@ -19,6 +19,7 @@ from UM.Version import Version
from cura import ApplicationMetadata from cura import ApplicationMetadata
from cura import UltimakerCloudAuthentication from cura import UltimakerCloudAuthentication
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from cura.Machines.ContainerTree import ContainerTree
from .AuthorsModel import AuthorsModel from .AuthorsModel import AuthorsModel
from .PackagesModel import PackagesModel from .PackagesModel import PackagesModel
@ -360,14 +361,19 @@ class Toolbox(QObject, Extension):
def resetMaterialsQualitiesAndUninstall(self) -> None: def resetMaterialsQualitiesAndUninstall(self) -> None:
application = CuraApplication.getInstance() application = CuraApplication.getInstance()
material_manager = application.getMaterialManager() material_manager = application.getMaterialManager()
quality_manager = application.getQualityManager()
machine_manager = application.getMachineManager() machine_manager = application.getMachineManager()
container_tree = ContainerTree.getInstance()
for global_stack, extruder_nr, container_id in self._package_used_materials: for global_stack, extruder_nr, container_id in self._package_used_materials:
default_material_node = material_manager.getDefaultMaterial(global_stack, extruder_nr, global_stack.extruders[extruder_nr].variant.getName()) default_material_node = material_manager.getDefaultMaterial(global_stack, extruder_nr, global_stack.extruders[extruder_nr].variant.getName())
machine_manager.setMaterial(extruder_nr, default_material_node, global_stack = global_stack) machine_manager.setMaterial(extruder_nr, default_material_node, global_stack = global_stack)
for global_stack, extruder_nr, container_id in self._package_used_qualities: for global_stack, extruder_nr, container_id in self._package_used_qualities:
default_quality_group = quality_manager.getDefaultQualityType(global_stack) variant_names = [extruder.variant.getName() for extruder in global_stack.extruders.values()]
material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in global_stack.extruders.values()]
extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruders.values()]
definition_id = global_stack.definition.getId()
machine_node = container_tree.machines[definition_id]
default_quality_group = machine_node.getQualityGroups(variant_names, material_bases, extruder_enabled)[machine_node.preferred_quality_type]
machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack) machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack)
if self._package_id_to_uninstall is not None: if self._package_id_to_uninstall is not None:

View file

@ -223,10 +223,8 @@ class XmlMaterialProfile(InstanceContainer):
for instance in self.findInstances(): for instance in self.findInstances():
self._addSettingElement(builder, instance) self._addSettingElement(builder, instance)
machine_container_map = {} # type: Dict[str, InstanceContainer] machine_container_map = {} # type: Dict[str, InstanceContainer]
machine_variant_map = {} # type: Dict[str, Dict[str, Any]] machine_variant_map = {} # type: Dict[str, Dict[str, Any]]
variant_manager = CuraApplication.getInstance().getVariantManager()
root_material_id = self.getMetaDataEntry("base_file") # if basefile is self.getId, this is a basefile. root_material_id = self.getMetaDataEntry("base_file") # if basefile is self.getId, this is a basefile.
all_containers = registry.findInstanceContainers(base_file = root_material_id) all_containers = registry.findInstanceContainers(base_file = root_material_id)
@ -243,16 +241,13 @@ class XmlMaterialProfile(InstanceContainer):
machine_variant_map[definition_id] = {} machine_variant_map[definition_id] = {}
variant_name = container.getMetaDataEntry("variant_name") variant_name = container.getMetaDataEntry("variant_name")
if variant_name: if not variant_name:
variant_node = variant_manager.getVariantNode(definition_id, variant_name) machine_container_map[definition_id] = container
if variant_node is None:
continue
variant_dict = {"variant_node":variant_node ,
"material_container": container}
machine_variant_map[definition_id][variant_name] = variant_dict
continue continue
machine_container_map[definition_id] = container variant_dict = {"variant_type": container.getMetaDataEntry("hardware_type", "nozzle"),
"material_container": container}
machine_variant_map[definition_id][variant_name] = variant_dict
# Map machine human-readable names to IDs # Map machine human-readable names to IDs
product_id_map = self.getProductIdMap() product_id_map = self.getProductIdMap()
@ -285,8 +280,7 @@ class XmlMaterialProfile(InstanceContainer):
# Find all hotend sub-profiles corresponding to this material and machine and add them to this profile. # Find all hotend sub-profiles corresponding to this material and machine and add them to this profile.
buildplate_dict = {} # type: Dict[str, Any] buildplate_dict = {} # type: Dict[str, Any]
for variant_name, variant_dict in machine_variant_map[definition_id].items(): for variant_name, variant_dict in machine_variant_map[definition_id].items():
variant_type = variant_dict["variant_node"].getMetaDataEntry("hardware_type", str(VariantType.NOZZLE)) variant_type = VariantType(variant_dict["variant_type"])
variant_type = VariantType(variant_type)
if variant_type == VariantType.NOZZLE: if variant_type == VariantType.NOZZLE:
# The hotend identifier is not the containers name, but its "name". # The hotend identifier is not the containers name, but its "name".
builder.start("hotend", {"id": variant_name}) builder.start("hotend", {"id": variant_name})
@ -349,7 +343,7 @@ class XmlMaterialProfile(InstanceContainer):
stream = io.BytesIO() stream = io.BytesIO()
tree = ET.ElementTree(root) tree = ET.ElementTree(root)
# this makes sure that the XML header states encoding="utf-8" # this makes sure that the XML header states encoding="utf-8"
tree.write(stream, encoding = "utf-8", xml_declaration=True) tree.write(stream, encoding = "utf-8", xml_declaration = True)
return stream.getvalue().decode("utf-8") return stream.getvalue().decode("utf-8")

View file

@ -7,7 +7,7 @@ import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2 import QtQuick.Dialogs 1.2
import UM 1.2 as UM import UM 1.2 as UM
import Cura 1.0 as Cura import Cura 1.5 as Cura
Item Item
{ {
@ -17,7 +17,7 @@ Item
property var resetEnabled: false property var resetEnabled: false
property var currentItem: null property var currentItem: null
property var materialManager: CuraApplication.getMaterialManager() property var materialManagementModel: CuraApplication.getMaterialManagementModel()
property var hasCurrentItem: base.currentItem != null property var hasCurrentItem: base.currentItem != null
property var isCurrentItemActivated: property var isCurrentItemActivated:
@ -121,7 +121,7 @@ Item
onClicked: onClicked:
{ {
forceActiveFocus(); forceActiveFocus();
base.newRootMaterialIdToSwitchTo = base.materialManager.createMaterial(); base.newRootMaterialIdToSwitchTo = base.materialManagementModel.createMaterial();
base.toActivateNewMaterial = true; base.toActivateNewMaterial = true;
} }
} }
@ -136,7 +136,7 @@ Item
onClicked: onClicked:
{ {
forceActiveFocus(); forceActiveFocus();
base.newRootMaterialIdToSwitchTo = base.materialManager.duplicateMaterial(base.currentItem.container_node); base.newRootMaterialIdToSwitchTo = base.materialManagementModel.duplicateMaterial(base.currentItem.container_node);
base.toActivateNewMaterial = true; base.toActivateNewMaterial = true;
} }
} }
@ -147,7 +147,7 @@ Item
id: removeMenuButton id: removeMenuButton
text: catalog.i18nc("@action:button", "Remove") text: catalog.i18nc("@action:button", "Remove")
iconName: "list-remove" iconName: "list-remove"
enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated && base.materialManager.canMaterialBeRemoved(base.currentItem.container_node) enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated && base.materialManagementModel.canMaterialBeRemoved(base.currentItem.container_node)
onClicked: onClicked:
{ {
@ -297,7 +297,7 @@ Item
{ {
// Set the active material as the fallback. It will be selected when the current material is deleted // Set the active material as the fallback. It will be selected when the current material is deleted
base.newRootMaterialIdToSwitchTo = base.active_root_material_id base.newRootMaterialIdToSwitchTo = base.active_root_material_id
base.materialManager.removeMaterial(base.currentItem.container_node); base.materialManagementModel.removeMaterial(base.currentItem.container_node);
} }
} }

View file

@ -23,6 +23,7 @@ TabView
property real secondColumnWidth: (width * 0.40) | 0 property real secondColumnWidth: (width * 0.40) | 0
property string containerId: "" property string containerId: ""
property var materialPreferenceValues: UM.Preferences.getValue("cura/material_settings") ? JSON.parse(UM.Preferences.getValue("cura/material_settings")) : {} property var materialPreferenceValues: UM.Preferences.getValue("cura/material_settings") ? JSON.parse(UM.Preferences.getValue("cura/material_settings")) : {}
property var materialManagementModel: CuraApplication.getMaterialManagementModel()
property double spoolLength: calculateSpoolLength() property double spoolLength: calculateSpoolLength()
property real costPerMeter: calculateCostPerMeter() property real costPerMeter: calculateCostPerMeter()
@ -565,7 +566,7 @@ TabView
} }
// update the values // update the values
CuraApplication.getMaterialManager().setMaterialName(base.currentMaterialNode, new_name) base.materialManagementModel.setMaterialName(base.currentMaterialNode, new_name)
properties.name = new_name properties.name = new_name
} }

View file

@ -17,13 +17,10 @@ Item
property QtObject qualityManager: CuraApplication.getQualityManager() property QtObject qualityManager: CuraApplication.getQualityManager()
property var resetEnabled: false // Keep PreferencesDialog happy property var resetEnabled: false // Keep PreferencesDialog happy
property var extrudersModel: CuraApplication.getExtrudersModel() property var extrudersModel: CuraApplication.getExtrudersModel()
property var qualityManagementModel: CuraApplication.getQualityManagementModel()
UM.I18nCatalog { id: catalog; name: "cura"; } UM.I18nCatalog { id: catalog; name: "cura"; }
Cura.QualityManagementModel {
id: qualitiesModel
}
Label { Label {
id: titleLabel id: titleLabel
anchors { anchors {
@ -40,7 +37,7 @@ Item
property var currentItem: { property var currentItem: {
var current_index = qualityListView.currentIndex; var current_index = qualityListView.currentIndex;
return (current_index == -1) ? null : qualitiesModel.getItem(current_index); return (current_index == -1) ? null : base.qualityManagementModel.getItem(current_index);
} }
property var currentItemName: hasCurrentItem ? base.currentItem.name : "" property var currentItemName: hasCurrentItem ? base.currentItem.name : ""
@ -195,7 +192,7 @@ Item
// This connection makes sure that we will switch to the correct quality after the model gets updated // This connection makes sure that we will switch to the correct quality after the model gets updated
Connections Connections
{ {
target: qualitiesModel target: base.qualityManagementModel
onItemsChanged: onItemsChanged:
{ {
var toSelectItemName = base.currentItem == null ? "" : base.currentItem.name; var toSelectItemName = base.currentItem == null ? "" : base.currentItem.name;
@ -208,9 +205,9 @@ Item
if (toSelectItemName != "") if (toSelectItemName != "")
{ {
// Select the required quality name if given // Select the required quality name if given
for (var idx = 0; idx < qualitiesModel.count; ++idx) for (var idx = 0; idx < base.qualityManagementModel.count; ++idx)
{ {
var item = qualitiesModel.getItem(idx); var item = base.qualityManagementModel.getItem(idx);
if (item.name == toSelectItemName) if (item.name == toSelectItemName)
{ {
// Switch to the newly created profile if needed // Switch to the newly created profile if needed
@ -257,7 +254,7 @@ Item
onYes: onYes:
{ {
base.qualityManager.removeQualityChangesGroup(base.currentItem.quality_changes_group); base.qualityManagementModel.removeQualityChangesGroup(base.currentItem.quality_changes_group);
// reset current item to the first if available // reset current item to the first if available
qualityListView.currentIndex = -1; // Reset selection. qualityListView.currentIndex = -1; // Reset selection.
} }
@ -282,7 +279,7 @@ Item
id: importDialog id: importDialog
title: catalog.i18nc("@title:window", "Import Profile") title: catalog.i18nc("@title:window", "Import Profile")
selectExisting: true selectExisting: true
nameFilters: qualitiesModel.getFileNameFilters("profile_reader") nameFilters: base.qualityManagementModel.getFileNameFilters("profile_reader")
folder: CuraApplication.getDefaultPath("dialog_profile_path") folder: CuraApplication.getDefaultPath("dialog_profile_path")
onAccepted: onAccepted:
{ {
@ -308,7 +305,7 @@ Item
id: exportDialog id: exportDialog
title: catalog.i18nc("@title:window", "Export Profile") title: catalog.i18nc("@title:window", "Export Profile")
selectExisting: false selectExisting: false
nameFilters: qualitiesModel.getFileNameFilters("profile_writer") nameFilters: base.qualityManagementModel.getFileNameFilters("profile_writer")
folder: CuraApplication.getDefaultPath("dialog_profile_path") folder: CuraApplication.getDefaultPath("dialog_profile_path")
onAccepted: onAccepted:
{ {
@ -390,16 +387,16 @@ Item
{ {
id: qualityListView id: qualityListView
model: qualitiesModel model: base.qualityManagementModel
Component.onCompleted: Component.onCompleted:
{ {
var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName; var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName;
// Select the required quality name if given // Select the required quality name if given
for (var idx = 0; idx < qualitiesModel.count; idx++) for (var idx = 0; idx < base.qualityManagementModel.count; idx++)
{ {
var item = qualitiesModel.getItem(idx); var item = base.qualityManagementModel.getItem(idx);
if (item.name == selectedItemName) if (item.name == selectedItemName)
{ {
currentIndex = idx; currentIndex = idx;

View file

@ -50,18 +50,11 @@ def test_getQualityGroups(quality_mocked_application):
assert "normal" in manager.getQualityGroups(mocked_stack) assert "normal" in manager.getQualityGroups(mocked_stack)
def test_getQualityGroupsForMachineDefinition(quality_mocked_application):
manager = QualityManager(quality_mocked_application)
manager.initialize()
assert "normal" in manager.getQualityGroupsForMachineDefinition(mocked_stack)
def test_getQualityChangesGroup(quality_mocked_application): def test_getQualityChangesGroup(quality_mocked_application):
manager = QualityManager(quality_mocked_application) manager = QualityManager(quality_mocked_application)
manager.initialize() manager.initialize()
assert "herp" in manager.getQualityChangesGroups(mocked_stack) assert "herp" in [qcg.name for qcg in manager.getQualityChangesGroups(mocked_stack)]
@pytest.mark.skip("Doesn't work on remote") @pytest.mark.skip("Doesn't work on remote")