diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 28a8e12deb..953336fc30 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -61,7 +61,6 @@ from cura.Arranging.Arrange import Arrange from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob from cura.Arranging.ShapeArray import ShapeArray -from cura.Machines.Models.IntentModel import IntentModel from cura.Operations.SetParentOperation import SetParentOperation @@ -92,6 +91,8 @@ from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfile from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel from cura.Machines.Models.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel from cura.Machines.Models.UserChangesModel import UserChangesModel +from cura.Machines.Models.IntentModel import IntentModel +from cura.Machines.Models.IntentCategoryModel import IntentCategoryModel from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage @@ -104,6 +105,7 @@ from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.MachineManager import MachineManager from cura.Settings.MachineNameValidator import MachineNameValidator +from cura.Settings.IntentManager import IntentManager from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler from cura.Settings.SettingInheritanceManager import SettingInheritanceManager from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel @@ -937,6 +939,9 @@ class CuraApplication(QtApplication): def getQualityManager(self, *args) -> "QualityManager": return self._quality_manager + def getIntentManager(self, *args) -> IntentManager: + return IntentManager.getInstance() + def getObjectsModel(self, *args): if self._object_manager is None: self._object_manager = ObjectsModel(self) @@ -1036,6 +1041,7 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 0, "SceneController", self.getCuraSceneController) qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, "ExtruderManager", self.getExtruderManager) qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager) + qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, "IntentManager", self.getIntentManager) qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager", self.getSettingInheritanceManager) qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager) qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) @@ -1070,6 +1076,7 @@ class CuraApplication(QtApplication): "CustomQualityProfilesDropDownMenuModel", self.getCustomQualityProfilesDropDownMenuModel) qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel") qmlRegisterType(IntentModel, "Cura", 1, 6, "IntentModel") + qmlRegisterType(IntentCategoryModel, "Cura", 1, 6, "IntentCategoryModel") qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") qmlRegisterType(SettingVisibilityPresetsModel, "Cura", 1, 0, "SettingVisibilityPresetsModel") diff --git a/cura/Machines/Models/IntentCategoryModel.py b/cura/Machines/Models/IntentCategoryModel.py index ac69191ca1..9a520a2d31 100644 --- a/cura/Machines/Models/IntentCategoryModel.py +++ b/cura/Machines/Models/IntentCategoryModel.py @@ -3,9 +3,14 @@ from PyQt5.QtCore import Qt import collections +from typing import TYPE_CHECKING from cura.Settings.IntentManager import IntentManager from UM.Qt.ListModel import ListModel +from UM.Settings.ContainerRegistry import ContainerRegistry #To update the list if anything changes. + +if TYPE_CHECKING: + from UM.Settings.ContainerRegistry import ContainerInterface from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -34,6 +39,17 @@ class IntentCategoryModel(ListModel): self.addRoleName(self.IntentCategoryRole, "intent_category") self.addRoleName(self.WeightRole, "weight") + ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChange) + ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChange) + IntentManager.getInstance().configurationChanged.connect(self.update) + + self.update() + + ## Updates the list of intents if an intent profile was added or removed. + def _onContainerChange(self, container: "ContainerInterface") -> None: + if container.getMetaDataEntry("type") == "intent": + self.update() + ## Updates the list of intents. def update(self) -> None: available_categories = IntentManager.getInstance().currentAvailableIntentCategories() @@ -42,6 +58,6 @@ class IntentCategoryModel(ListModel): result.append({ "name": self.name_translation.get(category, catalog.i18nc("@label", "Unknown")), "intent_category": category, - "weight": list(self.name_translation.items()).index(category) + "weight": list(self.name_translation.keys()).index(category) }) - super().update(result) \ No newline at end of file + self.setItems(result) \ No newline at end of file diff --git a/cura/Machines/Models/IntentModel.py b/cura/Machines/Models/IntentModel.py index 3f480e2eef..275087689b 100644 --- a/cura/Machines/Models/IntentModel.py +++ b/cura/Machines/Models/IntentModel.py @@ -1,38 +1,65 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional -from PyQt5.QtCore import QObject -from UM.Qt.ListModel import ListModel -from PyQt5.QtCore import Qt +from typing import Optional, List, Dict, Any +from PyQt5.QtCore import Qt, QObject, pyqtProperty, pyqtSignal + +from UM.Qt.ListModel import ListModel from UM.Settings.ContainerRegistry import ContainerRegistry +from cura.Settings.IntentManager import IntentManager +import cura.CuraApplication + class IntentModel(ListModel): NameRole = Qt.UserRole + 1 - IdRole = Qt.UserRole + 2 - ContainerRole = Qt.UserRole + 3 + QualityTypeRole = Qt.UserRole + 2 def __init__(self, parent: Optional[QObject] = None) -> None: super().__init__(parent) self.addRoleName(self.NameRole, "name") - self.addRoleName(self.IdRole, "id") - self.addRoleName(self.ContainerRole, "container") + self.addRoleName(self.QualityTypeRole, "quality_type") + + self._intent_category = "engineering" ContainerRegistry.getInstance().containerAdded.connect(self._onChanged) ContainerRegistry.getInstance().containerRemoved.connect(self._onChanged) self._update() + intentCategoryChanged = pyqtSignal() + + def setIntentCategory(self, new_category: str) -> None: + if self._intent_category != new_category: + self._intent_category = new_category + self.intentCategoryChanged.emit() + self._update() + + @pyqtProperty(str, fset = setIntentCategory, notify = intentCategoryChanged) + def intentCategory(self) -> str: + return self._intent_category + def _onChanged(self, container): if container.getMetaDataEntry("type") == "intent": self._update() def _update(self) -> None: - new_items = [] - for container in ContainerRegistry.getInstance().findInstanceContainers(type="intent"): - new_items.append({"name": container.getName(), "id": container.getId(), "container": container}) + new_items = [] # type: List[Dict[str, Any]] + application = cura.CuraApplication.CuraApplication.getInstance() + quality_manager = application.getQualityManager() + global_stack = application.getGlobalContainerStack() + if not global_stack: + self.setItems(new_items) + return + quality_groups = quality_manager.getQualityGroups(global_stack) + + for intent_category, quality_type in IntentManager.getInstance().getCurrentAvailableIntents(): + if intent_category == self._intent_category: + new_items.append({"name": quality_groups[quality_type].name, "quality_type": quality_type}) + if self._intent_category == "default": #For Default we always list all quality types. We can't filter on available profiles since the empty intent is not a specific quality type. + for quality_type in quality_groups.keys(): + new_items.append({"name": quality_groups[quality_type].name, "quality_type": quality_type}) self.setItems(new_items) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index e80c03ce34..17e793b948 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -1,10 +1,10 @@ #Copyright (c) 2019 Ultimaker B.V. #Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtSignal -from typing import Any, Dict, List, Set, Tuple, TYPE_CHECKING -from cura.CuraApplication import CuraApplication -from cura.Settings.ExtruderManager import ExtruderManager +from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot +from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING +import cura.CuraApplication +from cura.Settings.cura_empty_instance_containers import empty_intent_container from UM.Settings.InstanceContainer import InstanceContainer if TYPE_CHECKING: @@ -20,7 +20,7 @@ class IntentManager(QObject): def __init__(self) -> None: super().__init__() - CuraApplication.getInstance().getMachineManager().activeStackChanged.connect(self.configurationChanged) + cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeStackChanged.connect(self.configurationChanged) self.configurationChanged.connect(self.selectDefaultIntent) pass @@ -31,7 +31,8 @@ class IntentManager(QObject): cls.__instance = IntentManager() return cls.__instance - configurationChanged = pyqtSignal() + configurationChanged = pyqtSignal() #Triggered when something changed in the rest of the stack. + intentCategoryChanged = pyqtSignal() #Triggered when we switch categories. ## Gets the metadata dictionaries of all intent profiles for a given # configuration. @@ -42,7 +43,7 @@ class IntentManager(QObject): # \return A list of metadata dictionaries matching the search criteria, or # an empty list if nothing was found. def intentMetadatas(self, definition_id: str, nozzle_name: str, material_id: str) -> List[Dict[str, Any]]: - registry = CuraApplication.getInstance().getContainerRegistry() + registry = cura.CuraApplication.CuraApplication.getInstance().getContainerRegistry() return registry.findContainersMetadata(type = "intent", definition = definition_id, variant = nozzle_name, material = material_id) ## Collects and returns all intent categories available for the given @@ -66,8 +67,8 @@ class IntentManager(QObject): # # \return A list of tuples of intent_category and quality_type. The actual # instance may vary per extruder. - def currentAvailableIntents(self) -> List[Tuple[str, str]]: - application = CuraApplication.getInstance() + def getCurrentAvailableIntents(self) -> List[Tuple[str, str]]: + application = cura.CuraApplication.CuraApplication.getInstance() global_stack = application.getGlobalContainerStack() if global_stack is None: return [("default", "normal")] @@ -79,7 +80,7 @@ class IntentManager(QObject): final_intent_ids = set() # type: Set[str] current_definition_id = global_stack.definition.getMetaDataEntry("id") - for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks(): + for extruder_stack in global_stack.extruderList: 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["quality_type"] in available_quality_types} @@ -100,12 +101,12 @@ class IntentManager(QObject): # \return List of all categories in the current configurations of all # extruders. def currentAvailableIntentCategories(self) -> List[str]: - global_stack = CuraApplication.getInstance().getGlobalContainerStack() + global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() if global_stack is None: return ["default"] current_definition_id = global_stack.definition.getMetaDataEntry("id") final_intent_categories = set() # type: Set[str] - for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): + for extruder_stack in global_stack.extruderList: 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)) @@ -115,16 +116,26 @@ class IntentManager(QObject): # the configuration, an extruder can't match the intent that the user # selects, or just when creating a new printer. def getDefaultIntent(self) -> InstanceContainer: - return CuraApplication.getInstance().empty_intent_container + 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", "") ## Apply intent on the stacks. + @pyqtSlot(str, str) def selectIntent(self, intent_category: str, quality_type: str) -> None: - application = CuraApplication.getInstance() + 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.getMetaDataEntry("id") - for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): + for extruder_stack in global_stack.extruderList: nozzle_name = extruder_stack.variant.getMetaDataEntry("name") material_id = extruder_stack.material.getMetaDataEntry("base_file") intent = application.getContainerRegistry().findContainers(definition = current_definition_id, variant = nozzle_name, material = material_id, quality_type = quality_type, intent_category = intent_category) @@ -134,8 +145,14 @@ class IntentManager(QObject): extruder_stack.intent = self.getDefaultIntent() application.getMachineManager().setQualityGroupByQualityType(quality_type) + if old_intent_category != intent_category: + self.intentCategoryChanged.emit() ## Selects the default intents on every extruder. def selectDefaultIntent(self) -> None: - for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): + application = cura.CuraApplication.CuraApplication.getInstance() + global_stack = application.getGlobalContainerStack() + if global_stack is None: + return + for extruder_stack in global_stack.extruderList: extruder_stack.intent = self.getDefaultIntent() diff --git a/resources/intent/smooth.inst.cfg b/resources/intent/smooth.inst.cfg index e0ec3713ea..cfaa18c2bf 100644 --- a/resources/intent/smooth.inst.cfg +++ b/resources/intent/smooth.inst.cfg @@ -1,11 +1,15 @@ [general] version = 4 name = Smooth (TEST INTENT) -definition = fdmprinter +definition = ultimaker3 [metadata] -setting_version = 7 +setting_version = 8 type = intent +intent_category = smooth +quality_type = draft +variant = AA 0.4 +material = generic_pla [values] - +infill_sparse_density = 10 \ No newline at end of file diff --git a/resources/intent/strong.inst.cfg b/resources/intent/strong.inst.cfg index 702778d598..e90b73d7d8 100644 --- a/resources/intent/strong.inst.cfg +++ b/resources/intent/strong.inst.cfg @@ -1,15 +1,15 @@ [general] version = 4 name = Strong (TEST INTENT) -definition = fdmprinter +definition = ultimaker3 [metadata] -setting_version = 7 +setting_version = 8 type = intent intent_category = engineering quality_type = draft -material = generic_abs variant = AA 0.4 +material = generic_pla [values] - +infill_sparse_density = 50 \ No newline at end of file diff --git a/resources/qml/Menus/IntentMenu.qml b/resources/qml/Menus/IntentMenu.qml index 410ab70eb7..8aba7cbde6 100644 --- a/resources/qml/Menus/IntentMenu.qml +++ b/resources/qml/Menus/IntentMenu.qml @@ -14,27 +14,47 @@ Menu property int extruderIndex: 0 - Cura.IntentModel + Cura.IntentCategoryModel { - id: intentModel + id: intentCategoryModel } Instantiator { - model: intentModel + model: intentCategoryModel - MenuItem + MenuItem //Section header. { text: model.name - checkable: true + enabled: false checked: false - Binding on checked + + property var per_category_intents: Cura.IntentModel { - when: Cura.MachineManager.activeStack != null - value: Cura.MachineManager.activeStack.intent == model.container + id: intentModel + intentCategory: model.intent_category + } + + property var intent_instantiator: Instantiator + { + model: intentModel + MenuItem + { + text: model.name + checkable: true + checked: false + Binding on checked + { + when: Cura.MachineManager.activeStack != null + value: Cura.MachineManager.activeStack.intent.metaData["intent_category"] == intentModel.intentCategory && Cura.MachineManager.activeStack.quality.metaData["quality_type"] == model.quality_type + } + exclusiveGroup: group + onTriggered: Cura.IntentManager.selectIntent(intentModel.intentCategory, model.quality_type) + } + + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) } - exclusiveGroup: group - onTriggered: Cura.MachineManager.activeStack.intent = model.container } onObjectAdded: menu.insertItem(index, object) diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py index c9d39f607e..337af817d1 100644 --- a/tests/TestIntentManager.py +++ b/tests/TestIntentManager.py @@ -12,8 +12,8 @@ from tests.Settings.MockContainer import MockContainer @pytest.fixture() def quality_manager(application, container_registry, global_stack) -> QualityManager: application.getGlobalContainerStack = MagicMock(return_value = global_stack) - with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): - with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): + with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value = application)): + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)): manager = QualityManager(application) return manager @@ -24,8 +24,8 @@ def intent_manager(application, extruder_manager, machine_manager, quality_manag application.getGlobalContainerStack = MagicMock(return_value = global_stack) application.getMachineManager = MagicMock(return_value = machine_manager) application.getQualityManager = MagicMock(return_value = quality_manager) - with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): - with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): + with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value = application)): + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)): manager = IntentManager() return manager @@ -65,16 +65,16 @@ def mockFindContainers(**kwargs) -> List[MockContainer]: def doSetup(application, extruder_manager, quality_manager, container_registry, global_stack) -> None: - container_registry.findContainersMetadata = MagicMock(side_effect=mockFindMetadata) - container_registry.findContainers = MagicMock(side_effect=mockFindContainers) + container_registry.findContainersMetadata = MagicMock(side_effect = mockFindMetadata) + container_registry.findContainers = MagicMock(side_effect = mockFindContainers) - quality_manager.getQualityGroups = MagicMock(return_value=mocked_qualitygroup_metadata) + quality_manager.getQualityGroups = MagicMock(return_value = mocked_qualitygroup_metadata) for _, qualitygroup in mocked_qualitygroup_metadata.items(): - qualitygroup.node_for_global = MagicMock(name="Node for global") - application.getQualityManager = MagicMock(return_value=quality_manager) + qualitygroup.node_for_global = MagicMock(name = "Node for global") + application.getQualityManager = MagicMock(return_value = quality_manager) global_stack.definition = MockContainer({"id": "ultimaker3"}) - application.getGlobalContainerStack = MagicMock(return_value=global_stack) + application.getGlobalContainerStack = MagicMock(return_value = global_stack) extruder_stack_a = MockContainer({"id": "Extruder The First"}) extruder_stack_a.variant = MockContainer({"name": "AA 0.4"}) @@ -83,33 +83,33 @@ def doSetup(application, extruder_manager, quality_manager, container_registry, extruder_stack_b.variant = MockContainer({"name": "AA 0.4"}) extruder_stack_b.material = MockContainer({"base_file": "generic_pla"}) - extruder_manager.getUsedExtruderStacks = MagicMock(return_value=[extruder_stack_a, extruder_stack_b]) + application.getGlobalContainerStack().extruderList = [extruder_stack_a, extruder_stack_b] + extruder_manager.getUsedExtruderStacks = MagicMock(return_value = [extruder_stack_a, extruder_stack_b]) def test_intentCategories(application, intent_manager, container_registry): # Mock .findContainersMetadata so we also test .intentMetadatas (the latter is mostly a wrapper around the former). - container_registry.findContainersMetadata = MagicMock(return_value=mocked_intent_metadata) + container_registry.findContainersMetadata = MagicMock(return_value = mocked_intent_metadata) - with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): - with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): + with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value = application)): + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)): categories = intent_manager.intentCategories("ultimaker3", "AA 0.4", "generic_pla") # type:List[str] assert "default" in categories, "default should always be in categories" assert "strong" in categories, "strong should be in categories" assert "smooth" in categories, "smooth should be in categories" -def test_currentAvailableIntents(application, extruder_manager, quality_manager, intent_manager, container_registry, global_stack): +def test_getCurrentAvailableIntents(application, extruder_manager, quality_manager, intent_manager, container_registry, global_stack): doSetup(application, extruder_manager, quality_manager, container_registry, global_stack) - with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): - with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): - with patch("cura.Settings.ExtruderManager.ExtruderManager.getInstance", MagicMock(return_value=extruder_manager)): - intents = intent_manager.currentAvailableIntents() - assert ("smooth", "normal") in intents - assert ("strong", "abnorm") in intents - #assert ("default", "normal") in intents # Pending to-do in 'IntentManager'. - #assert ("default", "abnorm") in intents # Pending to-do in 'IntentManager'. - assert len(intents) == 2 # Or 4? pending to-do in 'IntentManager'. + with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value = application)): + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)): + intents = intent_manager.getCurrentAvailableIntents() + assert ("smooth", "normal") in intents + assert ("strong", "abnorm") in intents + #assert ("default", "normal") in intents # Pending to-do in 'IntentManager'. + #assert ("default", "abnorm") in intents # Pending to-do in 'IntentManager'. + assert len(intents) == 2 # Or 4? pending to-do in 'IntentManager'. def test_currentAvailableIntentCategories(application, extruder_manager, quality_manager, intent_manager, container_registry, global_stack): @@ -131,7 +131,7 @@ def test_selectIntent(application, extruder_manager, quality_manager, intent_man with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): with patch("cura.Settings.ExtruderManager.ExtruderManager.getInstance", MagicMock(return_value=extruder_manager)): - intents = intent_manager.currentAvailableIntents() + intents = intent_manager.getCurrentAvailableIntents() for intent, quality in intents: intent_manager.selectIntent(intent, quality) extruder_stacks = extruder_manager.getUsedExtruderStacks()