mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-16 18:59:39 +08:00
192 lines
9.1 KiB
Python
192 lines
9.1 KiB
Python
# Copyright (c) 2019 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 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.
|
|
|
|
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()
|
|
active_extruder_stack = application.getMachineManager().activeStack
|
|
if active_extruder_stack is None:
|
|
return ""
|
|
return active_extruder_stack.intent.getMetaDataEntry("intent_category", "")
|
|
|
|
@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()
|