From fa65875824138f20d3edf9a0409842a97aed7c46 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 19 Jun 2019 14:29:19 +0200 Subject: [PATCH 01/25] Add psuedocode/boilerplate for intent manager This won't run. Don't try to import this class yet. It's outlining what we need to implement to get intents per stack. It does no form of caching at this point. Build first, optimise later, right. Contributes to issue CURA-6091. --- cura/Machines/Models/IntentCategoryModel.py | 14 +++ cura/Settings/IntentManager.py | 95 +++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 cura/Machines/Models/IntentCategoryModel.py create mode 100644 cura/Settings/IntentManager.py diff --git a/cura/Machines/Models/IntentCategoryModel.py b/cura/Machines/Models/IntentCategoryModel.py new file mode 100644 index 0000000000..fb93c771a0 --- /dev/null +++ b/cura/Machines/Models/IntentCategoryModel.py @@ -0,0 +1,14 @@ +#Copyright (c) 2019 Ultimaker B.V. +#Cura is released under the terms of the LGPLv3 or higher. + +from UM.Qt.ListModel import ListModel +from cura.Settings.IntentManager import IntentManager + +class IntentCategoryModel(ListModel): + def __init__(self, intent_category: str): + self._intent_category = intent_category + + def update(self): + available_intents = IntentManager.getInstance().currentAvailableIntents() + result = filter(lambda intent: intent.getMetaDataEntry("intent_category") == self._intent_category, available_intents) + super().update(result) \ No newline at end of file diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py new file mode 100644 index 0000000000..3fb2657b00 --- /dev/null +++ b/cura/Settings/IntentManager.py @@ -0,0 +1,95 @@ +#Copyright (c) 2019 Ultimaker B.V. +#Cura is released under the terms of the LGPLv3 or higher. + +from PyQt5.QtCore import pyqtProperty, pyqtSignal +from typing import List, Tuple, TYPE_CHECKING +from cura.CuraApplication import CuraApplication +from cura.Machines.QualityManager import QualityManager +from cura.Settings.MachineManager import MachineManager +from UM.Settings.ContainerRegistry import ContainerRegistry + +if TYPE_CHECKING: + from UM.Settings.InstanceContainer import InstanceContainer + +## Front-end for querying which intents are available for a certain +# configuration. +# +# CURRENTLY THIS CLASS CONTAINS ONLY SOME PSEUDOCODE OF WHAT WE ARE SUPPOSED +# TO IMPLEMENT. +class IntentManager: + __instance = None + + def __init__(self) -> None: + MachineManager.activeStackChanged.connect(self.configurationChanged) + self.configurationChanged.connect(self.selectDefaultIntent) + pass + + @classmethod + def getInstance(cls): + if not cls.__instance: + cls.__instance = IntentManager() + return cls.__instance + + configurationChanged = pyqtSignal + + def intentMetadatas(self, definition_id: str, nozzle_id: str, material_id: str) -> List[str]: + #Return list of available intent profiles for any configuration. + #Use ContainerRegistry.findContainersMetadata for this. + return [] + + def intentCategories(self, definition_id: str, nozzle_id: str, material_id: str) -> List[str]: + categories = set() + for intent in self.intentMetadatas(definition_id, nozzle_id, material_id): + categories.add(intent["intent_category"]) + return list(categories) + + ## 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. + @pyqtProperty("QVariantList", notify = configurationChanged) + def currentAvailableIntents(self) -> List[Tuple[str, str]]: + final_intent_ids = {metadata["id"] for metadata in ContainerRegistry.getInstance().findContainersMetadata(type = "intent", definition = current_definition_id)} #All intents that match the global stack. + for extruder in all_extruders: + extruder_intent_ids = {metadata["id"] for metadata in self.intentMetadatas(current_definition_id, extruder_nozzle_id, extruder_material_id)} + final_intent_ids = final_intent_ids.intersection(extruder_intent_ids) + + result = set() + for intent_id in final_intent_ids: + intent = ContainerRegistry.getInstance().findContainers(id = intent_id)[0] + result.add((intent.getMetaDataEntry("intent_category"), intent.getMetaDataEntry("quality_type"))) + return list(result) + + ## List of intent categories to be displayed in the interface. + @pyqtProperty("QVariantList", notify = configurationChanged) + def currentAvailableIntentCategories(self) -> List[str]: + final_intent_categories = {metadata["intent_category"] for metadata in ContainerRegistry.getInstance().findContainersMetadata(type = "intent", definition = current_definition_id)} + for extruder in all_extruders: + final_intent_categories = final_intent_categories.intersection(self.intentCategories()) + return list(final_intent_categories) + + def defaultIntent(self) -> Tuple[str, str]: + default_quality_type = QualityManager.getInstance().getDefaultQualityType().quality_type + for intent in self.currentAvailableIntents(): + if intent.getMetaDataEntry("intent_category") == "default" and intent.getMetaDataEntry("quality_type") == default_quality_type: + return intent + else: #Fallback: Preferred quality type is not available for default category. + for intent in self.currentAvailableIntents(): + if intent.getMetaDataEntry("intent_category") == "default": + return intent + else: #Fallback: No default category. + if self.currentAvailableIntents(): + return self.currentAvailableIntents()[0] + else: + return CuraApplication.empty_intent_container + + def selectIntent(self, intent_category, quality_type): + for extruder in all_extruders: + extruder_stack.intent = ContainerRegistry.getInstance().findContainers(type = "intent", definition = current_definition_id, variant = extruder_nozzle_id, material = extruder_material_id)[0] + + def selectDefaultIntent(self) -> None: + category, quality_type = self.defaultIntent() + self.selectIntent(category, quality_type) \ No newline at end of file From 64e3a99ad307bd417d9413e300a2393fc02eac10 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 19 Jun 2019 16:52:54 +0200 Subject: [PATCH 02/25] Also select correct quality level on the stack when changing intent Contributes to issue CURA-6091. --- cura/Settings/IntentManager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 3fb2657b00..3c5a447866 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -89,6 +89,7 @@ class IntentManager: def selectIntent(self, intent_category, quality_type): for extruder in all_extruders: extruder_stack.intent = ContainerRegistry.getInstance().findContainers(type = "intent", definition = current_definition_id, variant = extruder_nozzle_id, material = extruder_material_id)[0] + extruder_stack.quality = ContainerRegistry.getInstance().findContainers(type = "quality", quality_type = quality_type) def selectDefaultIntent(self) -> None: category, quality_type = self.defaultIntent() From c83b9d158ba5aed644fa0315eabbfdbce83d3618 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Jun 2019 14:02:34 +0200 Subject: [PATCH 03/25] Final implementation of intentMetadatas and intentCategories Instead of pseudocode. Contributes to issue CURA-6091. --- cura/Settings/IntentManager.py | 19 +++++++++++++------ resources/intent/strong.inst.cfg | 4 ++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 3c5a447866..bae114fd97 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -2,7 +2,7 @@ #Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import pyqtProperty, pyqtSignal -from typing import List, Tuple, TYPE_CHECKING +from typing import Any, Dict, List, Tuple, TYPE_CHECKING from cura.CuraApplication import CuraApplication from cura.Machines.QualityManager import QualityManager from cura.Settings.MachineManager import MachineManager @@ -24,6 +24,7 @@ class IntentManager: self.configurationChanged.connect(self.selectDefaultIntent) pass + ## This class is a singleton. @classmethod def getInstance(cls): if not cls.__instance: @@ -32,15 +33,22 @@ class IntentManager: configurationChanged = pyqtSignal - def intentMetadatas(self, definition_id: str, nozzle_id: str, material_id: str) -> List[str]: - #Return list of available intent profiles for any configuration. - #Use ContainerRegistry.findContainersMetadata for this. - return [] + ## Gets the metadata dictionaries of all intent profiles for a given + # configuration. + # + # \param definition_id: ID of the printer. + # \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 = ContainerRegistry.getInstance() + return registry.findContainersMetadata(definition = definition_id, variant = nozzle_name, material_id = material_id) + ## def intentCategories(self, definition_id: str, nozzle_id: str, material_id: str) -> List[str]: 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) ## List of intents to be displayed in the interface. @@ -50,7 +58,6 @@ class IntentManager: # # \return A list of tuples of intent_category and quality_type. The actual # instance may vary per extruder. - @pyqtProperty("QVariantList", notify = configurationChanged) def currentAvailableIntents(self) -> List[Tuple[str, str]]: final_intent_ids = {metadata["id"] for metadata in ContainerRegistry.getInstance().findContainersMetadata(type = "intent", definition = current_definition_id)} #All intents that match the global stack. for extruder in all_extruders: diff --git a/resources/intent/strong.inst.cfg b/resources/intent/strong.inst.cfg index 4758841bf7..702778d598 100644 --- a/resources/intent/strong.inst.cfg +++ b/resources/intent/strong.inst.cfg @@ -6,6 +6,10 @@ definition = fdmprinter [metadata] setting_version = 7 type = intent +intent_category = engineering +quality_type = draft +material = generic_abs +variant = AA 0.4 [values] From 28e2569c86bbb9776dccbc0058d2628a8575f284 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Jun 2019 14:48:18 +0200 Subject: [PATCH 04/25] Implement complete code for currentAvailableIntents Slightly more complex than the original pseudocode was indicating: It needs to filter on the available quality types first in order to only show the quality types that can be printed with all extruders, but still show the union of all intents for those quality types. Contributes to issue CURA-6091. --- cura/Machines/QualityManager.py | 4 ++-- cura/Settings/IntentManager.py | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index 7da4f4f0d6..b4f8b8f679 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -194,9 +194,9 @@ class QualityManager(QObject): return quality_changes_group_dict # - # Gets all quality groups for the given machine. Both available and none available ones will be included. + # Gets all quality groups for the given machine. Both available and unavailable ones will be included. # It returns a dictionary with "quality_type"s as keys and "QualityGroup"s as values. - # Whether a QualityGroup is available can be unknown via the field QualityGroup.is_available. + # Whether a QualityGroup is available can be known via the field QualityGroup.is_available. # For more details, see QualityGroup. # def getQualityGroups(self, machine: "GlobalStack") -> Dict[str, QualityGroup]: diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index bae114fd97..98c4836d48 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -5,6 +5,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal from typing import Any, Dict, List, Tuple, TYPE_CHECKING from cura.CuraApplication import CuraApplication from cura.Machines.QualityManager import QualityManager +from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.MachineManager import MachineManager from UM.Settings.ContainerRegistry import ContainerRegistry @@ -59,15 +60,22 @@ class IntentManager: # \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]]: - final_intent_ids = {metadata["id"] for metadata in ContainerRegistry.getInstance().findContainersMetadata(type = "intent", definition = current_definition_id)} #All intents that match the global stack. - for extruder in all_extruders: - extruder_intent_ids = {metadata["id"] for metadata in self.intentMetadatas(current_definition_id, extruder_nozzle_id, extruder_material_id)} - final_intent_ids = final_intent_ids.intersection(extruder_intent_ids) + application = CuraApplication.getInstance() + quality_groups = application.getQualityManager().getQualityGroups(application.getGlobalContainerStack()) + available_quality_types = {quality_group.quality_type for quality_group in quality_groups if quality_group.node_for_global is not None} + + final_intent_ids = set() + global_stack = application.getGlobalContainerStack() + current_definition_id = global_stack.definition.getMetaDataEntry("id") + for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): + 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} result = set() for intent_id in final_intent_ids: - intent = ContainerRegistry.getInstance().findContainers(id = intent_id)[0] - result.add((intent.getMetaDataEntry("intent_category"), intent.getMetaDataEntry("quality_type"))) + intent_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = intent_id)[0] + result.add((intent_metadata["intent_category"], intent_metadata["quality_type"])) return list(result) ## List of intent categories to be displayed in the interface. From 2b775497b5b21ac7080a03296bba30146582f0c0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Jun 2019 15:06:24 +0200 Subject: [PATCH 05/25] Implement complete code for currentAvailableIntentCategories There is an inconsistency here with the available quality types. It's documented in the function for now. Contributes to issue CURA-6091. --- cura/Settings/IntentManager.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 98c4836d48..34b0d8cf68 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -78,12 +78,21 @@ class IntentManager: result.add((intent_metadata["intent_category"], intent_metadata["quality_type"])) return list(result) - ## List of intent categories to be displayed in the interface. - @pyqtProperty("QVariantList", notify = configurationChanged) + ## 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). def currentAvailableIntentCategories(self) -> List[str]: - final_intent_categories = {metadata["intent_category"] for metadata in ContainerRegistry.getInstance().findContainersMetadata(type = "intent", definition = current_definition_id)} - for extruder in all_extruders: - final_intent_categories = final_intent_categories.intersection(self.intentCategories()) + global_stack = CuraApplication.getInstance().getGlobalContainerStack() + current_definition_id = global_stack.definition.getMetaDataEntry("id") + final_intent_categories = set() + for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): + nozzle_name = extruder_stack.variant.getMetaDataEntry("name") + material_id = extruder_stack.material.getMetaDataEntry("base_file") + final_intent_categories |= self.intentCategories(current_definition_id, nozzle_name, material_id) return list(final_intent_categories) def defaultIntent(self) -> Tuple[str, str]: From 744fbec38c1ee7ddc9653c4398723f16744ce894 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Jun 2019 15:12:53 +0200 Subject: [PATCH 06/25] Implement defaultIntent We keep this function in as a way of documentation. Contributes to issue CURA-6091. --- cura/Settings/IntentManager.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 34b0d8cf68..528a060380 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -95,20 +95,11 @@ class IntentManager: final_intent_categories |= self.intentCategories(current_definition_id, nozzle_name, material_id) return list(final_intent_categories) - def defaultIntent(self) -> Tuple[str, str]: - default_quality_type = QualityManager.getInstance().getDefaultQualityType().quality_type - for intent in self.currentAvailableIntents(): - if intent.getMetaDataEntry("intent_category") == "default" and intent.getMetaDataEntry("quality_type") == default_quality_type: - return intent - else: #Fallback: Preferred quality type is not available for default category. - for intent in self.currentAvailableIntents(): - if intent.getMetaDataEntry("intent_category") == "default": - return intent - else: #Fallback: No default category. - if self.currentAvailableIntents(): - return self.currentAvailableIntents()[0] - else: - return CuraApplication.empty_intent_container + ## 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. + def defaultIntent(self) -> InstanceContainer: + return CuraApplication.getInstance().empty_intent_container def selectIntent(self, intent_category, quality_type): for extruder in all_extruders: From aa0bf2f6ba92f59ff6626d5e6d2333c65ad2ccd9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Jun 2019 15:27:07 +0200 Subject: [PATCH 07/25] Implement selectIntent Selects a certain intent profile, applying it to the stack. Contributes to issue CURA-6091. --- cura/Settings/IntentManager.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 528a060380..876d41a1b4 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -101,10 +101,21 @@ class IntentManager: def defaultIntent(self) -> InstanceContainer: return CuraApplication.getInstance().empty_intent_container - def selectIntent(self, intent_category, quality_type): - for extruder in all_extruders: - extruder_stack.intent = ContainerRegistry.getInstance().findContainers(type = "intent", definition = current_definition_id, variant = extruder_nozzle_id, material = extruder_material_id)[0] - extruder_stack.quality = ContainerRegistry.getInstance().findContainers(type = "quality", quality_type = quality_type) + ## Apply intent on the stacks. + def selectIntent(self, intent_category, quality_type) -> None: + application = CuraApplication.getInstance() + global_stack = application.getGlobalContainerStack() + current_definition_id = global_stack.definition.getMetaDataEntry("id") + for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): + nozzle_name = extruder_stack.variant.getMetaDataEntry("name") + material_id = extruder_stack.material.getMetaDataEntry("base_file") + intent = ContainerRegistry.getInstance().findContainers(definition = current_definition_id, variant = nozzle_name, material = material_id, quality_type = quality_type, intent_category = intent_category) + if intent: + extruder_stack.intent = intent[0] + else: + extruder_stack.intent = self.defaultIntent() + + application.getMachineManager().setQualityGroupByQualityType(quality_type) def selectDefaultIntent(self) -> None: category, quality_type = self.defaultIntent() From 96c111553a111229d5e10bc2b6f86c0eb62cd412 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Jun 2019 15:30:24 +0200 Subject: [PATCH 08/25] Implement selectDefaultIntent Bit of a weird one. Contributes to issue CURA-6091. --- cura/Settings/IntentManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 876d41a1b4..5af32b2154 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -118,5 +118,5 @@ class IntentManager: application.getMachineManager().setQualityGroupByQualityType(quality_type) def selectDefaultIntent(self) -> None: - category, quality_type = self.defaultIntent() - self.selectIntent(category, quality_type) \ No newline at end of file + for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): + extruder_stack.intent = self.defaultIntent() \ No newline at end of file From 53c387f34dfeadf1a8232939ffc6d55fcdbe487f Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 20 Jun 2019 16:04:37 +0200 Subject: [PATCH 09/25] Add TestIntentManager (mostly a dummy file), prevents future conflicts. [CURA-6091] --- tests/TestIntentManager.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/TestIntentManager.py diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py new file mode 100644 index 0000000000..a30de04565 --- /dev/null +++ b/tests/TestIntentManager.py @@ -0,0 +1,38 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from UM.Settings.ContainerRegistry import ContainerRegistry +from cura.Settings.ExtruderManager import ExtruderManager +from cura.Settings.MachineManager import MachineManager +from cura.Settings.IntentManager import IntentManager + +@pytest.fixture() +def global_stack(): + return MagicMock(name="Global Stack") + +@pytest.fixture() +def container_registry() -> ContainerRegistry: + return MagicMock(name = "ContainerRegistry") + + +@pytest.fixture() +def extruder_manager(application, container_registry) -> ExtruderManager: + if ExtruderManager.getInstance() is not None: + # Reset the data + ExtruderManager._ExtruderManager__instance = None + + with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): + manager = ExtruderManager() + return manager + + +@pytest.fixture() +def machine_manager(application, extruder_manager, container_registry, global_stack) -> MachineManager: + application.getExtruderManager = MagicMock(return_value = extruder_manager) + application.getGlobalContainerStack = MagicMock(return_value = global_stack) + with patch("cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance", MagicMock(return_value=container_registry)): + manager = MachineManager(application) + + return manager From ede6efb799fc521eefbc44f5c6b90441fa0b1203 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Jun 2019 15:42:35 +0200 Subject: [PATCH 10/25] Missing documentation Contributes to issue CURA-6091. --- cura/Settings/IntentManager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 5af32b2154..4cc167a3cd 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -85,6 +85,8 @@ class IntentManager: # 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. def currentAvailableIntentCategories(self) -> List[str]: global_stack = CuraApplication.getInstance().getGlobalContainerStack() current_definition_id = global_stack.definition.getMetaDataEntry("id") @@ -117,6 +119,7 @@ class IntentManager: application.getMachineManager().setQualityGroupByQualityType(quality_type) + ## Selects the default intents on every extruder. def selectDefaultIntent(self) -> None: for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): extruder_stack.intent = self.defaultIntent() \ No newline at end of file From fd80a6c1b6edc5b649e9c3a069ef64f9ad710d66 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 20 Jun 2019 17:48:52 +0200 Subject: [PATCH 11/25] Change some leftover pseudo-code to real code. Part of CURA-6091. --- cura/Settings/IntentManager.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 5af32b2154..66778b383c 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -1,13 +1,14 @@ #Copyright (c) 2019 Ultimaker B.V. #Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, pyqtSignal +from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal from typing import Any, Dict, List, Tuple, TYPE_CHECKING from cura.CuraApplication import CuraApplication from cura.Machines.QualityManager import QualityManager from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.MachineManager import MachineManager from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Settings.InstanceContainer import InstanceContainer if TYPE_CHECKING: from UM.Settings.InstanceContainer import InstanceContainer @@ -17,11 +18,12 @@ if TYPE_CHECKING: # # CURRENTLY THIS CLASS CONTAINS ONLY SOME PSEUDOCODE OF WHAT WE ARE SUPPOSED # TO IMPLEMENT. -class IntentManager: +class IntentManager(QObject): __instance = None def __init__(self) -> None: - MachineManager.activeStackChanged.connect(self.configurationChanged) + super().__init__() + CuraApplication.getInstance().getMachineManager().activeStackChanged.connect(self.configurationChanged) self.configurationChanged.connect(self.selectDefaultIntent) pass @@ -32,7 +34,7 @@ class IntentManager: cls.__instance = IntentManager() return cls.__instance - configurationChanged = pyqtSignal + configurationChanged = pyqtSignal() ## Gets the metadata dictionaries of all intent profiles for a given # configuration. @@ -41,7 +43,7 @@ class IntentManager: # \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 = ContainerRegistry.getInstance() + registry = CuraApplication.getInstance().getContainerRegistry() return registry.findContainersMetadata(definition = definition_id, variant = nozzle_name, material_id = material_id) ## @@ -74,7 +76,7 @@ class IntentManager: result = set() for intent_id in final_intent_ids: - intent_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = intent_id)[0] + intent_metadata = application.getContainerRegistry().findContainersMetadata(id = intent_id)[0] result.add((intent_metadata["intent_category"], intent_metadata["quality_type"])) return list(result) @@ -109,7 +111,7 @@ class IntentManager: for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): nozzle_name = extruder_stack.variant.getMetaDataEntry("name") material_id = extruder_stack.material.getMetaDataEntry("base_file") - intent = ContainerRegistry.getInstance().findContainers(definition = current_definition_id, variant = nozzle_name, material = material_id, quality_type = quality_type, intent_category = intent_category) + intent = application.getContainerRegistry().findContainers(definition = current_definition_id, variant = nozzle_name, material = material_id, quality_type = quality_type, intent_category = intent_category) if intent: extruder_stack.intent = intent[0] else: From f03c239041bdc48c8e85103781f236f3486672d7 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 20 Jun 2019 17:57:15 +0200 Subject: [PATCH 12/25] Update some documentation. [CURA-6091] --- cura/Settings/IntentManager.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index b7016c8114..9b0b5ff062 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -39,14 +39,22 @@ class IntentManager(QObject): ## Gets the metadata dictionaries of all intent profiles for a given # configuration. # - # \param definition_id: ID of the printer. + # \param definition_id ID of the printer. + # \param nozzle_name Name of the nozzle. + # \param material_id ID of the material. # \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() return registry.findContainersMetadata(definition = definition_id, variant = nozzle_name, material_id = material_id) - ## + ## 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. def intentCategories(self, definition_id: str, nozzle_id: str, material_id: str) -> List[str]: categories = set() for intent in self.intentMetadatas(definition_id, nozzle_id, material_id): From f339686c499de00a8cd17e7a10d0a7b221dfea8f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 21 Jun 2019 12:45:44 +0200 Subject: [PATCH 13/25] Implement category model Not just pseudocode. However this code is not yet tested. Contributes to issue CURA-6091. --- cura/Machines/Models/IntentCategoryModel.py | 41 +++++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/cura/Machines/Models/IntentCategoryModel.py b/cura/Machines/Models/IntentCategoryModel.py index fb93c771a0..789050e391 100644 --- a/cura/Machines/Models/IntentCategoryModel.py +++ b/cura/Machines/Models/IntentCategoryModel.py @@ -1,14 +1,47 @@ #Copyright (c) 2019 Ultimaker B.V. #Cura is released under the terms of the LGPLv3 or higher. -from UM.Qt.ListModel import ListModel -from cura.Settings.IntentManager import IntentManager +from PyQt5.QtCore import Qt +import collections +from cura.Settings.IntentManager import IntentManager +from UM.Qt.ListModel import ListModel + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + +## Lists the intent categories that are available for the current printer +# configuration. class IntentCategoryModel(ListModel): + NameRole = Qt.UserRole + 1 + IntentCategoryRole = Qt.UserRole + 2 + WeightRole = Qt.UserRole + 3 + + #Translations to user-visible string. Ordered by weight. + #TODO: Create a solution for this name and weight to be used dynamically. + name_translation = collections.OrderedDict() + name_translation["default"] = catalog.i18nc("@label", "Default") + name_translation["engineering"] = catalog.i18nc("@label", "Engineering") + name_translation["smooth"] = catalog.i18nc("@label", "Smooth") + + ## Creates a new model for a certain intent category. + # \param The category to list the intent profiles for. def __init__(self, intent_category: str): + super().__init__() self._intent_category = intent_category + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.IntentCategoryRole, "intent_category") + self.addRoleName(self.WeightRole, "weight") + + ## Updates the list of intents. def update(self): - available_intents = IntentManager.getInstance().currentAvailableIntents() - result = filter(lambda intent: intent.getMetaDataEntry("intent_category") == self._intent_category, available_intents) + available_categories = IntentManager.getInstance().currentAvailableIntentCategories() + result = [] + for category in available_categories: + result.append({ + "name": self.name_translation.get(category, catalog.i18nc("@label", "Unknown")), + "intent_category": category, + "weight": list(self.name_translation.items()).index(category) + }) super().update(result) \ No newline at end of file From 0f9de9935e8ce17febc0dc180a1be2ff416ca3ad Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 21 Jun 2019 14:37:10 +0200 Subject: [PATCH 14/25] Add unit-test for .intentCategories Part of CURA-6091. --- tests/TestIntentManager.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py index a30de04565..519fa49994 100644 --- a/tests/TestIntentManager.py +++ b/tests/TestIntentManager.py @@ -12,9 +12,16 @@ def global_stack(): return MagicMock(name="Global Stack") @pytest.fixture() -def container_registry() -> ContainerRegistry: - return MagicMock(name = "ContainerRegistry") +def container_registry(application, global_stack) -> ContainerRegistry: + result = MagicMock() + mocked_metadata = [{"id": "um3_aa4_pla_smooth", "GUID": "abcxyz", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "smooth"}, + {"id": "um3_aa4_pla_strong", "GUID": "defqrs", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "strong"}] + result.findContainersMetadata = MagicMock(return_value = mocked_metadata) + result.findContainerStacks = MagicMock(return_value = [global_stack]) + application.getContainerRegistry = MagicMock(return_value = result) + + return result @pytest.fixture() def extruder_manager(application, container_registry) -> ExtruderManager: @@ -32,7 +39,28 @@ def extruder_manager(application, container_registry) -> ExtruderManager: def machine_manager(application, extruder_manager, container_registry, global_stack) -> MachineManager: application.getExtruderManager = MagicMock(return_value = extruder_manager) application.getGlobalContainerStack = MagicMock(return_value = global_stack) - with patch("cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance", MagicMock(return_value=container_registry)): + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): manager = MachineManager(application) return manager + +# TODO: maybe put some definitions above in common file because they copy the ones in TestMachineManager (also there). + +@pytest.fixture() +def intent_manager(application, extruder_manager, machine_manager, container_registry, global_stack) -> IntentManager: + application.getExtruderManager = MagicMock(return_value = extruder_manager) + application.getGlobalContainerStack = MagicMock(return_value = global_stack) + application.getMachineManager = MagicMock(return_value = machine_manager) + 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 + +def test_intentCategories(application, intent_manager, 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" From 02516f0f477e5c4bf1da16c7698b3df2616b6bc2 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 21 Jun 2019 14:58:55 +0200 Subject: [PATCH 15/25] Refactor: Move common fixtures to 'conftest'. Part of CURA-6091. --- tests/TestIntentManager.py | 46 ++++--------------------------------- tests/TestMachineManager.py | 36 ----------------------------- tests/conftest.py | 39 +++++++++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 80 deletions(-) diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py index 519fa49994..01f8cb310b 100644 --- a/tests/TestIntentManager.py +++ b/tests/TestIntentManager.py @@ -2,50 +2,8 @@ from unittest.mock import MagicMock, patch import pytest -from UM.Settings.ContainerRegistry import ContainerRegistry -from cura.Settings.ExtruderManager import ExtruderManager -from cura.Settings.MachineManager import MachineManager from cura.Settings.IntentManager import IntentManager -@pytest.fixture() -def global_stack(): - return MagicMock(name="Global Stack") - -@pytest.fixture() -def container_registry(application, global_stack) -> ContainerRegistry: - result = MagicMock() - mocked_metadata = [{"id": "um3_aa4_pla_smooth", "GUID": "abcxyz", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "smooth"}, - {"id": "um3_aa4_pla_strong", "GUID": "defqrs", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "strong"}] - result.findContainersMetadata = MagicMock(return_value = mocked_metadata) - result.findContainerStacks = MagicMock(return_value = [global_stack]) - - application.getContainerRegistry = MagicMock(return_value = result) - - return result - -@pytest.fixture() -def extruder_manager(application, container_registry) -> ExtruderManager: - if ExtruderManager.getInstance() is not None: - # Reset the data - ExtruderManager._ExtruderManager__instance = None - - with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): - with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): - manager = ExtruderManager() - return manager - - -@pytest.fixture() -def machine_manager(application, extruder_manager, container_registry, global_stack) -> MachineManager: - application.getExtruderManager = MagicMock(return_value = extruder_manager) - application.getGlobalContainerStack = MagicMock(return_value = global_stack) - with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): - manager = MachineManager(application) - - return manager - -# TODO: maybe put some definitions above in common file because they copy the ones in TestMachineManager (also there). - @pytest.fixture() def intent_manager(application, extruder_manager, machine_manager, container_registry, global_stack) -> IntentManager: application.getExtruderManager = MagicMock(return_value = extruder_manager) @@ -58,6 +16,10 @@ def intent_manager(application, extruder_manager, machine_manager, container_reg return manager def test_intentCategories(application, intent_manager, container_registry): + mocked_metadata = [{"id": "um3_aa4_pla_smooth", "GUID": "abcxyz", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "smooth"}, + {"id": "um3_aa4_pla_strong", "GUID": "defqrs", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "strong"}] + container_registry.findContainersMetadata = MagicMock(return_value=mocked_metadata) + 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] diff --git a/tests/TestMachineManager.py b/tests/TestMachineManager.py index b1e155aa4f..e91cffb172 100644 --- a/tests/TestMachineManager.py +++ b/tests/TestMachineManager.py @@ -2,42 +2,6 @@ from unittest.mock import MagicMock, patch import pytest -from UM.Settings.ContainerRegistry import ContainerRegistry -from cura.Settings.ExtruderManager import ExtruderManager -from cura.Settings.MachineManager import MachineManager - - -@pytest.fixture() -def global_stack(): - return MagicMock(name="Global Stack") - -@pytest.fixture() -def container_registry() -> ContainerRegistry: - return MagicMock(name = "ContainerRegistry") - - -@pytest.fixture() -def extruder_manager(application, container_registry) -> ExtruderManager: - if ExtruderManager.getInstance() is not None: - # Reset the data - ExtruderManager._ExtruderManager__instance = None - - with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): - with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): - manager = ExtruderManager() - return manager - - -@pytest.fixture() -def machine_manager(application, extruder_manager, container_registry, global_stack) -> MachineManager: - application.getExtruderManager = MagicMock(return_value = extruder_manager) - application.getGlobalContainerStack = MagicMock(return_value = global_stack) - with patch("cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance", MagicMock(return_value=container_registry)): - manager = MachineManager(application) - - return manager - - def test_setActiveMachine(machine_manager): registry = MagicMock() diff --git a/tests/conftest.py b/tests/conftest.py index 7f46c202b3..876fb4f541 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,7 +3,7 @@ # The purpose of this class is to create fixtures or methods that can be shared among all tests. -import unittest.mock +from unittest.mock import MagicMock, patch import pytest # Prevents error: "PyCapsule_GetPointer called with incorrect name" with conflicting SIP configurations between Arcus and PyQt: Import Arcus and Savitar first! @@ -13,16 +13,51 @@ from UM.Qt.QtApplication import QtApplication # QtApplication import is require # Even though your IDE says these files are not used, don't believe it. It's lying. They need to be there. from cura.CuraApplication import CuraApplication +from cura.Settings.ExtruderManager import ExtruderManager +from cura.Settings.MachineManager import MachineManager from cura.UI.MachineActionManager import MachineActionManager +from UM.Settings.ContainerRegistry import ContainerRegistry # Create a CuraApplication object that will be shared among all tests. It needs to be initialized. # Since we need to use it more that once, we create the application the first time and use its instance afterwards. @pytest.fixture() def application() -> CuraApplication: - app = unittest.mock.MagicMock() + app = MagicMock() return app # Returns a MachineActionManager instance. @pytest.fixture() def machine_action_manager(application) -> MachineActionManager: return MachineActionManager(application) + +@pytest.fixture() +def global_stack(): + return MagicMock(name="Global Stack") + +@pytest.fixture() +def container_registry(application, global_stack) -> ContainerRegistry: + result = MagicMock() + result.findContainerStacks = MagicMock(return_value = [global_stack]) + application.getContainerRegistry = MagicMock(return_value = result) + return result + +@pytest.fixture() +def extruder_manager(application, container_registry) -> ExtruderManager: + if ExtruderManager.getInstance() is not None: + # Reset the data + ExtruderManager._ExtruderManager__instance = None + + with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): + manager = ExtruderManager() + return manager + + +@pytest.fixture() +def machine_manager(application, extruder_manager, container_registry, global_stack) -> MachineManager: + application.getExtruderManager = MagicMock(return_value = extruder_manager) + application.getGlobalContainerStack = MagicMock(return_value = global_stack) + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): + manager = MachineManager(application) + + return manager From 308fcb6b9f60c6b55095e0f63af694a83867a50a Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 21 Jun 2019 15:39:27 +0200 Subject: [PATCH 16/25] Fix typing for IntentManager Part of CURA-6091. --- cura/Settings/IntentManager.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 9b0b5ff062..1a38f8b78a 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -2,7 +2,7 @@ #Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal -from typing import Any, Dict, List, Tuple, TYPE_CHECKING +from typing import Any, Dict, List, Set, Tuple, TYPE_CHECKING from cura.CuraApplication import CuraApplication from cura.Machines.QualityManager import QualityManager from cura.Settings.ExtruderManager import ExtruderManager @@ -74,15 +74,17 @@ class IntentManager(QObject): quality_groups = application.getQualityManager().getQualityGroups(application.getGlobalContainerStack()) available_quality_types = {quality_group.quality_type for quality_group in quality_groups if quality_group.node_for_global is not None} - final_intent_ids = set() + final_intent_ids = set() #type: Set[str] global_stack = application.getGlobalContainerStack() + if global_stack is None: + return [("default", "normal")] current_definition_id = global_stack.definition.getMetaDataEntry("id") for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): 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} - result = set() + 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"])) @@ -99,12 +101,14 @@ class IntentManager(QObject): # extruders. def currentAvailableIntentCategories(self) -> List[str]: global_stack = CuraApplication.getInstance().getGlobalContainerStack() + if global_stack is None: + return ["default"] current_definition_id = global_stack.definition.getMetaDataEntry("id") - final_intent_categories = set() + final_intent_categories = set() #type: Set[str] for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): nozzle_name = extruder_stack.variant.getMetaDataEntry("name") material_id = extruder_stack.material.getMetaDataEntry("base_file") - final_intent_categories |= self.intentCategories(current_definition_id, nozzle_name, material_id) + final_intent_categories.update(self.intentCategories(current_definition_id, nozzle_name, material_id)) return list(final_intent_categories) ## The intent that gets selected by default when no intent is available for @@ -117,6 +121,8 @@ class IntentManager(QObject): def selectIntent(self, intent_category, quality_type) -> None: application = 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(): nozzle_name = extruder_stack.variant.getMetaDataEntry("name") @@ -132,4 +138,4 @@ class IntentManager(QObject): ## Selects the default intents on every extruder. def selectDefaultIntent(self) -> None: for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): - extruder_stack.intent = self.defaultIntent() \ No newline at end of file + extruder_stack.intent = self.defaultIntent() From 9e3f3c194cea1334d05c6945475fb5eb137c1ce0 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 25 Jun 2019 18:00:58 +0200 Subject: [PATCH 17/25] Small refactor: Dont retrieve global-stack twice. Part of CURA-6091 --- cura/Settings/IntentManager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 1a38f8b78a..c3fb41a0de 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -71,13 +71,13 @@ class IntentManager(QObject): # instance may vary per extruder. def currentAvailableIntents(self) -> List[Tuple[str, str]]: application = CuraApplication.getInstance() - quality_groups = application.getQualityManager().getQualityGroups(application.getGlobalContainerStack()) - available_quality_types = {quality_group.quality_type for quality_group in quality_groups if quality_group.node_for_global is not None} - - final_intent_ids = set() #type: Set[str] global_stack = application.getGlobalContainerStack() if global_stack is None: return [("default", "normal")] + quality_groups = application.getQualityManager().getQualityGroups(global_stack) + available_quality_types = {quality_group.quality_type for quality_group in quality_groups if quality_group.node_for_global is not None} + + final_intent_ids = set() #type: Set[str] current_definition_id = global_stack.definition.getMetaDataEntry("id") for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): nozzle_name = extruder_stack.variant.getMetaDataEntry("name") From e8a1c68d92d58bb7713fb17a7de7ec5e4cc79ccf Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 27 Jun 2019 17:07:26 +0200 Subject: [PATCH 18/25] Added (partial) test for 'currentAvailableIntents'. part of CURA-6091 --- cura/Settings/IntentManager.py | 8 ++-- tests/TestIntentManager.py | 78 +++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index c3fb41a0de..a5d71a918d 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -1,13 +1,10 @@ #Copyright (c) 2019 Ultimaker B.V. #Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal +from PyQt5.QtCore import QObject, pyqtSignal from typing import Any, Dict, List, Set, Tuple, TYPE_CHECKING from cura.CuraApplication import CuraApplication -from cura.Machines.QualityManager import QualityManager from cura.Settings.ExtruderManager import ExtruderManager -from cura.Settings.MachineManager import MachineManager -from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.InstanceContainer import InstanceContainer if TYPE_CHECKING: @@ -75,7 +72,8 @@ class IntentManager(QObject): if global_stack is None: return [("default", "normal")] quality_groups = application.getQualityManager().getQualityGroups(global_stack) - available_quality_types = {quality_group.quality_type for quality_group in quality_groups if quality_group.node_for_global is not None} + available_quality_types = {quality_group.quality_type for quality_group in quality_groups.values() if quality_group.node_for_global is not None} + # available_quality_types could just be 'quality_group.keys()', except for that the node_for_global may be None final_intent_ids = set() #type: Set[str] current_definition_id = global_stack.definition.getMetaDataEntry("id") diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py index 01f8cb310b..8b6a88b3b8 100644 --- a/tests/TestIntentManager.py +++ b/tests/TestIntentManager.py @@ -1,24 +1,45 @@ from unittest.mock import MagicMock, patch import pytest +from typing import Any, Dict, List from cura.Settings.IntentManager import IntentManager +from cura.Machines.QualityGroup import QualityGroup +from cura.Machines.QualityManager import QualityManager + +from tests.Settings.MockContainer import MockContainer @pytest.fixture() -def intent_manager(application, extruder_manager, machine_manager, container_registry, global_stack) -> IntentManager: +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)): + manager = QualityManager(application) + return manager + + +@pytest.fixture() +def intent_manager(application, extruder_manager, machine_manager, quality_manager, container_registry, global_stack) -> IntentManager: application.getExtruderManager = MagicMock(return_value = extruder_manager) 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)): manager = IntentManager() - return manager + +mocked_intent_metadata = [ + {"id": "um3_aa4_pla_smooth_normal", "GUID": "abcxyz", "definition": "ultimaker3", "variant": "AA 0.4", + "material_id": "generic_pla", "intent_category": "smooth", "quality_type": "normal"}, + {"id": "um3_aa4_pla_strong_abnorm", "GUID": "defqrs", "definition": "ultimaker3", "variant": "AA 0.4", + "material_id": "generic_pla", "intent_category": "strong", "quality_type": "abnorm"}] # type:List[Dict[str, str]] + + def test_intentCategories(application, intent_manager, container_registry): - mocked_metadata = [{"id": "um3_aa4_pla_smooth", "GUID": "abcxyz", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "smooth"}, - {"id": "um3_aa4_pla_strong", "GUID": "defqrs", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "strong"}] - container_registry.findContainersMetadata = MagicMock(return_value=mocked_metadata) + # 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) with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): @@ -26,3 +47,50 @@ def test_intentCategories(application, intent_manager, container_registry): 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): + mocked_qualitygroup_metadata = { + "normal": QualityGroup("um3_aa4_pla_normal", "normal"), + "abnorm": QualityGroup("um3_aa4_pla_abnorm", "abnorm")} # type:Dict[str, QualityGroup] + + def mockIntentMetadatas(**kwargs) -> List[Dict[str, Any]]: + if "id" in kwargs: + return [x for x in mocked_intent_metadata if x["id"] == kwargs["id"]] + else: + # TODO? switch on 'kwargs["definition_id"]', "nozzle_name", "material_id" -> ... or go 1 deeper + return mocked_intent_metadata + container_registry.findContainersMetadata = MagicMock(side_effect=mockIntentMetadatas) + + 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) + + extruder_stack_a = MockContainer({"id": "A"}) + extruder_stack_a.variant = MockContainer({"id": "A_variant"}) + extruder_stack_a.material = MockContainer({"id": "A_material"}) + extruder_stack_b = MockContainer({"id": "B"}) + extruder_stack_b.variant = MockContainer({"id": "B_variant"}) + extruder_stack_b.material = MockContainer({"id": "B_material"}) + # See previous TODO, the above doesn't really matter if intentmetadatas is mocked out the way it is, but it should. + + extruder_manager.getUsedExtruderStacks = MagicMock(return_value=[extruder_stack_a, extruder_stack_b]) + + 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 len(intents) == 2 + + +def test_currentAvailableIntentCategories(application, quality_manager, intent_manager, container_registry): + # def currentAvailableIntentCategories(self) -> List[str]: + pass + + +def test_selectIntent(application, intent_manager, container_registry): + # def selectIntent(self, intent_category, quality_type) -> None: + pass From 2843fc903b1fe8038886fdf1ee9f7eb30459cfe2 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 28 Jun 2019 11:09:42 +0200 Subject: [PATCH 19/25] First OK test-case for 'currentAvailableIntents'. part of CURA-6091 --- tests/TestIntentManager.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py index 8b6a88b3b8..af5a2437c3 100644 --- a/tests/TestIntentManager.py +++ b/tests/TestIntentManager.py @@ -49,31 +49,39 @@ def test_intentCategories(application, intent_manager, container_registry): assert "smooth" in categories, "smooth should be in categories" -def test_currentAvailableIntents(application, extruder_manager, quality_manager, intent_manager, container_registry): +def test_currentAvailableIntents(application, extruder_manager, quality_manager, intent_manager, container_registry, global_stack): mocked_qualitygroup_metadata = { "normal": QualityGroup("um3_aa4_pla_normal", "normal"), "abnorm": QualityGroup("um3_aa4_pla_abnorm", "abnorm")} # type:Dict[str, QualityGroup] - def mockIntentMetadatas(**kwargs) -> List[Dict[str, Any]]: + def mockFindMetadata(**kwargs) -> List[Dict[str, Any]]: if "id" in kwargs: return [x for x in mocked_intent_metadata if x["id"] == kwargs["id"]] else: - # TODO? switch on 'kwargs["definition_id"]', "nozzle_name", "material_id" -> ... or go 1 deeper - return mocked_intent_metadata - container_registry.findContainersMetadata = MagicMock(side_effect=mockIntentMetadatas) + result = [] + for data in mocked_intent_metadata: + should_add = True + for key, value in kwargs.items(): + should_add &= (data[key] == value) + if should_add: + result.append(data) + return result + container_registry.findContainersMetadata = MagicMock(side_effect=mockFindMetadata) 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) - extruder_stack_a = MockContainer({"id": "A"}) - extruder_stack_a.variant = MockContainer({"id": "A_variant"}) - extruder_stack_a.material = MockContainer({"id": "A_material"}) - extruder_stack_b = MockContainer({"id": "B"}) - extruder_stack_b.variant = MockContainer({"id": "B_variant"}) - extruder_stack_b.material = MockContainer({"id": "B_material"}) - # See previous TODO, the above doesn't really matter if intentmetadatas is mocked out the way it is, but it should. + global_stack.definition = MockContainer({"id": "ultimaker3"}) + application.getGlobalContainerStack = MagicMock(return_value=global_stack) + + extruder_stack_a = MockContainer({"id": "Extruder The First"}) + extruder_stack_a.variant = MockContainer({"name": "AA 0.4"}) + extruder_stack_a.material = MockContainer({"base_file": "generic_pla"}) + extruder_stack_b = MockContainer({"id": "Extruder II: Plastic Boogaloo"}) + 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]) From 6b918dbd1dabc49a894da35ab15b2c09369b4e33 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 28 Jun 2019 12:04:04 +0200 Subject: [PATCH 20/25] Fix typing in IntentCategoryModel. --- cura/Machines/Models/IntentCategoryModel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Machines/Models/IntentCategoryModel.py b/cura/Machines/Models/IntentCategoryModel.py index 789050e391..ac69191ca1 100644 --- a/cura/Machines/Models/IntentCategoryModel.py +++ b/cura/Machines/Models/IntentCategoryModel.py @@ -19,14 +19,14 @@ class IntentCategoryModel(ListModel): #Translations to user-visible string. Ordered by weight. #TODO: Create a solution for this name and weight to be used dynamically. - name_translation = collections.OrderedDict() + name_translation = collections.OrderedDict() #type: "collections.OrderedDict[str,str]" name_translation["default"] = catalog.i18nc("@label", "Default") name_translation["engineering"] = catalog.i18nc("@label", "Engineering") name_translation["smooth"] = catalog.i18nc("@label", "Smooth") ## Creates a new model for a certain intent category. # \param The category to list the intent profiles for. - def __init__(self, intent_category: str): + def __init__(self, intent_category: str) -> None: super().__init__() self._intent_category = intent_category @@ -35,7 +35,7 @@ class IntentCategoryModel(ListModel): self.addRoleName(self.WeightRole, "weight") ## Updates the list of intents. - def update(self): + def update(self) -> None: available_categories = IntentManager.getInstance().currentAvailableIntentCategories() result = [] for category in available_categories: From 810fee37eb39c52cdb42b705e35e8ffa85e81725 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 28 Jun 2019 12:10:39 +0200 Subject: [PATCH 21/25] Start to test other IntentManager functions. Very rudimentary at the moment, need to split the method into 3, and make a class for the setup. This also uncovered that the currentAvailableIntents doesn't (unless the global stack is missing) retrun any default intents, while currentAvailableIntentCategories does do that. Since it's not clear how we're going to handle that right now, I made a TODO in the code, which of course will have to be fixed before this/these branch/es are merged. part of CURA-6091 --- cura/Settings/IntentManager.py | 4 +++- tests/TestIntentManager.py | 25 ++++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index a5d71a918d..db7eb7b40a 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -71,9 +71,11 @@ class IntentManager(QObject): 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 'currentAvailableIntentCategoreis', which _does_ return default. quality_groups = application.getQualityManager().getQualityGroups(global_stack) available_quality_types = {quality_group.quality_type for quality_group in quality_groups.values() if quality_group.node_for_global is not None} - # available_quality_types could just be 'quality_group.keys()', except for that the node_for_global may be None final_intent_ids = set() #type: Set[str] current_definition_id = global_stack.definition.getMetaDataEntry("id") diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py index af5a2437c3..1012f8e2eb 100644 --- a/tests/TestIntentManager.py +++ b/tests/TestIntentManager.py @@ -50,6 +50,8 @@ def test_intentCategories(application, intent_manager, container_registry): def test_currentAvailableIntents(application, extruder_manager, quality_manager, intent_manager, container_registry, global_stack): + # This also tests 'currentAvailableIntentCategories' and 'selectIntent' since the methods are so similar + mocked_qualitygroup_metadata = { "normal": QualityGroup("um3_aa4_pla_normal", "normal"), "abnorm": QualityGroup("um3_aa4_pla_abnorm", "abnorm")} # type:Dict[str, QualityGroup] @@ -88,17 +90,22 @@ def test_currentAvailableIntents(application, extruder_manager, 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.Settings.ExtruderManager.ExtruderManager.getInstance", MagicMock(return_value=extruder_manager)): + intents = intent_manager.currentAvailableIntents() assert ("smooth", "normal") in intents assert ("strong", "abnorm") in intents - assert len(intents) == 2 + #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'. + categories = intent_manager.currentAvailableIntentCategories() + assert "default" in categories # Currently inconsistent with 'currentAvailableIntents'! + assert "smooth" in categories + assert "strong" in categories + assert len(categories) == 3 -def test_currentAvailableIntentCategories(application, quality_manager, intent_manager, container_registry): - # def currentAvailableIntentCategories(self) -> List[str]: - pass - - -def test_selectIntent(application, intent_manager, container_registry): - # def selectIntent(self, intent_category, quality_type) -> None: - pass + for intent, quality in intents: + intent_manager.selectIntent(intent, quality) + assert extruder_stack_a.intent is not None + assert extruder_stack_b.intent is not None + # ... need MachineManager for this, split up methods anyway -> make into class, see examples others From ffe951523611235f7f6563f5676534c43a351824 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 2 Jul 2019 08:57:40 +0200 Subject: [PATCH 22/25] Refactor: Split up single test method in smaller ones. part of CURA-6091 --- tests/TestIntentManager.py | 82 ++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py index 1012f8e2eb..22d4fccbb1 100644 --- a/tests/TestIntentManager.py +++ b/tests/TestIntentManager.py @@ -36,38 +36,26 @@ mocked_intent_metadata = [ {"id": "um3_aa4_pla_strong_abnorm", "GUID": "defqrs", "definition": "ultimaker3", "variant": "AA 0.4", "material_id": "generic_pla", "intent_category": "strong", "quality_type": "abnorm"}] # type:List[Dict[str, str]] - -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) - - 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" +mocked_qualitygroup_metadata = { + "normal": QualityGroup("um3_aa4_pla_normal", "normal"), + "abnorm": QualityGroup("um3_aa4_pla_abnorm", "abnorm")} # type:Dict[str, QualityGroup] -def test_currentAvailableIntents(application, extruder_manager, quality_manager, intent_manager, container_registry, global_stack): - # This also tests 'currentAvailableIntentCategories' and 'selectIntent' since the methods are so similar +def mockFindMetadata(**kwargs) -> List[Dict[str, Any]]: + if "id" in kwargs: + return [x for x in mocked_intent_metadata if x["id"] == kwargs["id"]] + else: + result = [] + for data in mocked_intent_metadata: + should_add = True + for key, value in kwargs.items(): + should_add &= (data[key] == value) + if should_add: + result.append(data) + return result - mocked_qualitygroup_metadata = { - "normal": QualityGroup("um3_aa4_pla_normal", "normal"), - "abnorm": QualityGroup("um3_aa4_pla_abnorm", "abnorm")} # type:Dict[str, QualityGroup] - def mockFindMetadata(**kwargs) -> List[Dict[str, Any]]: - if "id" in kwargs: - return [x for x in mocked_intent_metadata if x["id"] == kwargs["id"]] - else: - result = [] - for data in mocked_intent_metadata: - should_add = True - for key, value in kwargs.items(): - should_add &= (data[key] == value) - if should_add: - result.append(data) - return result +def doSetup(application, extruder_manager, quality_manager, container_registry, global_stack) -> None: container_registry.findContainersMetadata = MagicMock(side_effect=mockFindMetadata) quality_manager.getQualityGroups = MagicMock(return_value=mocked_qualitygroup_metadata) @@ -87,10 +75,25 @@ def test_currentAvailableIntents(application, extruder_manager, quality_manager, 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) + + 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): + 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 @@ -98,14 +101,31 @@ def test_currentAvailableIntents(application, extruder_manager, quality_manager, #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): + 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)): categories = intent_manager.currentAvailableIntentCategories() assert "default" in categories # Currently inconsistent with 'currentAvailableIntents'! assert "smooth" in categories assert "strong" in categories assert len(categories) == 3 + +def test_currentAvailableIntentCategories(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() for intent, quality in intents: intent_manager.selectIntent(intent, quality) - assert extruder_stack_a.intent is not None - assert extruder_stack_b.intent is not None + extruder_stacks = extruder_manager.getUsedExtruderStacks() + assert len(extruder_stacks) == 2 + assert extruder_stacks[0].intent is not None + assert extruder_stacks[1].intent is not None # ... need MachineManager for this, split up methods anyway -> make into class, see examples others From 50cbf71f6842d0dbcdb354f3c21755d7661d9876 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 2 Jul 2019 13:15:18 +0200 Subject: [PATCH 23/25] Fixed test 'selectIntent' for IntentManager-tests. part of CURA-6091 --- tests/TestIntentManager.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/TestIntentManager.py b/tests/TestIntentManager.py index 22d4fccbb1..c9d39f607e 100644 --- a/tests/TestIntentManager.py +++ b/tests/TestIntentManager.py @@ -49,14 +49,24 @@ def mockFindMetadata(**kwargs) -> List[Dict[str, Any]]: for data in mocked_intent_metadata: should_add = True for key, value in kwargs.items(): - should_add &= (data[key] == value) + if key in data.keys(): + should_add &= (data[key] == value) if should_add: result.append(data) return result +def mockFindContainers(**kwargs) -> List[MockContainer]: + result = [] + metadatas = mockFindMetadata(**kwargs) + for metadata in metadatas: + result.append(MockContainer(metadata)) + return result + + 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) quality_manager.getQualityGroups = MagicMock(return_value=mocked_qualitygroup_metadata) for _, qualitygroup in mocked_qualitygroup_metadata.items(): @@ -115,7 +125,7 @@ def test_currentAvailableIntentCategories(application, extruder_manager, quality assert len(categories) == 3 -def test_currentAvailableIntentCategories(application, extruder_manager, quality_manager, intent_manager, container_registry, global_stack): +def test_selectIntent(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)): @@ -124,8 +134,7 @@ def test_currentAvailableIntentCategories(application, extruder_manager, quality intents = intent_manager.currentAvailableIntents() for intent, quality in intents: intent_manager.selectIntent(intent, quality) - extruder_stacks = extruder_manager.getUsedExtruderStacks() + extruder_stacks = extruder_manager.getUsedExtruderStacks() assert len(extruder_stacks) == 2 - assert extruder_stacks[0].intent is not None - assert extruder_stacks[1].intent is not None - # ... need MachineManager for this, split up methods anyway -> make into class, see examples others + assert extruder_stacks[0].intent.getMetaDataEntry("intent_category") == intent + assert extruder_stacks[1].intent.getMetaDataEntry("intent_category") == intent From b463b0c35bfeabc6a07aaf984e52321819ea3a4e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 2 Jul 2019 14:08:11 +0200 Subject: [PATCH 24/25] Fix CuraVerison.py configuration --- CMakeLists.txt | 2 ++ cura/ApplicationMetadata.py | 6 +++++- cura/CuraVersion.py.in | 3 --- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4406fd4856..d5109f0f7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,8 @@ set(CURA_CLOUD_API_ROOT "" CACHE STRING "Alternative Cura cloud API root") set(CURA_CLOUD_API_VERSION "" CACHE STRING "Alternative Cura cloud API version") configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY) + + configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) diff --git a/cura/ApplicationMetadata.py b/cura/ApplicationMetadata.py index aea68c0af5..73eb9bb288 100644 --- a/cura/ApplicationMetadata.py +++ b/cura/ApplicationMetadata.py @@ -9,6 +9,7 @@ DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura" DEFAULT_CURA_VERSION = "master" DEFAULT_CURA_BUILD_TYPE = "" DEFAULT_CURA_DEBUG_MODE = False +DEFAULT_CURA_SDK_VERSION = "6.1.0" try: from cura.CuraVersion import CuraAppName # type: ignore @@ -41,4 +42,7 @@ try: except ImportError: CuraDebugMode = DEFAULT_CURA_DEBUG_MODE -from cura.CuraVersion import CuraSDKVersion # type: ignore +# Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for +# example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the +# CuraVersion.py.in template. +CuraSDKVersion = "6.1.0" diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in index abbc3a0a0d..4583e76f67 100644 --- a/cura/CuraVersion.py.in +++ b/cura/CuraVersion.py.in @@ -6,9 +6,6 @@ CuraAppDisplayName = "@CURA_APP_DISPLAY_NAME@" CuraVersion = "@CURA_VERSION@" CuraBuildType = "@CURA_BUILDTYPE@" CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False - -CuraSDKVersion = "6.1.0" - CuraCloudAPIRoot = "@CURA_CLOUD_API_ROOT@" CuraCloudAPIVersion = "@CURA_CLOUD_API_VERSION@" CuraCloudAccountAPIRoot = "@CURA_CLOUD_ACCOUNT_API_ROOT@" From 270cf28ea12a6eba74a291565f7a0b7ba9dafe32 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 2 Jul 2019 14:43:56 +0200 Subject: [PATCH 25/25] Fix comments code review IntentManager. part of CURA-6091 --- cura/Settings/CuraContainerStack.py | 2 +- cura/Settings/IntentManager.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index 278bc1dc4f..f3159467c6 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -92,7 +92,7 @@ class CuraContainerStack(ContainerStack): ## Set the intent container. # # \param new_intent The new intent container. It is expected to have a "type" metadata entry with the value "intent". - def setIntent(self, new_intent: InstanceContainer, postpone_emit: bool = False) -> None: + def setIntent(self, new_intent: InstanceContainer, *, postpone_emit: bool = False) -> None: self.replaceContainer(_ContainerIndexes.Intent, new_intent, postpone_emit=postpone_emit) ## Get the quality container. diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index db7eb7b40a..e16115ba2a 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -77,14 +77,14 @@ class IntentManager(QObject): quality_groups = application.getQualityManager().getQualityGroups(global_stack) 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] + final_intent_ids = set() # type: Set[str] current_definition_id = global_stack.definition.getMetaDataEntry("id") for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): 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} - result = set() #type: Set[Tuple[str, str]] + 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"])) @@ -104,7 +104,7 @@ class IntentManager(QObject): if global_stack is None: return ["default"] current_definition_id = global_stack.definition.getMetaDataEntry("id") - final_intent_categories = set() #type: Set[str] + final_intent_categories = set() # type: Set[str] for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): nozzle_name = extruder_stack.variant.getMetaDataEntry("name") material_id = extruder_stack.material.getMetaDataEntry("base_file") @@ -114,11 +114,11 @@ class IntentManager(QObject): ## 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. - def defaultIntent(self) -> InstanceContainer: + def getDefaultIntent(self) -> InstanceContainer: return CuraApplication.getInstance().empty_intent_container ## Apply intent on the stacks. - def selectIntent(self, intent_category, quality_type) -> None: + def selectIntent(self, intent_category: str, quality_type: str) -> None: application = CuraApplication.getInstance() global_stack = application.getGlobalContainerStack() if global_stack is None: @@ -131,11 +131,11 @@ class IntentManager(QObject): if intent: extruder_stack.intent = intent[0] else: - extruder_stack.intent = self.defaultIntent() + extruder_stack.intent = self.getDefaultIntent() application.getMachineManager().setQualityGroupByQualityType(quality_type) ## Selects the default intents on every extruder. def selectDefaultIntent(self) -> None: for extruder_stack in ExtruderManager.getInstance().getUsedExtruderStacks(): - extruder_stack.intent = self.defaultIntent() + extruder_stack.intent = self.getDefaultIntent()