mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 15:07:28 -06:00
Merge branch 'feature_intent_container_tree' of github.com:Ultimaker/Cura into feature_intent_interface
This commit is contained in:
commit
b00b8c8c58
27 changed files with 603 additions and 239 deletions
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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")
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()):
|
||||||
|
|
|
@ -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
|
||||||
|
|
169
cura/Machines/Models/MaterialManagementModel.py
Normal file
169
cura/Machines/Models/MaterialManagementModel.py
Normal 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
|
|
@ -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,
|
||||||
|
|
|
@ -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 = ""
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
#
|
#
|
||||||
|
|
|
@ -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"])
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue