mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-14 03:55:56 +08:00
Merge branch 'feature_intent_container_tree' of https://github.com/Ultimaker/Cura into feature_intent_container_tree
This commit is contained in:
commit
5039d8db05
@ -5,10 +5,15 @@ from UM.Logger import Logger
|
|||||||
from UM.Settings.ContainerRegistry import ContainerRegistry # To listen to containers being added.
|
from UM.Settings.ContainerRegistry import ContainerRegistry # To listen to containers being added.
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
from UM.Settings.Interfaces import ContainerInterface
|
from UM.Settings.Interfaces import ContainerInterface
|
||||||
|
import cura.CuraApplication # Imported like this to prevent circular dependencies.
|
||||||
from cura.Machines.MachineNode import MachineNode
|
from cura.Machines.MachineNode import MachineNode
|
||||||
|
|
||||||
from typing import Dict
|
from typing import Dict, List, TYPE_CHECKING
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from cura.Machines.QualityGroup import QualityGroup
|
||||||
|
|
||||||
## This class contains a look-up tree for which containers are available at
|
## This class contains a look-up tree for which containers are available at
|
||||||
# which stages of configuration.
|
# which stages of configuration.
|
||||||
#
|
#
|
||||||
@ -29,6 +34,21 @@ class ContainerTree:
|
|||||||
container_registry.containerAdded.connect(self._machineAdded)
|
container_registry.containerAdded.connect(self._machineAdded)
|
||||||
self._loadAll()
|
self._loadAll()
|
||||||
|
|
||||||
|
## Get the quality groups available for the currently activated printer.
|
||||||
|
#
|
||||||
|
# This contains all quality groups, enabled or disabled. To check whether
|
||||||
|
# the quality group can be activated, test for the
|
||||||
|
# ``QualityGroup.is_available`` property.
|
||||||
|
# \return For every quality type, one quality group.
|
||||||
|
def getCurrentQualityGroups(self) -> Dict[str, "QualityGroup"]:
|
||||||
|
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
|
||||||
|
if global_stack is None:
|
||||||
|
return {}
|
||||||
|
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()]
|
||||||
|
return self.machines[global_stack.definition.getId()].getQualityGroups(variant_names, material_bases, extruder_enabled)
|
||||||
|
|
||||||
## Builds the initial container tree.
|
## Builds the initial container tree.
|
||||||
def _loadAll(self):
|
def _loadAll(self):
|
||||||
Logger.log("i", "Building container tree.")
|
Logger.log("i", "Building container tree.")
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
# 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
|
from typing import Dict, List
|
||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Util import parseBool
|
from UM.Util import parseBool
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry # To find all the variants for this machine.
|
from UM.Settings.ContainerRegistry import ContainerRegistry # To find all the variants for this machine.
|
||||||
from UM.Settings.Interfaces import ContainerInterface
|
|
||||||
from cura.Machines.ContainerNode import ContainerNode
|
from cura.Machines.ContainerNode import ContainerNode
|
||||||
|
from cura.Machines.QualityGroup import QualityGroup # To construct groups of quality profiles that belong together.
|
||||||
from cura.Machines.QualityNode import QualityNode
|
from cura.Machines.QualityNode import QualityNode
|
||||||
from cura.Machines.VariantNode import VariantNode
|
from cura.Machines.VariantNode import VariantNode
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
## This class represents a machine in the container tree.
|
## This class represents a machine in the container tree.
|
||||||
#
|
#
|
||||||
# The subnodes of these nodes are variants.
|
# The subnodes of these nodes are variants.
|
||||||
@ -36,10 +33,57 @@ class MachineNode(ContainerNode):
|
|||||||
self.quality_definition = my_metadata.get("quality_definition", container_id)
|
self.quality_definition = my_metadata.get("quality_definition", container_id)
|
||||||
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_quality_type = my_metadata.get("preferred_quality_type", "")
|
||||||
|
|
||||||
container_registry.containerAdded.connect(self._variantAdded)
|
|
||||||
self._loadAll()
|
self._loadAll()
|
||||||
|
|
||||||
|
## Get the available quality groups for this machine.
|
||||||
|
#
|
||||||
|
# This returns all quality groups, regardless of whether they are
|
||||||
|
# available to the combination of extruders or not. On the resulting
|
||||||
|
# quality groups, the is_available property is set to indicate whether the
|
||||||
|
# quality group can be selected according to the combination of extruders
|
||||||
|
# in the parameters.
|
||||||
|
# \param variant_names The names of the variants loaded in each extruder.
|
||||||
|
# \param material_bases The base file names of the materials loaded in
|
||||||
|
# each extruder.
|
||||||
|
# \param extruder_enabled Whether or not the extruders are enabled. This
|
||||||
|
# allows the function to set the is_available properly.
|
||||||
|
# \return For each available quality type, a QualityGroup instance.
|
||||||
|
def getQualityGroups(self, variant_names: List[str], material_bases: List[str], extruder_enabled: List[bool]) -> Dict[str, QualityGroup]:
|
||||||
|
if len(variant_names) != len(material_bases) or len(variant_names) != 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 {}
|
||||||
|
# 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]]
|
||||||
|
for extruder_nr, variant_name in enumerate(variant_names):
|
||||||
|
if not extruder_enabled[extruder_nr]:
|
||||||
|
continue # No qualities are available in this extruder. It'll get skipped when calculating the available quality types.
|
||||||
|
material_base = material_bases[extruder_nr]
|
||||||
|
if variant_name not in self.variants or material_base not in self.variants[variant_name].materials:
|
||||||
|
# The printer has no variant/material-specific quality profiles. Use the global quality profiles.
|
||||||
|
qualities_per_type_per_extruder[extruder_nr] = self.global_qualities
|
||||||
|
else:
|
||||||
|
# Use the actually specialised quality profiles.
|
||||||
|
qualities_per_type_per_extruder[extruder_nr] = self.variants[variant_name].materials[material_base].qualities
|
||||||
|
|
||||||
|
# Create the quality group for each available type.
|
||||||
|
quality_groups = {}
|
||||||
|
for quality_type, global_quality_node in self.global_qualities.items():
|
||||||
|
quality_groups[quality_type] = QualityGroup(name = global_quality_node.getMetaDataEntry("name", "Unnamed profile"), quality_type = quality_type)
|
||||||
|
quality_groups[quality_type].node_for_global = global_quality_node
|
||||||
|
for extruder, qualities_per_type in qualities_per_type_per_extruder:
|
||||||
|
quality_groups[quality_type].nodes_for_extruders[extruder] = qualities_per_type[quality_type]
|
||||||
|
|
||||||
|
available_quality_types = set(quality_groups.keys())
|
||||||
|
for extruder_nr, qualities_per_type in enumerate(qualities_per_type_per_extruder):
|
||||||
|
if not extruder_enabled[extruder_nr]:
|
||||||
|
continue
|
||||||
|
available_quality_types.intersection_update(qualities_per_type.keys())
|
||||||
|
for quality_type in available_quality_types:
|
||||||
|
quality_groups[quality_type].is_available = True
|
||||||
|
return quality_groups
|
||||||
|
|
||||||
## (Re)loads all variants under this printer.
|
## (Re)loads all variants under this printer.
|
||||||
def _loadAll(self):
|
def _loadAll(self):
|
||||||
# Find all the variants for this definition ID.
|
# Find all the variants for this definition ID.
|
||||||
@ -55,19 +99,4 @@ class MachineNode(ContainerNode):
|
|||||||
if len(global_qualities) == 0: # This printer doesn't override the global qualities.
|
if len(global_qualities) == 0: # This printer doesn't override the global qualities.
|
||||||
global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter", global_quality = True) # Otherwise pick the global global qualities.
|
global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter", global_quality = True) # Otherwise pick the global global qualities.
|
||||||
for global_quality in global_qualities:
|
for global_quality in global_qualities:
|
||||||
self.global_qualities[global_quality["quality_type"]] = QualityNode(global_quality["id"], parent = self)
|
self.global_qualities[global_quality["quality_type"]] = QualityNode(global_quality["id"], parent = self)
|
||||||
|
|
||||||
## When a variant gets added to the set of profiles, we need to update our
|
|
||||||
# tree here.
|
|
||||||
def _variantAdded(self, container: ContainerInterface):
|
|
||||||
if container.getMetaDataEntry("type") != "variant":
|
|
||||||
return # Not interested.
|
|
||||||
name = container.getMetaDataEntry("name")
|
|
||||||
if name in self.variants:
|
|
||||||
return # Already have this one.
|
|
||||||
if container.getMetaDataEntry("hardware_type") != "nozzle":
|
|
||||||
return # Only want nozzles in my tree.
|
|
||||||
if container.getMetaDataEntry("definition") != self.container_id:
|
|
||||||
return # Not a nozzle that fits in my machine.
|
|
||||||
|
|
||||||
self.variants[name] = VariantNode(container.getId(), machine = self)
|
|
@ -59,15 +59,6 @@ class MaterialManager(QObject):
|
|||||||
# Root_material_id -> MaterialGroup
|
# Root_material_id -> MaterialGroup
|
||||||
self._material_group_map = dict() # type: Dict[str, MaterialGroup]
|
self._material_group_map = dict() # type: Dict[str, MaterialGroup]
|
||||||
|
|
||||||
# Approximate diameter str
|
|
||||||
self._diameter_machine_nozzle_buildplate_material_map = dict() # type: Dict[str, Dict[str, MaterialNode]]
|
|
||||||
|
|
||||||
# We're using these two maps to convert between the specific diameter material id and the generic material id
|
|
||||||
# because the generic material ids are used in qualities and definitions, while the specific diameter material is meant
|
|
||||||
# i.e. generic_pla -> generic_pla_175
|
|
||||||
# root_material_id -> approximate diameter str -> root_material_id for that diameter
|
|
||||||
self._material_diameter_map = defaultdict(dict) # type: Dict[str, Dict[str, str]]
|
|
||||||
|
|
||||||
# Material id including diameter (generic_pla_175) -> material root id (generic_pla)
|
# Material id including diameter (generic_pla_175) -> material root id (generic_pla)
|
||||||
self._diameter_material_map = dict() # type: Dict[str, str]
|
self._diameter_material_map = dict() # type: Dict[str, str]
|
||||||
|
|
||||||
@ -75,11 +66,6 @@ class MaterialManager(QObject):
|
|||||||
# GUID -> a list of material_groups
|
# GUID -> a list of material_groups
|
||||||
self._guid_material_groups_map = defaultdict(list) # type: Dict[str, List[MaterialGroup]]
|
self._guid_material_groups_map = defaultdict(list) # type: Dict[str, List[MaterialGroup]]
|
||||||
|
|
||||||
# The machine definition ID for the non-machine-specific materials.
|
|
||||||
# This is used as the last fallback option if the given machine-specific material(s) cannot be found.
|
|
||||||
self._default_machine_definition_id = "fdmprinter"
|
|
||||||
self._default_approximate_diameter_for_quality_search = "3"
|
|
||||||
|
|
||||||
self._favorites = set(cura.CuraApplication.CuraApplication.getInstance().getPreferences().getValue("cura/favorite_materials").split(";"))
|
self._favorites = set(cura.CuraApplication.CuraApplication.getInstance().getPreferences().getValue("cura/favorite_materials").split(";"))
|
||||||
self.materialsUpdated.emit()
|
self.materialsUpdated.emit()
|
||||||
|
|
||||||
@ -87,7 +73,15 @@ class MaterialManager(QObject):
|
|||||||
return self._material_group_map.get(root_material_id)
|
return self._material_group_map.get(root_material_id)
|
||||||
|
|
||||||
def getRootMaterialIDForDiameter(self, root_material_id: str, approximate_diameter: str) -> str:
|
def getRootMaterialIDForDiameter(self, root_material_id: str, approximate_diameter: str) -> str:
|
||||||
return self._material_diameter_map.get(root_material_id, {}).get(approximate_diameter, root_material_id)
|
original_material = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(id=root_material_id)[0]
|
||||||
|
if original_material["approximate_diameter"] == approximate_diameter:
|
||||||
|
return root_material_id
|
||||||
|
|
||||||
|
matching_materials = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "material", brand = original_material["brand"], definition = original_material["definition"], material = original_material["material"], color_name = original_material["color_name"])
|
||||||
|
for material in matching_materials:
|
||||||
|
if material["approximate_diameter"] == approximate_diameter:
|
||||||
|
return material["id"]
|
||||||
|
return root_material_id
|
||||||
|
|
||||||
def getRootMaterialIDWithoutDiameter(self, root_material_id: str) -> str:
|
def getRootMaterialIDWithoutDiameter(self, root_material_id: str) -> str:
|
||||||
return self._diameter_material_map.get(root_material_id, "")
|
return self._diameter_material_map.get(root_material_id, "")
|
||||||
@ -109,13 +103,16 @@ class MaterialManager(QObject):
|
|||||||
# A convenience function to get available materials for the given machine with the extruder position.
|
# A convenience function to get available materials for the given machine with the extruder position.
|
||||||
#
|
#
|
||||||
def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
|
def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
|
||||||
extruder_stack: "ExtruderStack") -> Optional[Dict[str, MaterialNode]]:
|
extruder_stack: "ExtruderStack") -> Dict[str, MaterialNode]:
|
||||||
nozzle_name = None
|
nozzle_name = None
|
||||||
if extruder_stack.variant.getId() != "empty_variant":
|
if extruder_stack.variant.getId() != "empty_variant":
|
||||||
nozzle_name = extruder_stack.variant.getName()
|
nozzle_name = extruder_stack.variant.getName()
|
||||||
|
|
||||||
# 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.
|
||||||
return self.getAvailableMaterials(machine.definition.getId(), nozzle_name)
|
materials =self.getAvailableMaterials(machine.definition.getId(), nozzle_name)
|
||||||
|
compatible_material_diameter = str(round(extruder_stack.getCompatibleMaterialDiameter()))
|
||||||
|
|
||||||
|
return {key: material for key, material in materials.items() if material.getMetaDataEntry("approximate_diameter") == compatible_material_diameter}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Gets MaterialNode for the given extruder and machine with the given material name.
|
# Gets MaterialNode for the given extruder and machine with the given material name.
|
||||||
@ -152,27 +149,10 @@ class MaterialManager(QObject):
|
|||||||
#
|
#
|
||||||
def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, nozzle_name: str,
|
def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, nozzle_name: str,
|
||||||
buildplate_name: Optional[str], material_guid: str) -> Optional["MaterialNode"]:
|
buildplate_name: Optional[str], material_guid: str) -> Optional["MaterialNode"]:
|
||||||
node = None
|
|
||||||
machine_definition = global_stack.definition
|
machine_definition = global_stack.definition
|
||||||
extruder_definition = global_stack.extruders[position].definition
|
variant_name = global_stack.extruders[position].variant.getName()
|
||||||
if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
|
|
||||||
material_diameter = extruder_definition.getProperty("material_diameter", "value")
|
|
||||||
if isinstance(material_diameter, SettingFunction):
|
|
||||||
material_diameter = material_diameter(global_stack)
|
|
||||||
|
|
||||||
# Look at the guid to material dictionary
|
return self.getMaterialNode(machine_definition.getId(), variant_name, buildplate_name, 3, material_guid)
|
||||||
root_material_id = None
|
|
||||||
for material_group in self._guid_material_groups_map[material_guid]:
|
|
||||||
root_material_id = cast(str, material_group.root_material_node.getMetaDataEntry("id", ""))
|
|
||||||
break
|
|
||||||
|
|
||||||
if not root_material_id:
|
|
||||||
Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
|
|
||||||
return None
|
|
||||||
|
|
||||||
node = self.getMaterialNode(machine_definition.getId(), nozzle_name, buildplate_name,
|
|
||||||
material_diameter, root_material_id)
|
|
||||||
return node
|
|
||||||
|
|
||||||
# There are 2 ways to get fallback materials;
|
# There are 2 ways to get fallback materials;
|
||||||
# - A fallback by type (@sa getFallbackMaterialIdByMaterialType), which adds the generic version of this material
|
# - A fallback by type (@sa getFallbackMaterialIdByMaterialType), which adds the generic version of this material
|
||||||
@ -254,41 +234,21 @@ class MaterialManager(QObject):
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
def removeMaterialByRootId(self, root_material_id: str):
|
def removeMaterialByRootId(self, root_material_id: str):
|
||||||
material_group = self.getMaterialGroup(root_material_id)
|
|
||||||
if not material_group:
|
|
||||||
Logger.log("i", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
container_registry = CuraContainerRegistry.getInstance()
|
container_registry = CuraContainerRegistry.getInstance()
|
||||||
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
|
results = container_registry.findContainers(id=root_material_id)
|
||||||
# Sort all nodes with respect to the container ID lengths in the ascending order so the base material container
|
if not results:
|
||||||
# will be the first one to be removed. We need to do this to ensure that all containers get loaded & deleted.
|
container_registry.addWrongContainerId(root_material_id)
|
||||||
nodes_to_remove = sorted(nodes_to_remove, key = lambda x: len(x.getMetaDataEntry("id", "")))
|
|
||||||
# Try to load all containers first. If there is any faulty ones, they will be put into the faulty container
|
for result in results:
|
||||||
# list, so removeContainer() can ignore those ones.
|
container_registry.removeContainer(result.getMetaDataEntry("id", ""))
|
||||||
for node in nodes_to_remove:
|
|
||||||
container_id = node.getMetaDataEntry("id", "")
|
|
||||||
results = container_registry.findContainers(id = container_id)
|
|
||||||
if not results:
|
|
||||||
container_registry.addWrongContainerId(container_id)
|
|
||||||
for node in nodes_to_remove:
|
|
||||||
container_registry.removeContainer(node.getMetaDataEntry("id", ""))
|
|
||||||
|
|
||||||
#
|
|
||||||
# Methods for GUI
|
|
||||||
#
|
|
||||||
@pyqtSlot("QVariant", result=bool)
|
@pyqtSlot("QVariant", result=bool)
|
||||||
def canMaterialBeRemoved(self, material_node: "MaterialNode"):
|
def canMaterialBeRemoved(self, material_node: "MaterialNode"):
|
||||||
# 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.getMetaDataEntry("base_file")
|
root_material_id = material_node.getMetaDataEntry("base_file")
|
||||||
material_group = self.getMaterialGroup(root_material_id)
|
ids_to_remove = [metadata.get("id", "") for metadata in CuraContainerRegistry.getInstance().findInstanceContainersMetadata(base_file=root_material_id)]
|
||||||
if not material_group:
|
|
||||||
return False
|
|
||||||
|
|
||||||
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
|
|
||||||
ids_to_remove = [node.getMetaDataEntry("id", "") for node in nodes_to_remove]
|
|
||||||
|
|
||||||
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:
|
||||||
@ -303,38 +263,24 @@ class MaterialManager(QObject):
|
|||||||
if CuraContainerRegistry.getInstance().isReadOnly(root_material_id):
|
if CuraContainerRegistry.getInstance().isReadOnly(root_material_id):
|
||||||
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
|
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
|
||||||
return
|
return
|
||||||
|
containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = root_material_id)
|
||||||
|
containers[0].setName(name)
|
||||||
|
|
||||||
material_group = self.getMaterialGroup(root_material_id)
|
|
||||||
if material_group:
|
|
||||||
container = material_group.root_material_node.container
|
|
||||||
if container:
|
|
||||||
container.setName(name)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Removes the given material.
|
|
||||||
#
|
|
||||||
@pyqtSlot("QVariant")
|
@pyqtSlot("QVariant")
|
||||||
def removeMaterial(self, material_node: "MaterialNode") -> None:
|
def removeMaterial(self, material_node: "MaterialNode") -> None:
|
||||||
root_material_id = material_node.getMetaDataEntry("base_file")
|
root_material_id = material_node.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)
|
||||||
|
|
||||||
#
|
def duplicateMaterialByRootId(self, root_material_id, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
|
||||||
# Creates a duplicate of a material, which has the same GUID and base_file metadata.
|
container_registry = CuraContainerRegistry.getInstance()
|
||||||
# Returns the root material ID of the duplicated material if successful.
|
results = container_registry.findContainers(id=root_material_id)
|
||||||
#
|
|
||||||
@pyqtSlot("QVariant", result = str)
|
|
||||||
def duplicateMaterial(self, material_node: MaterialNode, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
|
|
||||||
root_material_id = cast(str, material_node.getMetaDataEntry("base_file", ""))
|
|
||||||
|
|
||||||
material_group = self.getMaterialGroup(root_material_id)
|
if not results:
|
||||||
if not material_group:
|
|
||||||
Logger.log("i", "Unable to duplicate the material with id %s, because it doesn't exist.", root_material_id)
|
Logger.log("i", "Unable to duplicate the material with id %s, because it doesn't exist.", root_material_id)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
base_container = material_group.root_material_node.container
|
base_container = results[0]
|
||||||
if not base_container:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Ensure all settings are saved.
|
# Ensure all settings are saved.
|
||||||
cura.CuraApplication.CuraApplication.getInstance().saveSettings()
|
cura.CuraApplication.CuraApplication.getInstance().saveSettings()
|
||||||
@ -353,11 +299,9 @@ class MaterialManager(QObject):
|
|||||||
new_containers.append(new_base_container)
|
new_containers.append(new_base_container)
|
||||||
|
|
||||||
# Clone all of them.
|
# Clone all of them.
|
||||||
for node in material_group.derived_material_node_list:
|
for container_to_copy in container_registry.findContainers(base_file=root_material_id):
|
||||||
container_to_copy = node.container
|
if container_to_copy.getId() == root_material_id:
|
||||||
if not container_to_copy:
|
continue # We already have that one, skip it
|
||||||
continue
|
|
||||||
# Create unique IDs for every clone.
|
|
||||||
new_id = new_base_id
|
new_id = new_base_id
|
||||||
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
|
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
|
||||||
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
|
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
|
||||||
@ -371,7 +315,6 @@ class MaterialManager(QObject):
|
|||||||
if new_metadata is not None:
|
if new_metadata is not None:
|
||||||
for key, value in new_metadata.items():
|
for key, value in new_metadata.items():
|
||||||
new_container.getMetaData()[key] = value
|
new_container.getMetaData()[key] = value
|
||||||
|
|
||||||
new_containers.append(new_container)
|
new_containers.append(new_container)
|
||||||
|
|
||||||
for container_to_add in new_containers:
|
for container_to_add in new_containers:
|
||||||
@ -383,8 +326,15 @@ class MaterialManager(QObject):
|
|||||||
self.addFavorite(new_base_id)
|
self.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.
|
||||||
|
# Returns the root material ID of the duplicated material if successful.
|
||||||
|
#
|
||||||
|
@pyqtSlot("QVariant", result = str)
|
||||||
|
def duplicateMaterial(self, material_node: MaterialNode, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
|
||||||
|
root_material_id = cast(str, material_node.getMetaDataEntry("base_file", ""))
|
||||||
|
return self.duplicateMaterialByRootId(root_material_id, new_base_id, new_metadata)
|
||||||
|
|
||||||
# Create a new material by cloning Generic PLA for the current material diameter and generate a new GUID.
|
# Create a new material by cloning Generic PLA for the current material diameter and generate a new GUID.
|
||||||
# Returns the ID of the newly created material.
|
# Returns the ID of the newly created material.
|
||||||
@pyqtSlot(result = str)
|
@pyqtSlot(result = str)
|
||||||
@ -403,11 +353,6 @@ class MaterialManager(QObject):
|
|||||||
|
|
||||||
approximate_diameter = str(extruder_stack.approximateMaterialDiameter)
|
approximate_diameter = str(extruder_stack.approximateMaterialDiameter)
|
||||||
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter)
|
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter)
|
||||||
material_group = self.getMaterialGroup(root_material_id)
|
|
||||||
|
|
||||||
if not material_group: # This should never happen
|
|
||||||
Logger.log("w", "Cannot get the material group of %s.", root_material_id)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
# Create a new ID & container to hold the data.
|
# Create a new ID & container to hold the data.
|
||||||
new_id = CuraContainerRegistry.getInstance().uniqueName("custom_material")
|
new_id = CuraContainerRegistry.getInstance().uniqueName("custom_material")
|
||||||
@ -416,9 +361,7 @@ class MaterialManager(QObject):
|
|||||||
"GUID": str(uuid.uuid4()),
|
"GUID": str(uuid.uuid4()),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.duplicateMaterial(material_group.root_material_node,
|
self.duplicateMaterialByRootId(root_material_id, new_base_id = new_id, new_metadata = new_metadata)
|
||||||
new_base_id = new_id,
|
|
||||||
new_metadata = new_metadata)
|
|
||||||
return new_id
|
return new_id
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
|
@ -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
|
from typing import Any, TYPE_CHECKING
|
||||||
|
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from UM.Settings.Interfaces import ContainerInterface
|
from UM.Settings.Interfaces import ContainerInterface
|
||||||
@ -23,8 +23,11 @@ class MaterialNode(ContainerNode):
|
|||||||
container_registry = ContainerRegistry.getInstance()
|
container_registry = ContainerRegistry.getInstance()
|
||||||
my_metadata = container_registry.findContainersMetadata(id = container_id)[0]
|
my_metadata = container_registry.findContainersMetadata(id = container_id)[0]
|
||||||
self.base_file = my_metadata["base_file"]
|
self.base_file = my_metadata["base_file"]
|
||||||
container_registry.containerAdded.connect(self._qualityAdded)
|
self.material_type = my_metadata["material"]
|
||||||
|
self.guid = my_metadata["GUID"]
|
||||||
self._loadAll()
|
self._loadAll()
|
||||||
|
container_registry.containerRemoved.connect(self._onRemoved)
|
||||||
|
container_registry.containerMetaDataChanged.connect(self._onMetadataChanged)
|
||||||
|
|
||||||
def _loadAll(self) -> None:
|
def _loadAll(self) -> None:
|
||||||
container_registry = ContainerRegistry.getInstance()
|
container_registry = ContainerRegistry.getInstance()
|
||||||
@ -33,14 +36,13 @@ 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_metadata = container_registry.findInstanceContainersMetadata(id = self.container_id)[0]
|
my_material_type = self.material_type
|
||||||
my_material_type = my_metadata.get("material")
|
|
||||||
qualities = []
|
qualities = []
|
||||||
qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition, variant = self.variant.variant_name)
|
qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition, variant = self.variant.variant_name)
|
||||||
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type):
|
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type):
|
||||||
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.
|
if not qualities: # No quality profiles found. Go by GUID then.
|
||||||
my_guid = my_metadata.get("material")
|
my_guid = self.guid
|
||||||
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", guid = my_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"]))
|
||||||
|
|
||||||
@ -49,37 +51,37 @@ class MaterialNode(ContainerNode):
|
|||||||
if quality_id not in self.qualities:
|
if quality_id not in self.qualities:
|
||||||
self.qualities[quality_id] = QualityNode(quality_id, parent = self)
|
self.qualities[quality_id] = QualityNode(quality_id, parent = self)
|
||||||
|
|
||||||
def _qualityAdded(self, container: ContainerInterface) -> None:
|
## Triggered when any container is removed, but only handles it when the
|
||||||
if container.getMetaDataEntry("type") != "quality":
|
# container is removed that this node represents.
|
||||||
return # Not interested.
|
# \param container The container that was allegedly removed.
|
||||||
if not self.variant.machine.has_machine_quality:
|
def _onRemoved(self, container: ContainerInterface) -> None:
|
||||||
if container.getMetaDataEntry("definition") != "fdmprinter":
|
if container.getId() == self.container_id:
|
||||||
return # Only want global qualities.
|
# Remove myself from my parent.
|
||||||
else:
|
if self.base_file in self.variant.materials:
|
||||||
if container.getMetaDataEntry("definition") != self.variant.machine.quality_definition:
|
del self.variant.materials[self.base_file]
|
||||||
return # Doesn't match the machine.
|
|
||||||
if container.getMetaDataEntry("variant") != self.variant.variant_name:
|
|
||||||
return # Doesn't match the variant.
|
|
||||||
# Detect if we're falling back to matching via GUID.
|
|
||||||
# If so, we might need to erase the current list and put just this one in (i.e. no longer use the fallback).
|
|
||||||
container_registry = ContainerRegistry.getInstance()
|
|
||||||
my_metadata = container_registry.findInstanceContainersMetadata(id = self.container_id)[0]
|
|
||||||
my_material_type = my_metadata.get("material")
|
|
||||||
allowed_material_ids = {metadata["id"] for metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type)}
|
|
||||||
# Select any quality profile; if the material is not matching by material type, we've been falling back to GUID all along.
|
|
||||||
is_fallback_guid = len(self.qualities) == 0 or next(iter(self.qualities.values())).getMetaDataEntry("material") not in allowed_material_ids
|
|
||||||
|
|
||||||
if is_fallback_guid and container.getMetaDataEntry("material") in allowed_material_ids: # So far we needed the fallback, but no longer!
|
## Triggered when any metadata changed in any container, but only handles
|
||||||
self.qualities.clear() # It'll get filled with the new quality profile then.
|
# it when the metadata of this node is changed.
|
||||||
else:
|
# \param container The container whose metadata changed.
|
||||||
if not is_fallback_guid:
|
# \param kwargs Key-word arguments provided when changing the metadata.
|
||||||
if container.getMetaDataEntry("material") not in allowed_material_ids:
|
# These are ignored. As far as I know they are never provided to this
|
||||||
return # Doesn't match the material type.
|
# call.
|
||||||
else:
|
def _onMetadataChanged(self, container: ContainerInterface, **kwargs: Any) -> None:
|
||||||
my_material_guid = my_metadata.get("GUID")
|
if container.getId() != self.container_id:
|
||||||
allowed_material_ids = {metadata["id"] for metadata in container_registry.findInstanceContainersMetadata(type = "material", guid = my_material_guid)}
|
return
|
||||||
if container.getMetaDataEntry("material") not in allowed_material_ids:
|
|
||||||
return # Doesn't match the material GUID.
|
|
||||||
|
|
||||||
quality_id = container.getId()
|
new_metadata = container.getMetaData()
|
||||||
self.qualities[quality_id] = QualityNode(quality_id, parent = self)
|
old_base_file = self.base_file
|
||||||
|
if new_metadata["base_file"] != old_base_file:
|
||||||
|
self.base_file = new_metadata["base_file"]
|
||||||
|
if old_base_file in self.variant.materials: # Move in parent node.
|
||||||
|
del self.variant.materials[old_base_file]
|
||||||
|
self.variant.materials[self.base_file] = self
|
||||||
|
|
||||||
|
old_material_type = self.material_type
|
||||||
|
self.material_type = new_metadata["material"]
|
||||||
|
old_guid = self.guid
|
||||||
|
self.guid = new_metadata["GUID"]
|
||||||
|
if self.base_file != old_base_file or self.material_type != old_material_type or self.guid != old_guid: # List of quality profiles could've changed.
|
||||||
|
self.qualities = {}
|
||||||
|
self._loadAll() # Re-load the quality profiles for this node.
|
@ -34,9 +34,6 @@ class BaseMaterialsModel(ListModel):
|
|||||||
|
|
||||||
# Update this model when switching machines
|
# Update this model when switching machines
|
||||||
self._machine_manager.activeStackChanged.connect(self._update)
|
self._machine_manager.activeStackChanged.connect(self._update)
|
||||||
|
|
||||||
# Update this model when list of materials changes
|
|
||||||
self._material_manager.materialsUpdated.connect(self._update)
|
|
||||||
|
|
||||||
self.addRoleName(Qt.UserRole + 1, "root_material_id")
|
self.addRoleName(Qt.UserRole + 1, "root_material_id")
|
||||||
self.addRoleName(Qt.UserRole + 2, "id")
|
self.addRoleName(Qt.UserRole + 2, "id")
|
||||||
@ -109,26 +106,24 @@ class BaseMaterialsModel(ListModel):
|
|||||||
# so it's placed here for easy access.
|
# so it's placed here for easy access.
|
||||||
def _canUpdate(self):
|
def _canUpdate(self):
|
||||||
global_stack = self._machine_manager.activeMachine
|
global_stack = self._machine_manager.activeMachine
|
||||||
|
|
||||||
if global_stack is None or not self._enabled:
|
if global_stack is None or not self._enabled:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
extruder_position = str(self._extruder_position)
|
extruder_position = str(self._extruder_position)
|
||||||
|
|
||||||
if extruder_position not in global_stack.extruders:
|
if extruder_position not in global_stack.extruders:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
extruder_stack = global_stack.extruders[extruder_position]
|
extruder_stack = global_stack.extruders[extruder_position]
|
||||||
self._available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack)
|
|
||||||
if self._available_materials is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
self._available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
## This is another convenience function which is shared by all material
|
## This is another convenience function which is shared by all material
|
||||||
# models so it's put here to avoid having so much duplicated code.
|
# models so it's put here to avoid having so much duplicated code.
|
||||||
def _createMaterialItem(self, root_material_id, container_node):
|
def _createMaterialItem(self, root_material_id, container_node):
|
||||||
metadata = CuraContainerRegistry.getInstance().findContainersMetadata(id = container_node.container_id)[0]
|
metadata_list = CuraContainerRegistry.getInstance().findContainersMetadata(id = container_node.container_id)
|
||||||
|
if not metadata_list:
|
||||||
|
return None
|
||||||
|
metadata = metadata_list[0]
|
||||||
item = {
|
item = {
|
||||||
"root_material_id": root_material_id,
|
"root_material_id": root_material_id,
|
||||||
"id": metadata["id"],
|
"id": metadata["id"],
|
||||||
|
@ -2,11 +2,20 @@
|
|||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
|
from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
|
||||||
|
import cura.CuraApplication # To listen to changes to the preferences.
|
||||||
|
|
||||||
## Model that shows the list of favorite materials.
|
## Model that shows the list of favorite materials.
|
||||||
class FavoriteMaterialsModel(BaseMaterialsModel):
|
class FavoriteMaterialsModel(BaseMaterialsModel):
|
||||||
def __init__(self, parent = None):
|
def __init__(self, parent = None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
cura.CuraApplication.CuraApplication.getInstance().getPreferences().preferenceChanged.connect(self._onFavoritesChanged)
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
## Triggered when any preference changes, but only handles it when the list
|
||||||
|
# of favourites is changed.
|
||||||
|
def _onFavoritesChanged(self, preference_key: str) -> None:
|
||||||
|
if preference_key != "cura/favorite_materials":
|
||||||
|
return
|
||||||
self._update()
|
self._update()
|
||||||
|
|
||||||
def _update(self):
|
def _update(self):
|
||||||
@ -28,7 +37,8 @@ class FavoriteMaterialsModel(BaseMaterialsModel):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
item = self._createMaterialItem(root_material_id, container_node)
|
item = self._createMaterialItem(root_material_id, container_node)
|
||||||
item_list.append(item)
|
if item:
|
||||||
|
item_list.append(item)
|
||||||
|
|
||||||
# Sort the item list alphabetically by name
|
# Sort the item list alphabetically by name
|
||||||
item_list = sorted(item_list, key = lambda d: d["brand"].upper())
|
item_list = sorted(item_list, key = lambda d: d["brand"].upper())
|
||||||
|
@ -24,11 +24,12 @@ class GenericMaterialsModel(BaseMaterialsModel):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Only add results for generic materials
|
# Only add results for generic materials
|
||||||
if container_node.getMetaDataEntry("brand").lower() != "generic":
|
if container_node.getMetaDataEntry("brand", "unknown").lower() != "generic":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item = self._createMaterialItem(root_material_id, container_node)
|
item = self._createMaterialItem(root_material_id, container_node)
|
||||||
item_list.append(item)
|
if item:
|
||||||
|
item_list.append(item)
|
||||||
|
|
||||||
# Sort the item list alphabetically by name
|
# Sort the item list alphabetically by name
|
||||||
item_list = sorted(item_list, key = lambda d: d["name"].upper())
|
item_list = sorted(item_list, key = lambda d: d["name"].upper())
|
||||||
|
@ -8,6 +8,7 @@ from PyQt5.QtCore import Qt, QObject, pyqtProperty, pyqtSignal
|
|||||||
from UM.Qt.ListModel import ListModel
|
from UM.Qt.ListModel import ListModel
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
|
|
||||||
|
from cura.Machines.ContainerTree import ContainerTree
|
||||||
from cura.Settings.IntentManager import IntentManager
|
from cura.Settings.IntentManager import IntentManager
|
||||||
import cura.CuraApplication
|
import cura.CuraApplication
|
||||||
|
|
||||||
@ -47,13 +48,11 @@ class IntentModel(ListModel):
|
|||||||
|
|
||||||
def _update(self) -> None:
|
def _update(self) -> None:
|
||||||
new_items = [] # type: List[Dict[str, Any]]
|
new_items = [] # type: List[Dict[str, Any]]
|
||||||
application = cura.CuraApplication.CuraApplication.getInstance()
|
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
|
||||||
quality_manager = application.getQualityManager()
|
|
||||||
global_stack = application.getGlobalContainerStack()
|
|
||||||
if not global_stack:
|
if not global_stack:
|
||||||
self.setItems(new_items)
|
self.setItems(new_items)
|
||||||
return
|
return
|
||||||
quality_groups = quality_manager.getQualityGroups(global_stack)
|
quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
|
||||||
|
|
||||||
for intent_category, quality_type in IntentManager.getInstance().getCurrentAvailableIntents():
|
for intent_category, quality_type in IntentManager.getInstance().getCurrentAvailableIntents():
|
||||||
if intent_category == self._intent_category:
|
if intent_category == self._intent_category:
|
||||||
|
@ -55,7 +55,8 @@ class MaterialBrandsModel(BaseMaterialsModel):
|
|||||||
|
|
||||||
# Now handle the individual materials
|
# Now handle the individual materials
|
||||||
item = self._createMaterialItem(root_material_id, container_node)
|
item = self._createMaterialItem(root_material_id, container_node)
|
||||||
brand_group_dict[brand][material_type].append(item)
|
if item:
|
||||||
|
brand_group_dict[brand][material_type].append(item)
|
||||||
|
|
||||||
# Part 2: Organize the tree into models
|
# Part 2: Organize the tree into models
|
||||||
#
|
#
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# 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 Qt, pyqtSlot
|
from PyQt5.QtCore import Qt, pyqtSlot
|
||||||
|
|
||||||
from UM.Qt.ListModel import ListModel
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
|
from UM.Qt.ListModel import ListModel
|
||||||
|
from cura.Machines.ContainerTree import ContainerTree
|
||||||
|
|
||||||
#
|
#
|
||||||
# This the QML model for the quality management page.
|
# This the QML model for the quality management page.
|
||||||
@ -42,7 +43,7 @@ class QualityManagementModel(ListModel):
|
|||||||
self.setItems([])
|
self.setItems([])
|
||||||
return
|
return
|
||||||
|
|
||||||
quality_group_dict = self._quality_manager.getQualityGroups(global_stack)
|
quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups()
|
||||||
quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(global_stack)
|
quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(global_stack)
|
||||||
|
|
||||||
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()
|
||||||
|
@ -105,37 +105,12 @@ class QualityManager(QObject):
|
|||||||
# \return A dictionary with quality types as keys and the quality groups
|
# \return A dictionary with quality types as keys and the quality groups
|
||||||
# for those types as values.
|
# for those types as values.
|
||||||
def getQualityGroups(self, global_stack: "GlobalStack") -> Dict[str, QualityGroup]:
|
def getQualityGroups(self, global_stack: "GlobalStack") -> Dict[str, QualityGroup]:
|
||||||
|
# Gather up the variant names and material base files for each extruder.
|
||||||
|
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()
|
definition_id = global_stack.definition.getId()
|
||||||
machine_node = ContainerTree.getInstance().machines[definition_id]
|
return ContainerTree.getInstance().machines[definition_id].getQualityGroups(variant_names, material_bases, extruder_enabled)
|
||||||
|
|
||||||
# For each extruder, find which quality profiles are available. Later we'll intersect the quality types.
|
|
||||||
qualities_per_type_per_extruder = {} # type: Dict[str, Dict[str, QualityNode]]
|
|
||||||
for extruder_nr, extruder in global_stack.extruders.items():
|
|
||||||
if not extruder.isEnabled:
|
|
||||||
continue # No qualities available in this extruder. It'll get skipped when intersecting the quality types.
|
|
||||||
nozzle_name = extruder.variant.getName()
|
|
||||||
material_base = extruder.material.getMetaDataEntry("base_file")
|
|
||||||
if nozzle_name not in machine_node.variants or material_base not in machine_node.variants[nozzle_name].materials:
|
|
||||||
# The printer has no variant/material-specific quality profiles. Use the global quality profiles.
|
|
||||||
qualities_per_type_per_extruder[extruder_nr] = machine_node.global_qualities
|
|
||||||
else:
|
|
||||||
# Use the actually specialised quality profiles.
|
|
||||||
qualities_per_type_per_extruder[extruder_nr] = machine_node.variants[nozzle_name].materials[material_base].qualities
|
|
||||||
|
|
||||||
# Create the quality group for each available type.
|
|
||||||
quality_groups = {}
|
|
||||||
for quality_type, global_quality_node in machine_node.global_qualities.items():
|
|
||||||
quality_groups[quality_type] = QualityGroup(name = global_quality_node.getMetaDataEntry("name", "Unnamed profile"), quality_type = quality_type)
|
|
||||||
quality_groups[quality_type].node_for_global = global_quality_node
|
|
||||||
for extruder, qualities_per_type in qualities_per_type_per_extruder:
|
|
||||||
quality_groups[quality_type].nodes_for_extruders[extruder] = qualities_per_type[quality_type]
|
|
||||||
|
|
||||||
available_quality_types = set(quality_groups.keys())
|
|
||||||
for qualities_per_type in qualities_per_type_per_extruder.values():
|
|
||||||
available_quality_types.intersection_update(qualities_per_type.keys())
|
|
||||||
for quality_type in available_quality_types:
|
|
||||||
quality_groups[quality_type].is_available = True
|
|
||||||
return quality_groups
|
|
||||||
|
|
||||||
def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> Dict[str, QualityGroup]:
|
def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> Dict[str, QualityGroup]:
|
||||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
|
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
|
||||||
@ -160,11 +135,22 @@ class QualityManager(QObject):
|
|||||||
|
|
||||||
return quality_group_dict
|
return quality_group_dict
|
||||||
|
|
||||||
|
## Get the quality group for the preferred quality type for a certain
|
||||||
|
# global stack.
|
||||||
|
#
|
||||||
|
# If the preferred quality type is not available, ``None`` will be
|
||||||
|
# returned.
|
||||||
|
# \param machine The global stack of the machine to get the preferred
|
||||||
|
# quality group for.
|
||||||
|
# \return The preferred quality group, or ``None`` if that is not
|
||||||
|
# available.
|
||||||
def getDefaultQualityType(self, machine: "GlobalStack") -> Optional[QualityGroup]:
|
def getDefaultQualityType(self, machine: "GlobalStack") -> Optional[QualityGroup]:
|
||||||
preferred_quality_type = machine.definition.getMetaDataEntry("preferred_quality_type")
|
machine_node = ContainerTree.getInstance().machines[machine.definition.getId()]
|
||||||
quality_group_dict = self.getQualityGroups(machine)
|
quality_groups = self.getQualityGroups(machine)
|
||||||
quality_group = quality_group_dict.get(preferred_quality_type)
|
result = quality_groups.get(machine_node.preferred_quality_type)
|
||||||
return quality_group
|
if result is not None and result.is_available:
|
||||||
|
return result
|
||||||
|
return None # If preferred quality type is not available, leave it up for the caller.
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
from typing import Union, TYPE_CHECKING
|
from typing import Union, TYPE_CHECKING
|
||||||
|
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from UM.Settings.Interfaces import ContainerInterface
|
|
||||||
from cura.Machines.ContainerNode import ContainerNode
|
from cura.Machines.ContainerNode import ContainerNode
|
||||||
from cura.Machines.IntentNode import IntentNode
|
from cura.Machines.IntentNode import IntentNode
|
||||||
|
|
||||||
@ -21,7 +20,6 @@ class QualityNode(ContainerNode):
|
|||||||
super().__init__(container_id)
|
super().__init__(container_id)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.intents = {} # type: Dict[str, IntentNode]
|
self.intents = {} # type: Dict[str, IntentNode]
|
||||||
ContainerRegistry.getInstance().containerAdded.connect(self._intentAdded)
|
|
||||||
self._loadAll()
|
self._loadAll()
|
||||||
|
|
||||||
def _loadAll(self) -> None:
|
def _loadAll(self) -> None:
|
||||||
@ -31,21 +29,4 @@ class QualityNode(ContainerNode):
|
|||||||
if not isinstance(self.parent, MachineNode): # Not a global profile.
|
if not isinstance(self.parent, MachineNode): # Not a global profile.
|
||||||
for intent in container_registry.findInstanceContainersMetadata(type = "intent", definition = self.parent.variant.machine.quality_definition, variant = self.parent.variant.variant_name, material = self.parent.base_file):
|
for intent in container_registry.findInstanceContainersMetadata(type = "intent", definition = self.parent.variant.machine.quality_definition, variant = self.parent.variant.variant_name, material = self.parent.base_file):
|
||||||
self.intents[intent["id"]] = IntentNode(intent["id"], quality = self)
|
self.intents[intent["id"]] = IntentNode(intent["id"], quality = self)
|
||||||
# Otherwise, there are no intents for global profiles.
|
# Otherwise, there are no intents for global profiles.
|
||||||
|
|
||||||
def _intentAdded(self, container: ContainerInterface) -> None:
|
|
||||||
from cura.Machines.MachineNode import MachineNode # Imported here to prevent circular imports.
|
|
||||||
if container.getMetaDataEntry("type") != "intent":
|
|
||||||
return # Not interested if it's not an intent.
|
|
||||||
if isinstance(self.parent, MachineNode):
|
|
||||||
return # Global profiles don't have intents.
|
|
||||||
if container.getMetaDataEntry("definition") != self.parent.variant.machine.quality_definition:
|
|
||||||
return # Incorrect printer.
|
|
||||||
if container.getMetaDataEntry("variant") != self.parent.variant.variant_name:
|
|
||||||
return # Incorrect variant.
|
|
||||||
if container.getMetaDataEntry("material") != self.parent.base_file:
|
|
||||||
return # Incorrect material.
|
|
||||||
container_id = container.getId()
|
|
||||||
if container_id in self.intents:
|
|
||||||
return # Already have this.
|
|
||||||
self.intents[container_id] = IntentNode(container_id, quality = self)
|
|
@ -115,7 +115,7 @@ class VariantManager:
|
|||||||
variant_type: Optional["VariantType"] = None) -> Optional["ContainerNode"]:
|
variant_type: Optional["VariantType"] = None) -> Optional["ContainerNode"]:
|
||||||
if variant_type is None:
|
if variant_type is None:
|
||||||
variant_node = None
|
variant_node = None
|
||||||
variant_type_dict = self._machine_to_variant_dict_map[machine_definition_id]
|
variant_type_dict = self._machine_to_variant_dict_map.get("machine_definition_id", {})
|
||||||
for variant_dict in variant_type_dict.values():
|
for variant_dict in variant_type_dict.values():
|
||||||
if variant_name in variant_dict:
|
if variant_name in variant_dict:
|
||||||
variant_node = variant_dict[variant_name]
|
variant_node = variant_dict[variant_name]
|
||||||
|
@ -9,7 +9,6 @@ from typing import Dict, Union, Any, TYPE_CHECKING, List
|
|||||||
from PyQt5.QtCore import QObject, QUrl
|
from PyQt5.QtCore import QObject, QUrl
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
|
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
from UM.FlameProfiler import pyqtSlot
|
from UM.FlameProfiler import pyqtSlot
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
@ -17,18 +16,19 @@ from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError
|
|||||||
from UM.Platform import Platform
|
from UM.Platform import Platform
|
||||||
from UM.SaveFile import SaveFile
|
from UM.SaveFile import SaveFile
|
||||||
from UM.Settings.ContainerFormatError import ContainerFormatError
|
from UM.Settings.ContainerFormatError import ContainerFormatError
|
||||||
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from UM.Settings.ContainerStack import ContainerStack
|
from UM.Settings.ContainerStack import ContainerStack
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
import cura.CuraApplication
|
import cura.CuraApplication
|
||||||
|
from cura.Machines.MaterialManager import MaterialManager
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
from cura.Machines.ContainerNode import ContainerNode
|
from cura.Machines.ContainerNode import ContainerNode
|
||||||
from cura.Machines.MaterialNode import MaterialNode
|
from cura.Machines.MaterialNode import MaterialNode
|
||||||
from cura.Machines.QualityChangesGroup import QualityChangesGroup
|
from cura.Machines.QualityChangesGroup import QualityChangesGroup
|
||||||
from cura.Machines.MaterialManager import MaterialManager
|
|
||||||
from cura.Machines.QualityManager import QualityManager
|
from cura.Machines.QualityManager import QualityManager
|
||||||
|
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
@ -323,22 +323,18 @@ class ContainerManager(QObject):
|
|||||||
|
|
||||||
## Get a list of materials that have the same GUID as the reference material
|
## Get a list of materials that have the same GUID as the reference material
|
||||||
#
|
#
|
||||||
# \param material_id \type{str} the id of the material for which to get the linked materials.
|
# \param material_node The node representing the material for which to get
|
||||||
# \return \type{list} a list of names of materials with the same GUID
|
# the same GUID.
|
||||||
|
# \param exclude_self Whether to include the name of the material you
|
||||||
|
# provided.
|
||||||
|
# \return A list of names of materials with the same GUID.
|
||||||
@pyqtSlot("QVariant", bool, result = "QStringList")
|
@pyqtSlot("QVariant", bool, result = "QStringList")
|
||||||
def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False):
|
def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False) -> List[str]:
|
||||||
guid = material_node.getMetaDataEntry("GUID", "")
|
same_guid = ContainerRegistry.getInstance().findInstanceContainersMetadata(guid = material_node.guid)
|
||||||
|
if exclude_self:
|
||||||
self_root_material_id = material_node.getMetaDataEntry("base_file")
|
return [metadata["name"] for metadata in same_guid if metadata["base_file"] != material_node.base_file]
|
||||||
material_group_list = MaterialManager.getInstance().getMaterialGroupListByGUID(guid)
|
else:
|
||||||
|
return [metadata["name"] for metadata in same_guid]
|
||||||
linked_material_names = []
|
|
||||||
if material_group_list:
|
|
||||||
for material_group in material_group_list:
|
|
||||||
if exclude_self and material_group.name == self_root_material_id:
|
|
||||||
continue
|
|
||||||
linked_material_names.append(material_group.root_material_node.getMetaDataEntry("name", ""))
|
|
||||||
return linked_material_names
|
|
||||||
|
|
||||||
## Unlink a material from all other materials by creating a new GUID
|
## Unlink a material from all other materials by creating a new GUID
|
||||||
# \param material_id \type{str} the id of the material to create a new GUID for.
|
# \param material_id \type{str} the id of the material to create a new GUID for.
|
||||||
|
@ -28,7 +28,6 @@ class CuraStackBuilder:
|
|||||||
def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
|
def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
application = CuraApplication.getInstance()
|
application = CuraApplication.getInstance()
|
||||||
quality_manager = application.getQualityManager()
|
|
||||||
registry = application.getContainerRegistry()
|
registry = application.getContainerRegistry()
|
||||||
|
|
||||||
definitions = registry.findDefinitionContainers(id = definition_id)
|
definitions = registry.findDefinitionContainers(id = definition_id)
|
||||||
@ -64,7 +63,7 @@ class CuraStackBuilder:
|
|||||||
registry.addContainer(new_extruder)
|
registry.addContainer(new_extruder)
|
||||||
|
|
||||||
preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
|
preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
|
||||||
quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
|
quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups()
|
||||||
if not quality_group_dict:
|
if not quality_group_dict:
|
||||||
# There is no available quality group, set all quality containers to empty.
|
# There is no available quality group, set all quality containers to empty.
|
||||||
new_global_stack.quality = application.empty_quality_container
|
new_global_stack.quality = application.empty_quality_container
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||||
from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING
|
from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING
|
||||||
import cura.CuraApplication
|
import cura.CuraApplication
|
||||||
|
from cura.Machines.ContainerTree import ContainerTree
|
||||||
from cura.Settings.cura_empty_instance_containers import empty_intent_container
|
from cura.Settings.cura_empty_instance_containers import empty_intent_container
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
|
|
||||||
@ -75,7 +76,8 @@ class IntentManager(QObject):
|
|||||||
# TODO: We now do this (return a default) if the global stack is missing, but not in the code below,
|
# TODO: We now do this (return a default) if the global stack is missing, but not in the code below,
|
||||||
# even though there should always be defaults. The problem then is what to do with the quality_types.
|
# even though there should always be defaults. The problem then is what to do with the quality_types.
|
||||||
# Currently _also_ inconsistent with 'currentAvailableIntentCategories', which _does_ return default.
|
# Currently _also_ inconsistent with 'currentAvailableIntentCategories', which _does_ return default.
|
||||||
quality_groups = application.getQualityManager().getQualityGroups(global_stack)
|
quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
|
||||||
|
# TODO: These quality nodes in that tree already contain the intent nodes. We can optimise this.
|
||||||
available_quality_types = {quality_group.quality_type for quality_group in quality_groups.values() if quality_group.node_for_global is not None}
|
available_quality_types = {quality_group.quality_type for quality_group in quality_groups.values() if quality_group.node_for_global is not None}
|
||||||
|
|
||||||
final_intent_ids = set() # type: Set[str]
|
final_intent_ids = set() # type: Set[str]
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
# 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 Optional
|
from typing import Optional
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from cura.PrinterOutput.Models.MaterialOutputModel import MaterialOutputModel
|
from cura.PrinterOutput.Models.MaterialOutputModel import MaterialOutputModel
|
||||||
|
|
||||||
from ..BaseModel import BaseModel
|
from ..BaseModel import BaseModel
|
||||||
@ -24,32 +25,27 @@ class ClusterPrinterConfigurationMaterial(BaseModel):
|
|||||||
self.material = material
|
self.material = material
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
## Creates a material output model based on this cloud printer material.
|
## Creates a material output model based on this cloud printer material.
|
||||||
|
#
|
||||||
|
# A material is chosen that matches the current GUID. If multiple such
|
||||||
|
# materials are available, read-only materials are preferred and the
|
||||||
|
# material with the earliest alphabetical name will be selected.
|
||||||
|
# \return A material output model that matches the current GUID.
|
||||||
def createOutputModel(self) -> MaterialOutputModel:
|
def createOutputModel(self) -> MaterialOutputModel:
|
||||||
material_manager = CuraApplication.getInstance().getMaterialManager()
|
container_registry = ContainerRegistry.getInstance()
|
||||||
material_group_list = material_manager.getMaterialGroupListByGUID(self.guid) or []
|
same_guid = container_registry.findInstanceContainersMetadata(GUID = self.guid)
|
||||||
|
if same_guid:
|
||||||
# Sort the material groups by "is_read_only = True" first, and then the name alphabetically.
|
read_only = sorted(filter(lambda metadata: container_registry.isReadOnly(metadata["id"]), same_guid), key = lambda metadata: metadata["name"])
|
||||||
read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list))
|
if read_only:
|
||||||
non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list))
|
material_metadata = read_only[0]
|
||||||
material_group = None
|
else:
|
||||||
if read_only_material_group_list:
|
material_metadata = min(same_guid, key = lambda metadata: metadata["name"])
|
||||||
read_only_material_group_list = sorted(read_only_material_group_list, key = lambda x: x.name)
|
|
||||||
material_group = read_only_material_group_list[0]
|
|
||||||
elif non_read_only_material_group_list:
|
|
||||||
non_read_only_material_group_list = sorted(non_read_only_material_group_list, key = lambda x: x.name)
|
|
||||||
material_group = non_read_only_material_group_list[0]
|
|
||||||
|
|
||||||
if material_group:
|
|
||||||
container = material_group.root_material_node.container
|
|
||||||
color = container.getMetaDataEntry("color_code")
|
|
||||||
brand = container.getMetaDataEntry("brand")
|
|
||||||
material_type = container.getMetaDataEntry("material")
|
|
||||||
name = container.getName()
|
|
||||||
else:
|
else:
|
||||||
color = self.color
|
material_metadata = {
|
||||||
brand = self.brand
|
"color_code": self.color,
|
||||||
material_type = self.material
|
"brand": self.brand,
|
||||||
name = "Empty" if self.material == "empty" else "Unknown"
|
"material": self.material,
|
||||||
|
"name": "Empty" if self.material == "empty" else "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
return MaterialOutputModel(guid=self.guid, type=material_type, brand=brand, color=color, name=name)
|
return MaterialOutputModel(guid = self.guid, type = material_metadata["material"], brand = material_metadata["brand"], color = material_metadata["color_code"], name = material_metadata["name"])
|
||||||
|
@ -244,7 +244,10 @@ class XmlMaterialProfile(InstanceContainer):
|
|||||||
|
|
||||||
variant_name = container.getMetaDataEntry("variant_name")
|
variant_name = container.getMetaDataEntry("variant_name")
|
||||||
if variant_name:
|
if variant_name:
|
||||||
variant_dict = {"variant_node": variant_manager.getVariantNode(definition_id, variant_name),
|
variant_node = variant_manager.getVariantNode(definition_id, variant_name)
|
||||||
|
if variant_node is None:
|
||||||
|
continue
|
||||||
|
variant_dict = {"variant_node":variant_node ,
|
||||||
"material_container": container}
|
"material_container": container}
|
||||||
machine_variant_map[definition_id][variant_name] = variant_dict
|
machine_variant_map[definition_id][variant_name] = variant_dict
|
||||||
continue
|
continue
|
||||||
|
@ -17,6 +17,8 @@ Item
|
|||||||
property var resetEnabled: false
|
property var resetEnabled: false
|
||||||
property var currentItem: null
|
property var currentItem: null
|
||||||
|
|
||||||
|
property var materialManager: CuraApplication.getMaterialManager()
|
||||||
|
|
||||||
property var hasCurrentItem: base.currentItem != null
|
property var hasCurrentItem: base.currentItem != null
|
||||||
property var isCurrentItemActivated:
|
property var isCurrentItemActivated:
|
||||||
{
|
{
|
||||||
@ -119,7 +121,7 @@ Item
|
|||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
forceActiveFocus();
|
forceActiveFocus();
|
||||||
base.newRootMaterialIdToSwitchTo = CuraApplication.getMaterialManager().createMaterial();
|
base.newRootMaterialIdToSwitchTo = base.materialManager.createMaterial();
|
||||||
base.toActivateNewMaterial = true;
|
base.toActivateNewMaterial = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +136,7 @@ Item
|
|||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
forceActiveFocus();
|
forceActiveFocus();
|
||||||
base.newRootMaterialIdToSwitchTo = CuraApplication.getMaterialManager().duplicateMaterial(base.currentItem.container_node);
|
base.newRootMaterialIdToSwitchTo = base.materialManager.duplicateMaterial(base.currentItem.container_node);
|
||||||
base.toActivateNewMaterial = true;
|
base.toActivateNewMaterial = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,7 +147,8 @@ 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 && CuraApplication.getMaterialManager().canMaterialBeRemoved(base.currentItem.container_node)
|
enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated && base.materialManager.canMaterialBeRemoved(base.currentItem.container_node)
|
||||||
|
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
forceActiveFocus();
|
forceActiveFocus();
|
||||||
@ -294,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
|
||||||
CuraApplication.getMaterialManager().removeMaterial(base.currentItem.container_node);
|
base.materialManager.removeMaterial(base.currentItem.container_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,6 @@ import pytest
|
|||||||
from UM.Settings.Interfaces import ContainerInterface
|
from UM.Settings.Interfaces import ContainerInterface
|
||||||
from cura.Machines.MachineNode import MachineNode
|
from cura.Machines.MachineNode import MachineNode
|
||||||
|
|
||||||
|
|
||||||
machine_node_variant_added_test_data = [({"type": "Not a variant!"}, ["Variant One", "Variant Two"]), # Wrong type
|
|
||||||
({"type": "variant", "name": "Variant One"}, ["Variant One", "Variant Two"]), # Name already added
|
|
||||||
({"type": "variant", "name": "Variant Three", "hardware_type": "Not a nozzle"}, ["Variant One", "Variant Two"]), # Wrong hardware type
|
|
||||||
({"type": "variant", "name": "Variant Three", "hardware_type": "nozzle", "definition": "machine_3"}, ["Variant One", "Variant Two"]), # Wrong definition ID
|
|
||||||
({"type": "variant", "name": "Variant Three", "hardware_type": "nozzle", "definition": "machine_1"}, ["Variant One", "Variant Two", "Variant Three"])] # Yay! It's finally added
|
|
||||||
|
|
||||||
|
|
||||||
metadata_dict = {}
|
metadata_dict = {}
|
||||||
|
|
||||||
|
|
||||||
@ -44,18 +36,4 @@ def test_machineNodeInit(container_registry):
|
|||||||
# As variants get stored by name, we want to check if those get added.
|
# As variants get stored by name, we want to check if those get added.
|
||||||
assert "Variant One" in machine_node.variants
|
assert "Variant One" in machine_node.variants
|
||||||
assert "Variant Two" in machine_node.variants
|
assert "Variant Two" in machine_node.variants
|
||||||
assert len(machine_node.variants) == 2 # And ensure that *only* those two got added.
|
assert len(machine_node.variants) == 2 # And ensure that *only* those two got added.
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("metadata,variant_result_list", machine_node_variant_added_test_data)
|
|
||||||
def test_machineNodeVariantAdded(container_registry, metadata, variant_result_list):
|
|
||||||
machine_node = createMachineNode("machine_1", container_registry)
|
|
||||||
|
|
||||||
with patch("cura.Machines.MachineNode.VariantNode"): # We're not testing the variant node here, so patch it out.
|
|
||||||
with patch.dict(metadata_dict, metadata):
|
|
||||||
mocked_container = createMockedInstanceContainer()
|
|
||||||
machine_node._variantAdded(mocked_container)
|
|
||||||
|
|
||||||
assert len(variant_result_list) == len(machine_node.variants)
|
|
||||||
for name in variant_result_list:
|
|
||||||
assert name in machine_node.variants
|
|
@ -6,21 +6,6 @@ from cura.Machines.MaterialNode import MaterialNode
|
|||||||
instance_container_metadata_dict = {"fdmprinter": {"no_variant": [{"id": "quality_1", "material": "material_1"}]},
|
instance_container_metadata_dict = {"fdmprinter": {"no_variant": [{"id": "quality_1", "material": "material_1"}]},
|
||||||
"machine_1": {"variant_1": {"material_1": [{"id": "quality_2", "material": "material_1"}, {"id": "quality_3","material": "material_1"}]}}}
|
"machine_1": {"variant_1": {"material_1": [{"id": "quality_2", "material": "material_1"}, {"id": "quality_3","material": "material_1"}]}}}
|
||||||
|
|
||||||
|
|
||||||
quality_metadata_machine_quality_test_data = [({"type": "Not a quality"}, ["quality_2", "quality_3"]), # Wrong type
|
|
||||||
({"type": "quality", "definition": "machine_2"}, ["quality_2", "quality_3"]), # Wrong defintion
|
|
||||||
({"type": "quality", "definition": "machine_1", "variant": "variant_2"}, ["quality_2", "quality_3"]), # Wrong variant
|
|
||||||
({"type": "quality", "definition": "machine_1", "variant": "variant_1", "material": "material_2"}, ["quality_2", "quality_3"]), # wrong material
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
quality_metadata_no_machine_quality =[({"type": "Not a quality"}, ["quality_1"]), # Wrong type
|
|
||||||
({"type": "quality", "definition": "machine_1"}, ["quality_1"]), # Wrong defintion (it needs fdmprinter)
|
|
||||||
({"type": "quality", "definition": "fdmprinter", "variant": "variant_2"}, ["quality_1", "quality_4"]), # Wrong variant, but should be added (as we ignore the variant)
|
|
||||||
({"type": "quality", "definition": "fdmprinter", "variant": "variant_1", "material": "material_2"}, ["quality_1", "quality_4"]), # wrong material, but should be added (as we ignore the material)
|
|
||||||
({"type": "quality", "definition": "fdmprinter", "variant": "variant_1", "material": "material_1"}, ["quality_1", "quality_4"]),
|
|
||||||
]
|
|
||||||
|
|
||||||
metadata_dict = {}
|
metadata_dict = {}
|
||||||
|
|
||||||
|
|
||||||
@ -86,45 +71,4 @@ def test_materialNodeInit_MachineQuality(container_registry):
|
|||||||
|
|
||||||
assert len(node.qualities) == 2
|
assert len(node.qualities) == 2
|
||||||
assert "quality_2" in node.qualities
|
assert "quality_2" in node.qualities
|
||||||
assert "quality_3" in node.qualities
|
assert "quality_3" in node.qualities
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("metadata,qualities_result_list", quality_metadata_machine_quality_test_data)
|
|
||||||
def test_qualityAdded_hasMachineQuality(container_registry, metadata, qualities_result_list):
|
|
||||||
variant_node = MagicMock()
|
|
||||||
variant_node.variant_name = "variant_1"
|
|
||||||
variant_node.machine.has_machine_quality = True
|
|
||||||
variant_node.machine.quality_definition = "machine_1"
|
|
||||||
|
|
||||||
container = createMockedInstanceContainer("quality_4")
|
|
||||||
|
|
||||||
with patch("cura.Machines.MaterialNode.QualityNode"):
|
|
||||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
|
|
||||||
node = MaterialNode("material_1", variant_node)
|
|
||||||
|
|
||||||
with patch.dict(metadata_dict, metadata):
|
|
||||||
node._qualityAdded(container)
|
|
||||||
|
|
||||||
assert len(qualities_result_list) == len(node.qualities)
|
|
||||||
for name in qualities_result_list:
|
|
||||||
assert name in node.qualities
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("metadata,qualities_result_list", quality_metadata_no_machine_quality)
|
|
||||||
def test_qualityAdded_noMachineQuality(container_registry, metadata, qualities_result_list):
|
|
||||||
variant_node = MagicMock()
|
|
||||||
variant_node.variant_name = "variant_1"
|
|
||||||
variant_node.machine.has_machine_quality = False
|
|
||||||
|
|
||||||
container = createMockedInstanceContainer("quality_4")
|
|
||||||
|
|
||||||
with patch("cura.Machines.MaterialNode.QualityNode"):
|
|
||||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
|
|
||||||
node = MaterialNode("material_1", variant_node)
|
|
||||||
|
|
||||||
with patch.dict(metadata_dict, metadata):
|
|
||||||
node._qualityAdded(container)
|
|
||||||
|
|
||||||
assert len(qualities_result_list) == len(node.qualities)
|
|
||||||
for name in qualities_result_list:
|
|
||||||
assert name in node.qualities
|
|
@ -1,21 +1,12 @@
|
|||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from cura.Machines.MaterialNode import MaterialNode
|
|
||||||
from cura.Machines.QualityNode import QualityNode
|
from cura.Machines.QualityNode import QualityNode
|
||||||
|
|
||||||
|
|
||||||
instance_container_metadata_dict = {"fdmprinter": {"variant_1": {"material_1": [{"id": "intent_1"}, {"id": "intent_2"}]}},
|
instance_container_metadata_dict = {"fdmprinter": {"variant_1": {"material_1": [{"id": "intent_1"}, {"id": "intent_2"}]}},
|
||||||
"machine_1": {"variant_2": {"material_2": [{"id": "intent_3"}, {"id": "intent_4"}]}}}
|
"machine_1": {"variant_2": {"material_2": [{"id": "intent_3"}, {"id": "intent_4"}]}}}
|
||||||
|
|
||||||
|
|
||||||
intent_metadata_intent_added_data = [({"type": "Not an intent"}, ["intent_3", "intent_4"]), # Wrong type
|
|
||||||
({"type": "intent", "definition": "machine_9000"}, ["intent_3", "intent_4"]), # wrong definition
|
|
||||||
({"type": "intent", "definition": "machine_1", "variant": "variant_299101"}, ["intent_3", "intent_4"]), # wrong variant
|
|
||||||
({"type": "intent", "definition": "machine_1", "variant": "variant_2", "material": "super cool material!"}, ["intent_3", "intent_4"]), # Wrong material
|
|
||||||
({"type": "intent", "definition": "machine_1", "variant": "variant_2", "material": "material_2"}, ["intent_3", "intent_4", "intent_9001"]), # Yay, all good.
|
|
||||||
]
|
|
||||||
|
|
||||||
metadata_dict = {}
|
metadata_dict = {}
|
||||||
|
|
||||||
|
|
||||||
@ -55,24 +46,4 @@ def test_qualityNode_machine_1(container_registry):
|
|||||||
|
|
||||||
assert len(node.intents) == 2
|
assert len(node.intents) == 2
|
||||||
assert "intent_3" in node.intents
|
assert "intent_3" in node.intents
|
||||||
assert "intent_4" in node.intents
|
assert "intent_4" in node.intents
|
||||||
|
|
||||||
@pytest.mark.parametrize("metadata,intent_result_list", intent_metadata_intent_added_data)
|
|
||||||
def test_intentNodeAdded(container_registry, metadata, intent_result_list):
|
|
||||||
material_node = MagicMock()
|
|
||||||
material_node.variant.machine.quality_definition = "machine_1"
|
|
||||||
material_node.variant.variant_name = "variant_2"
|
|
||||||
material_node.base_file = "material_2"
|
|
||||||
|
|
||||||
intent_container = createMockedInstanceContainer("intent_9001")
|
|
||||||
|
|
||||||
with patch("cura.Machines.QualityNode.IntentNode"):
|
|
||||||
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
|
|
||||||
node = QualityNode("quality_1", material_node)
|
|
||||||
with patch.dict(metadata_dict, metadata):
|
|
||||||
node._intentAdded(intent_container)
|
|
||||||
|
|
||||||
assert len(intent_result_list) == len(node.intents)
|
|
||||||
for identifier in intent_result_list:
|
|
||||||
assert identifier in node.intents
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user