123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- from typing import TYPE_CHECKING, Optional, cast, Dict, List, Set
- from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtSlot
- from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
- from UM.Logger import Logger
- from UM.Util import parseBool
- from UM.Settings.InstanceContainer import InstanceContainer
- from cura.Settings.ExtruderStack import ExtruderStack
- from cura.Machines.ContainerTree import ContainerTree
- from .QualityGroup import QualityGroup
- from .QualityNode import QualityNode
- if TYPE_CHECKING:
- from UM.Settings.Interfaces import DefinitionContainerInterface
- from cura.Settings.GlobalStack import GlobalStack
- from .QualityChangesGroup import QualityChangesGroup
- from cura.CuraApplication import CuraApplication
- class QualityManager(QObject):
- qualitiesUpdated = pyqtSignal()
- def __init__(self, application: "CuraApplication", parent = None) -> None:
- super().__init__(parent)
- self._application = application
- self._material_manager = self._application.getMaterialManager()
- self._container_registry = self._application.getContainerRegistry()
- self._empty_quality_container = self._application.empty_quality_container
- self._empty_quality_changes_container = self._application.empty_quality_changes_container
-
- self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {}
-
- self._machine_quality_type_to_quality_changes_dict = {}
- self._default_machine_definition_id = "fdmprinter"
- self._container_registry.containerMetaDataChanged.connect(self._onContainerMetadataChanged)
- self._container_registry.containerAdded.connect(self._onContainerMetadataChanged)
- self._container_registry.containerRemoved.connect(self._onContainerMetadataChanged)
-
-
-
- self._update_timer = QTimer(self)
- self._update_timer.setInterval(300)
- self._update_timer.setSingleShot(True)
- self._update_timer.timeout.connect(self._updateMaps)
- def initialize(self) -> None:
- container_tree = ContainerTree()
- for machine_id, machine in container_tree.machines.items():
- print("--", machine_id)
- for variant_name, variant in machine.variants.items():
- print("-- --", variant_name)
- for material_base_file, material in variant.materials.items():
- print("-- -- --", material_base_file)
- for quality_id, quality in material.qualities.items():
- print("-- -- -- --", quality_id)
- for intent_id in quality.intents:
- print("-- -- -- -- --", intent_id)
-
-
-
- self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {}
- self._machine_quality_type_to_quality_changes_dict = {}
- quality_metadata_list = self._container_registry.findContainersMetadata(type = "quality")
- for metadata in quality_metadata_list:
- if metadata["id"] == "empty_quality":
- continue
- definition_id = metadata["definition"]
- quality_type = metadata["quality_type"]
- root_material_id = metadata.get("material")
- nozzle_name = metadata.get("variant")
- buildplate_name = metadata.get("buildplate")
- is_global_quality = metadata.get("global_quality", False)
- is_global_quality = is_global_quality or (root_material_id is None and nozzle_name is None and buildplate_name is None)
-
- if is_global_quality and (root_material_id or nozzle_name):
- ConfigurationErrorMessage.getInstance().addFaultyContainers(metadata["id"])
- continue
- if definition_id not in self._machine_nozzle_buildplate_material_quality_type_to_quality_dict:
- self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id] = QualityNode()
- machine_node = cast(QualityNode, self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id])
- if is_global_quality:
-
- machine_node.addQualityMetadata(quality_type, metadata)
- continue
- current_node = machine_node
- intermediate_node_info_list = [nozzle_name, buildplate_name, root_material_id]
- current_intermediate_node_info_idx = 0
- while current_intermediate_node_info_idx < len(intermediate_node_info_list):
- node_name = intermediate_node_info_list[current_intermediate_node_info_idx]
- if node_name is not None:
-
-
- if node_name not in current_node.children_map:
- current_node.children_map[node_name] = QualityNode()
- current_node = cast(QualityNode, current_node.children_map[node_name])
- current_intermediate_node_info_idx += 1
- current_node.addQualityMetadata(quality_type, metadata)
-
-
- quality_changes_metadata_list = self._container_registry.findContainersMetadata(type = "quality_changes")
- for metadata in quality_changes_metadata_list:
- if metadata["id"] == "empty_quality_changes":
- continue
- machine_definition_id = metadata["definition"]
- quality_type = metadata["quality_type"]
- if machine_definition_id not in self._machine_quality_type_to_quality_changes_dict:
- self._machine_quality_type_to_quality_changes_dict[machine_definition_id] = QualityNode()
- machine_node = self._machine_quality_type_to_quality_changes_dict[machine_definition_id]
- machine_node.addQualityChangesMetadata(quality_type, metadata)
- Logger.log("d", "Lookup tables updated.")
- self.qualitiesUpdated.emit()
- def _updateMaps(self) -> None:
- self.initialize()
- def _onContainerMetadataChanged(self, container: InstanceContainer) -> None:
- self._onContainerChanged(container)
- def _onContainerChanged(self, container: InstanceContainer) -> None:
- container_type = container.getMetaDataEntry("type")
- if container_type not in ("quality", "quality_changes"):
- return
-
- self._update_timer.start()
-
- def _updateQualityGroupsAvailability(self, machine: "GlobalStack", quality_group_list) -> None:
- used_extruders = set()
- for i in range(machine.getProperty("machine_extruder_count", "value")):
- if str(i) in machine.extruders and machine.extruders[str(i)].isEnabled:
- used_extruders.add(str(i))
-
- for quality_group in quality_group_list:
- is_available = True
- if quality_group.node_for_global is None:
- is_available = False
- if is_available:
- for position in used_extruders:
- if position not in quality_group.nodes_for_extruders:
- is_available = False
- break
- quality_group.is_available = is_available
-
- def getQualityChangesGroups(self, machine: "GlobalStack") -> dict:
- machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
- machine_node = self._machine_quality_type_to_quality_changes_dict.get(machine_definition_id)
- if not machine_node:
- Logger.log("i", "Cannot find node for machine def [%s] in QualityChanges lookup table", machine_definition_id)
- return dict()
-
-
- quality_group_dict = self.getQualityGroups(machine)
- available_quality_type_list = [qt for qt, qg in quality_group_dict.items() if qg.is_available]
-
- quality_changes_group_dict = dict()
- for quality_type, quality_changes_node in machine_node.quality_type_map.items():
- for quality_changes_name, quality_changes_group in quality_changes_node.children_map.items():
- quality_changes_group_dict[quality_changes_name] = quality_changes_group
- quality_changes_group.is_available = quality_type in available_quality_type_list
- return quality_changes_group_dict
-
-
-
-
-
-
- def getQualityGroups(self, machine: "GlobalStack") -> Dict[str, QualityGroup]:
- machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
-
-
-
- machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
-
-
- has_extruder_specific_qualities = False
- if machine_node:
- if machine_node.children_map:
- has_extruder_specific_qualities = True
- default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)
- nodes_to_check = []
- if machine_node is not None:
- nodes_to_check.append(machine_node)
- if default_machine_node is not None:
- nodes_to_check.append(default_machine_node)
-
- quality_group_dict = {}
- for node in nodes_to_check:
- if node and node.quality_type_map:
- quality_node = list(node.quality_type_map.values())[0]
- is_global_quality = parseBool(quality_node.getMetaDataEntry("global_quality", False))
- if not is_global_quality:
- continue
- for quality_type, quality_node in node.quality_type_map.items():
- quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
- quality_group.setGlobalNode(quality_node)
- quality_group_dict[quality_type] = quality_group
- break
- buildplate_name = machine.getBuildplateName()
-
- for position, extruder in machine.extruders.items():
- nozzle_name = None
- if extruder.variant.getId() != "empty_variant":
- nozzle_name = extruder.variant.getName()
-
-
- root_material_id_list = []
- has_material = False
- root_material_id = None
- if extruder.material.getId() != "empty_material":
- has_material = True
- root_material_id = extruder.material.getMetaDataEntry("base_file")
-
- root_material_id = self._material_manager.getRootMaterialIDWithoutDiameter(root_material_id)
- root_material_id_list.append(root_material_id)
-
- fallback_ids = self._material_manager.getFallBackMaterialIdsByMaterial(extruder.material)
- if fallback_ids:
- root_material_id_list.extend(fallback_ids)
-
- seen = set()
- root_material_id_list = [x for x in root_material_id_list if x not in seen and not seen.add(x)]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- node_info_list_0 = [nozzle_name, buildplate_name, root_material_id]
- nodes_to_check = []
-
-
-
- def addNodesToCheck(node: Optional[QualityNode], nodes_to_check_list: List[QualityNode], node_info_list, node_info_idx: int) -> None:
- if node is None:
- return
- if node_info_idx < len(node_info_list):
- node_name = node_info_list[node_info_idx]
- if node_name is not None:
- current_node = node.getChildNode(node_name)
- if current_node is not None and has_material:
- addNodesToCheck(current_node, nodes_to_check_list, node_info_list, node_info_idx + 1)
- if has_material:
- for rmid in root_material_id_list:
- material_node = node.getChildNode(rmid)
- if material_node:
- nodes_to_check_list.append(material_node)
- break
- nodes_to_check_list.append(node)
- addNodesToCheck(machine_node, nodes_to_check, node_info_list_0, 0)
-
-
- if machine_node is not None and machine_node.quality_type_map:
- nodes_to_check += [machine_node]
- elif default_machine_node is not None:
- nodes_to_check += [default_machine_node]
- for node_idx, node in enumerate(nodes_to_check):
- if node and node.quality_type_map:
- if has_extruder_specific_qualities:
-
- quality_node = list(node.quality_type_map.values())[0]
- is_global_quality = parseBool(quality_node.getMetaDataEntry("global_quality", False))
- if is_global_quality:
- continue
- for quality_type, quality_node in node.quality_type_map.items():
- if quality_type not in quality_group_dict:
- quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
- quality_group_dict[quality_type] = quality_group
- quality_group = quality_group_dict[quality_type]
- if position not in quality_group.nodes_for_extruders:
- quality_group.setExtruderNode(position, quality_node)
-
-
- if has_extruder_specific_qualities:
- if node_idx == len(nodes_to_check) - 1:
- break
-
- self._updateQualityGroupsAvailability(machine, quality_group_dict.values())
- return quality_group_dict
- def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> Dict[str, QualityGroup]:
- machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
-
-
-
- machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
- default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(
- self._default_machine_definition_id)
- nodes_to_check = [machine_node, default_machine_node]
-
- quality_group_dict = dict()
- for node in nodes_to_check:
- if node and node.quality_type_map:
- for quality_type, quality_node in node.quality_type_map.items():
- quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
- quality_group.setGlobalNode(quality_node)
- quality_group_dict[quality_type] = quality_group
- break
- return quality_group_dict
- def getDefaultQualityType(self, machine: "GlobalStack") -> Optional[QualityGroup]:
- preferred_quality_type = machine.definition.getMetaDataEntry("preferred_quality_type")
- quality_group_dict = self.getQualityGroups(machine)
- quality_group = quality_group_dict.get(preferred_quality_type)
- return quality_group
-
-
-
-
-
-
- @pyqtSlot(QObject)
- def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
- Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name)
- removed_quality_changes_ids = set()
- for node in quality_changes_group.getAllNodes():
- container_id = node.getMetaDataEntry("id")
- self._container_registry.removeContainer(container_id)
- removed_quality_changes_ids.add(container_id)
-
- 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
-
-
-
- @pyqtSlot(QObject, str, result = 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)
- 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)
- for node in quality_changes_group.getAllNodes():
- container = node.getContainer()
- if container:
- container.setName(new_name)
- quality_changes_group.name = new_name
- self._application.getMachineManager().activeQualityChanged.emit()
- self._application.getMachineManager().activeQualityGroupChanged.emit()
- return new_name
-
-
-
- @pyqtSlot(str, "QVariantMap")
- def duplicateQualityChanges(self, quality_changes_name: str, quality_model_item) -> None:
- global_stack = self._application.getGlobalContainerStack()
- 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:
-
- 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.getContainer()
- if not container:
- continue
- new_id = self._container_registry.uniqueName(container.getId())
- self._container_registry.addContainer(container.duplicate(new_id, new_name))
-
-
-
-
-
- @pyqtSlot(str)
- def createQualityChanges(self, base_name: str) -> None:
- machine_manager = self._application.getMachineManager()
- global_stack = machine_manager.activeMachine
- if not global_stack:
- return
- active_quality_name = machine_manager.activeQualityOrQualityChangesName
- if active_quality_name == "":
- Logger.log("w", "No quality container found in stack %s, cannot create profile", global_stack.getId())
- return
- machine_manager.blurSettings.emit()
- if base_name is None or base_name == "":
- base_name = active_quality_name
- unique_name = self._container_registry.uniqueName(base_name)
-
- stack_list = [global_stack] + list(global_stack.extruders.values())
- for stack in stack_list:
- user_container = stack.userChanges
- quality_container = stack.quality
- quality_changes_container = stack.qualityChanges
- if not quality_container or not quality_changes_container:
- Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId())
- continue
- quality_type = quality_container.getMetaDataEntry("quality_type")
- extruder_stack = None
- if isinstance(stack, ExtruderStack):
- extruder_stack = stack
- new_changes = self._createQualityChanges(quality_type, unique_name, global_stack, extruder_stack)
- from cura.Settings.ContainerManager import ContainerManager
- ContainerManager.getInstance()._performMerge(new_changes, quality_changes_container, clear_settings = False)
- ContainerManager.getInstance()._performMerge(new_changes, user_container)
- self._container_registry.addContainer(new_changes)
-
-
-
- def _createQualityChanges(self, quality_type: str, new_name: str, machine: "GlobalStack",
- extruder_stack: Optional["ExtruderStack"]) -> "InstanceContainer":
- 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 = self._container_registry.uniqueName(new_id)
-
- quality_changes = InstanceContainer(new_id)
- quality_changes.setName(new_name)
- quality_changes.setMetaDataEntry("type", "quality_changes")
- quality_changes.setMetaDataEntry("quality_type", quality_type)
-
- if extruder_stack is not None:
- quality_changes.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
-
- machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
- quality_changes.setDefinition(machine_definition_id)
- quality_changes.setMetaDataEntry("setting_version", self._application.SettingVersion)
- return quality_changes
- def getMachineDefinitionIDForQualitySearch(machine_definition: "DefinitionContainerInterface",
- default_definition_id: str = "fdmprinter") -> str:
- machine_definition_id = default_definition_id
- if parseBool(machine_definition.getMetaDataEntry("has_machine_quality", False)):
-
- machine_definition_id = machine_definition.getMetaDataEntry("quality_definition")
- if machine_definition_id is None:
- machine_definition_id = machine_definition.getId()
- return machine_definition_id
|