123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- # Copyright (c) 2022 Ultimaker B.V.
- # Cura is released under the terms of the LGPLv3 or higher.
- from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
- from typing import Any, Dict, List, Set, Tuple, TYPE_CHECKING
- from UM.Logger import Logger
- from UM.Settings.InstanceContainer import InstanceContainer
- import cura.CuraApplication
- from UM.Signal import Signal
- from cura.Machines.ContainerTree import ContainerTree
- from cura.Settings.cura_empty_instance_containers import empty_intent_container
- if TYPE_CHECKING:
- from UM.Settings.InstanceContainer import InstanceContainer
- class IntentManager(QObject):
- """Front-end for querying which intents are available for a certain configuration.
- """
- __instance = None
- @classmethod
- def getInstance(cls):
- """This class is a singleton."""
- if not cls.__instance:
- cls.__instance = IntentManager()
- return cls.__instance
- intentCategoryChanged = pyqtSignal() #Triggered when we switch categories.
- intentCategoryChangedSignal = Signal()
- def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]:
- """Gets the metadata dictionaries of all intent profiles for a given
- configuration.
- :param definition_id: ID of the printer.
- :param nozzle_name: Name of the nozzle.
- :param material_base_file: The base_file of the material.
- :return: A list of metadata dictionaries matching the search criteria, or
- an empty list if nothing was found.
- """
- intent_metadatas = [] # type: List[Dict[str, Any]]
- try:
- materials = ContainerTree.getInstance().machines[definition_id].variants[nozzle_name].materials
- except KeyError:
- Logger.log("w", "Unable to find the machine %s or the variant %s", definition_id, nozzle_name)
- materials = {}
- if material_base_file not in materials:
- return intent_metadatas
- material_node = materials[material_base_file]
- for quality_node in material_node.qualities.values():
- for intent_node in quality_node.intents.values():
- intent_metadatas.append(intent_node.getMetadata())
- return intent_metadatas
- def intentCategories(self, definition_id: str, nozzle_id: str, material_id: str) -> List[str]:
- """Collects and returns all intent categories available for the given
- parameters. Note that the 'default' category is always available.
- :param definition_id: ID of the printer.
- :param nozzle_name: Name of the nozzle.
- :param material_id: ID of the material.
- :return: A set of intent category names.
- """
- categories = set()
- for intent in self.intentMetadatas(definition_id, nozzle_id, material_id):
- categories.add(intent["intent_category"])
- categories.add("default") #The "empty" intent is not an actual profile specific to the configuration but we do want it to appear in the categories list.
- return list(categories)
- def getCurrentAvailableIntents(self) -> List[Tuple[str, str]]:
- """List of intents to be displayed in the interface.
- For the interface this will have to be broken up into the different
- intent categories. That is up to the model there.
- :return: A list of tuples of intent_category and quality_type. The actual
- instance may vary per extruder.
- """
- application = cura.CuraApplication.CuraApplication.getInstance()
- global_stack = application.getGlobalContainerStack()
- if global_stack is None:
- return [("default", "normal")]
- # 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.
- # Currently _also_ inconsistent with 'currentAvailableIntentCategories', which _does_ return default.
- quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
- 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]
- current_definition_id = global_stack.definition.getId()
- for extruder_stack in global_stack.extruderList:
- if not extruder_stack.isEnabled:
- continue
- nozzle_name = extruder_stack.variant.getMetaDataEntry("name")
- material_id = extruder_stack.material.getMetaDataEntry("base_file")
- final_intent_ids |= {metadata["id"] for metadata in self.intentMetadatas(current_definition_id, nozzle_name, material_id) if metadata.get("quality_type") in available_quality_types}
- result = set() # type: Set[Tuple[str, str]]
- for intent_id in final_intent_ids:
- intent_metadata = application.getContainerRegistry().findContainersMetadata(id = intent_id)[0]
- result.add((intent_metadata["intent_category"], intent_metadata["quality_type"]))
- return list(result)
- def currentAvailableIntentCategories(self) -> List[str]:
- """List of intent categories available in either of the extruders.
- This is purposefully inconsistent with the way that the quality types
- are listed. The quality types will show all quality types available in
- the printer using any configuration. This will only list the intent
- categories that are available using the current configuration (but the
- union over the extruders).
- :return: List of all categories in the current configurations of all
- extruders.
- """
- global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
- if global_stack is None:
- return ["default"]
- current_definition_id = global_stack.definition.getId()
- final_intent_categories = set() # type: Set[str]
- for extruder_stack in global_stack.extruderList:
- if not extruder_stack.isEnabled:
- continue
- nozzle_name = extruder_stack.variant.getMetaDataEntry("name")
- material_id = extruder_stack.material.getMetaDataEntry("base_file")
- final_intent_categories.update(self.intentCategories(current_definition_id, nozzle_name, material_id))
- return list(final_intent_categories)
- def getDefaultIntent(self) -> "InstanceContainer":
- """The intent that gets selected by default when no intent is available for
- the configuration, an extruder can't match the intent that the user
- selects, or just when creating a new printer.
- """
- return empty_intent_container
- @pyqtProperty(str, notify = intentCategoryChanged)
- def currentIntentCategory(self) -> str:
- application = cura.CuraApplication.CuraApplication.getInstance()
- global_stack = application.getGlobalContainerStack()
- active_intent = "default"
- if global_stack is None:
- return active_intent
- # Loop over all active extruders and check if they have an intent that isn't default.
- # The logic behind this is that support materials (for instance, PVA) don't have intents, but they should be
- # combinable with all other intents. So if one extruder has "engineering" as an intent and the other has
- # "default" the 'dominant' intent is "engineering"
- for extruder_stack in global_stack.extruderList:
- if not extruder_stack.isEnabled: # Ignore disabled stacks
- continue
- extruder_intent = extruder_stack.intent.getMetaDataEntry("intent_category", "")
- if extruder_intent != "default":
- active_intent = extruder_intent
- return active_intent
- @pyqtSlot(str, str)
- def selectIntent(self, intent_category: str, quality_type: str) -> None:
- """Apply intent on the stacks."""
- Logger.log("i", "Attempting to set intent_category to [%s] and quality type to [%s]", intent_category, quality_type)
- old_intent_category = self.currentIntentCategory
- application = cura.CuraApplication.CuraApplication.getInstance()
- global_stack = application.getGlobalContainerStack()
- if global_stack is None:
- return
- current_definition_id = global_stack.definition.getId()
- machine_node = ContainerTree.getInstance().machines[current_definition_id]
- for extruder_stack in global_stack.extruderList:
- nozzle_name = extruder_stack.variant.getMetaDataEntry("name")
- material_id = extruder_stack.material.getMetaDataEntry("base_file")
- material_node = machine_node.variants[nozzle_name].materials[material_id]
- # Since we want to switch to a certain quality type, check the tree if we have one.
- quality_node = None
- for q_node in material_node.qualities.values():
- if q_node.quality_type == quality_type:
- quality_node = q_node
- if quality_node is None:
- Logger.log("w", "Unable to find quality_type [%s] for extruder [%s]", quality_type, extruder_stack.getId())
- continue
- # Check that quality node if we can find a matching intent.
- intent_id = None
- for id, intent_node in quality_node.intents.items():
- if intent_node.intent_category == intent_category:
- intent_id = id
- intent = application.getContainerRegistry().findContainers(id = intent_id)
- if intent:
- extruder_stack.intent = intent[0]
- else:
- extruder_stack.intent = self.getDefaultIntent()
- application.getMachineManager().setQualityGroupByQualityType(quality_type)
- if old_intent_category != intent_category:
- self.intentCategoryChanged.emit()
- self.intentCategoryChangedSignal.emit()
|