diff options
author | j.delarago <joeydelarago@gmail.com> | 2022-06-14 12:41:38 +0300 |
---|---|---|
committer | j.delarago <joeydelarago@gmail.com> | 2022-06-14 12:41:38 +0300 |
commit | a87695cd8dfb5c257ed63ffd05a972846bcb77ec (patch) | |
tree | ca418c069db6f044df4fc308e52134a61fe1a27a /cura | |
parent | 6f88adab8e031176b27f90ed50abe374112afeef (diff) |
Added new intent selection buttons and resolution drop down to replace the matrix.
We are now selecting intents first and then quality, however the container hierarchy quality -> intents. This is the reason for the new functions inside machine manager.
CURA-8849
Diffstat (limited to 'cura')
-rwxr-xr-x | cura/CuraApplication.py | 4 | ||||
-rw-r--r-- | cura/Machines/Models/ActiveIntentQualitiesModel.py | 121 | ||||
-rw-r--r-- | cura/Machines/Models/IntentSelectionModel.py | 129 | ||||
-rwxr-xr-x | cura/Settings/MachineManager.py | 28 |
4 files changed, 282 insertions, 0 deletions
diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 3a3ac17cdf..e1805584b0 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -115,6 +115,8 @@ from . import CuraActions from . import PlatformPhysics from . import PrintJobPreviewImageProvider from .AutoSave import AutoSave +from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel +from .Machines.Models.IntentSelectionModel import IntentSelectionModel from .SingleInstance import SingleInstance if TYPE_CHECKING: @@ -1192,6 +1194,8 @@ class CuraApplication(QtApplication): qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel") qmlRegisterType(IntentModel, "Cura", 1, 6, "IntentModel") qmlRegisterType(IntentCategoryModel, "Cura", 1, 6, "IntentCategoryModel") + qmlRegisterType(IntentSelectionModel, "Cura", 1, 7, "IntentSelectionModel") + qmlRegisterType(ActiveIntentQualitiesModel, "Cura", 1, 7, "ActiveIntentQualitiesModel") self.processEvents() qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") diff --git a/cura/Machines/Models/ActiveIntentQualitiesModel.py b/cura/Machines/Models/ActiveIntentQualitiesModel.py new file mode 100644 index 0000000000..67b9cec9a4 --- /dev/null +++ b/cura/Machines/Models/ActiveIntentQualitiesModel.py @@ -0,0 +1,121 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional, Set, Dict, List, Any + +from PyQt6.QtCore import Qt, QObject, QTimer + +import cura.CuraApplication +from UM.Logger import Logger +from UM.Qt.ListModel import ListModel +from cura.Machines.ContainerTree import ContainerTree +from cura.Machines.Models.MachineModelUtils import fetchLayerHeight +from cura.Machines.MaterialNode import MaterialNode +from cura.Machines.QualityGroup import QualityGroup +from cura.Settings.IntentManager import IntentManager + + +class ActiveIntentQualitiesModel(ListModel): + NameRole = Qt.ItemDataRole.UserRole + 1 + DisplayTextRole = Qt.ItemDataRole.UserRole + 2 + QualityTypeRole = Qt.ItemDataRole.UserRole + 3 + LayerHeightRole = Qt.ItemDataRole.UserRole + 4 + IntentCategeoryRole = Qt.ItemDataRole.UserRole + 5 + + def __init__(self, parent: Optional[QObject] = None) -> None: + super().__init__(parent) + + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.QualityTypeRole, "quality_type") + self.addRoleName(self.LayerHeightRole, "layer_height") + self.addRoleName(self.DisplayTextRole, "display_text") + self.addRoleName(self.IntentCategeoryRole, "intent_category") + + self._intent_category = "" + + IntentManager.intentCategoryChangedSignal.connect(self._update) + + self._update_timer = QTimer() + self._update_timer.setInterval(100) + self._update_timer.setSingleShot(True) + self._update_timer.timeout.connect(self._update) + + self._update() + + def _updateDelayed(self): + self._update_timer.start() + + def _onChanged(self, container): + if container.getMetaDataEntry("type") == "intent": + self._updateDelayed() + + def _update(self): + active_extruder_stack = cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeStack + if active_extruder_stack: + self._intent_category = active_extruder_stack.intent.getMetaDataEntry("intent_category", "") + + new_items = [] # type: List[Dict[str, Any]] + global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() + if not global_stack: + self.setItems(new_items) + return + quality_groups = ContainerTree.getInstance().getCurrentQualityGroups() + + material_nodes = self._getActiveMaterials() + + added_quality_type_set = set() # type: Set[str] + for material_node in material_nodes: + intents = self._getIntentsForMaterial(material_node, quality_groups) + for intent in intents: + if intent["quality_type"] not in added_quality_type_set: + new_items.append(intent) + added_quality_type_set.add(intent["quality_type"]) + + new_items = sorted(new_items, key=lambda x: x["layer_height"]) + self.setItems(new_items) + + def _getActiveMaterials(self) -> Set["MaterialNode"]: + """Get the active materials for all extruders. No duplicates will be returned""" + + global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() + if global_stack is None: + return set() + + container_tree = ContainerTree.getInstance() + machine_node = container_tree.machines[global_stack.definition.getId()] + nodes = set() # type: Set[MaterialNode] + + for extruder in global_stack.extruderList: + active_variant_name = extruder.variant.getMetaDataEntry("name") + if active_variant_name not in machine_node.variants: + Logger.log("w", "Could not find the variant %s", active_variant_name) + continue + active_variant_node = machine_node.variants[active_variant_name] + active_material_node = active_variant_node.materials.get(extruder.material.getMetaDataEntry("base_file")) + if active_material_node is None: + Logger.log("w", "Could not find the material %s", extruder.material.getMetaDataEntry("base_file")) + continue + nodes.add(active_material_node) + + return nodes + + def _getIntentsForMaterial(self, active_material_node: "MaterialNode", quality_groups: Dict[str, "QualityGroup"]) -> List[Dict[str, Any]]: + extruder_intents = [] # type: List[Dict[str, Any]] + + for quality_id, quality_node in active_material_node.qualities.items(): + if quality_node.quality_type not in quality_groups: # Don't add the empty quality type (or anything else that would crash, defensively). + continue + quality_group = quality_groups[quality_node.quality_type] + layer_height = fetchLayerHeight(quality_group) + + for intent_id, intent_node in quality_node.intents.items(): + if intent_node.intent_category != self._intent_category: + continue + extruder_intents.append({"name": quality_group.name, + "display_text": f"{quality_group.name} - {layer_height}mm", + "quality_type": quality_group.quality_type, + "layer_height": layer_height, + "intent_category": self._intent_category + }) + return extruder_intents + + diff --git a/cura/Machines/Models/IntentSelectionModel.py b/cura/Machines/Models/IntentSelectionModel.py new file mode 100644 index 0000000000..c8c9b9974d --- /dev/null +++ b/cura/Machines/Models/IntentSelectionModel.py @@ -0,0 +1,129 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import collections +from typing import OrderedDict + +from PyQt6.QtCore import Qt, QTimer + +import cura +from UM import i18nCatalog +from UM.Logger import Logger +from UM.Qt.ListModel import ListModel +from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Settings.Interfaces import ContainerInterface +from cura.Settings.IntentManager import IntentManager + +catalog = i18nCatalog("cura") + + +class IntentSelectionModel(ListModel): + + NameRole = Qt.ItemDataRole.UserRole + 1 + IntentCategoryRole = Qt.ItemDataRole.UserRole + 2 + WeightRole = Qt.ItemDataRole.UserRole + 3 + DescriptionRole = Qt.ItemDataRole.UserRole + 4 + IconRole = Qt.ItemDataRole.UserRole + 5 + + def __init__(self, parent=None): + super().__init__(parent) + + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.IntentCategoryRole, "intent_category") + self.addRoleName(self.WeightRole, "weight") + self.addRoleName(self.DescriptionRole, "description") + self.addRoleName(self.IconRole, "icon") + + application = cura.CuraApplication.CuraApplication.getInstance() + + ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChange) + ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChange) + machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager() + machine_manager.activeMaterialChanged.connect(self._update) + machine_manager.activeVariantChanged.connect(self._update) + machine_manager.extruderChanged.connect(self._update) + + extruder_manager = application.getExtruderManager() + extruder_manager.extrudersChanged.connect(self._update) + + self._update_timer = QTimer() # type: QTimer + self._update_timer.setInterval(100) + self._update_timer.setSingleShot(True) + self._update_timer.timeout.connect(self._update) + + self._onChange() + + @staticmethod + def _getDefaultProfileInformation() -> OrderedDict[str, dict]: + """ Default information user-visible string. Ordered by weight. """ + default_profile_information = collections.OrderedDict() + default_profile_information["default"] = { + "name": catalog.i18nc("@label", "Default"), + "icon": "GearCheck" + } + default_profile_information["visual"] = { + "name": catalog.i18nc("@label", "Visual"), + "description": catalog.i18nc("@text", "The visual profile is designed to print visual prototypes and models with the intent of high visual and surface quality."), + "icon" : "Visual" + } + default_profile_information["engineering"] = { + "name": catalog.i18nc("@label", "Engineering"), + "description": catalog.i18nc("@text", "The engineering profile is designed to print functional prototypes and end-use parts with the intent of better accuracy and for closer tolerances."), + "icon": "Nut" + } + default_profile_information["quick"] = { + "name": catalog.i18nc("@label", "Draft"), + "description": catalog.i18nc("@text", "The draft profile is designed to print initial prototypes and concept validation with the intent of significant print time reduction."), + "icon": "SpeedOMeter" + } + return default_profile_information + + def _onContainerChange(self, container: ContainerInterface) -> None: + """Updates the list of intents if an intent profile was added or removed.""" + + if container.getMetaDataEntry("type") == "intent": + self._update() + + def _onChange(self) -> None: + self._update_timer.start() + + def _update(self): + Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) + + global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() + if global_stack is None: + self.setItems([]) + Logger.log("d", "No active GlobalStack, set quality profile model as empty.") + return + + # Check for material compatibility + if not cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeMaterialsCompatible(): + Logger.log("d", "No active material compatibility, set quality profile model as empty.") + self.setItems([]) + return + + default_profile_info = self._getDefaultProfileInformation() + + available_categories = IntentManager.getInstance().currentAvailableIntentCategories() + result = [] + for i, category in enumerate(available_categories): + profile_info = default_profile_info.get(category, {}) + + try: + weight = list(default_profile_info.keys()).index(category) + except ValueError: + weight = len(available_categories) + i + + result.append({ + "name": profile_info.get("name", category.title()), + "description": profile_info.get("description", None), + "icon" : profile_info.get("icon", ""), + "intent_category": category, + "weight": weight, + }) + + result.sort(key=lambda k: k["weight"]) + + self.setItems(result) + + diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 9b98179bff..64d34d6c3e 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1778,3 +1778,31 @@ class MachineManager(QObject): abbr_machine += stripped_word return abbr_machine + + @pyqtSlot(str, str, result = bool) + def intentCategoryHasQuality(self, intent_category: str, quality_type: str) -> bool: + """ Checks if there are any quality groups for active extruders that have an intent category """ + quality_groups = ContainerTree.getInstance().getCurrentQualityGroups() + + if quality_type in quality_groups: + quality_group = quality_groups[quality_type] + for node in quality_group.nodes_for_extruders.values(): + if any(intent.intent_category == intent_category for intent in node.intents.values()): + return True + + return False + + @pyqtSlot(str, result = str) + def getDefaultQualityTypeForIntent(self, intent_category) -> str: + """ If there is an intent category for the default machine quality return it, otherwise return the first quality for this intent category """ + machine = ContainerTree.getInstance().machines.get(self._global_container_stack.definition.getId()) + + if self.intentCategoryHasQuality(intent_category, machine.preferred_quality_type): + return machine.preferred_quality_type + + for quality_type, quality_group in ContainerTree.getInstance().getCurrentQualityGroups().items(): + for node in quality_group.nodes_for_extruders.values(): + if any(intent.intent_category == intent_category for intent in node.intents.values()): + return quality_type + + return "" |