diff --git a/cura/Machines/Models/IntentModel.py b/cura/Machines/Models/IntentModel.py index e105f012cd..4e96d8e152 100644 --- a/cura/Machines/Models/IntentModel.py +++ b/cura/Machines/Models/IntentModel.py @@ -1,18 +1,16 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - -from typing import Optional, List, Dict, Any +from typing import Optional, Dict, Any, Set, List from PyQt5.QtCore import Qt, QObject, pyqtProperty, pyqtSignal +import cura.CuraApplication from UM.Qt.ListModel import ListModel from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.SettingFunction import SettingFunction - from cura.Machines.ContainerTree import ContainerTree -from cura.Settings.ExtruderManager import ExtruderManager -from cura.Settings.IntentManager import IntentManager -import cura.CuraApplication +from cura.Machines.MaterialNode import MaterialNode +from cura.Machines.Models.MachineModelUtils import fetchLayerHeight +from cura.Machines.QualityGroup import QualityGroup class IntentModel(ListModel): @@ -65,54 +63,23 @@ class IntentModel(ListModel): return quality_groups = ContainerTree.getInstance().getCurrentQualityGroups() - container_tree = ContainerTree.getInstance() - machine_node = container_tree.machines[global_stack.definition.getId()] + material_nodes = self._getActiveMaterials() - # We can't just look at the active extruder, since it is possible for only one extruder to have an intent - # and the other extruders have no intent (eg, default). It is not possible for one extruder to have intent A and - # the other to have B. - # So we will use the first extruder that we find that has an intent that is not default as the "active" extruder + layer_heights_added = [] # type: List[float] - active_extruder = None - for extruder in global_stack.extruderList: - if not extruder.isEnabled: - continue - if extruder.intent.getMetaDataEntry("intent_category", "default") == "default": - if active_extruder is None: - active_extruder = extruder # If there is no extruder found and the intent is default, use that. - else: # We found an intent, use that extruder as "active" - active_extruder = extruder - - if not active_extruder: - return - active_variant_name = active_extruder.variant.getMetaDataEntry("name") - active_variant_node = machine_node.variants[active_variant_name] - active_material_node = active_variant_node.materials[active_extruder.material.getMetaDataEntry("base_file")] - - layer_heights_added = [] - for quality_id, quality_node in active_material_node.qualities.items(): - if quality_node.quality_type not in quality_groups: # Don't add the empty quality type (or anything else that would crash, defensively). - continue - quality_group = quality_groups[quality_node.quality_type] - layer_height = self._fetchLayerHeight(quality_group) - - for intent_id, intent_node in quality_node.intents.items(): - if intent_node.intent_category != self._intent_category: - continue - layer_heights_added.append(layer_height) - new_items.append({"name": quality_group.name, - "quality_type": quality_group.quality_type, - "layer_height": layer_height, - "available": quality_group.is_available, - "intent_category": self._intent_category - }) + for material_node in material_nodes: + intents = self._getIntentsForMaterial(material_node, quality_groups) + for intent in intents: + if intent["layer_height"] not in layer_heights_added: + new_items.append(intent) + layer_heights_added.append(intent["layer_height"]) # Now that we added all intents that we found something for, ensure that we set add ticks (and layer_heights) # for all groups that we don't have anything for (and set it to not available) for quality_tuple, quality_group in quality_groups.items(): # Add the intents that are of the correct category if quality_tuple[0] != self._intent_category: - layer_height = self._fetchLayerHeight(quality_group) + layer_height = fetchLayerHeight(quality_group) if layer_height not in layer_heights_added: new_items.append({"name": "Unavailable", "quality_type": "", @@ -124,35 +91,43 @@ class IntentModel(ListModel): new_items = sorted(new_items, key=lambda x: x["layer_height"]) self.setItems(new_items) - #TODO: Copied this from QualityProfilesDropdownMenuModel for the moment. This code duplication should be fixed. - def _fetchLayerHeight(self, quality_group) -> float: - global_stack = cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeMachine - if not self._layer_height_unit: - unit = global_stack.definition.getProperty("layer_height", "unit") - if not unit: - unit = "" - self._layer_height_unit = unit + ## Get the active materials for all extruders. No duplicates will be returned + def _getActiveMaterials(self) -> Set["MaterialNode"]: + global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() + if global_stack is None: + return set() - default_layer_height = global_stack.definition.getProperty("layer_height", "value") + container_tree = ContainerTree.getInstance() + machine_node = container_tree.machines[global_stack.definition.getId()] + nodes = set() # type: Set[MaterialNode] - # Get layer_height from the quality profile for the GlobalStack - if quality_group.node_for_global is None: - return float(default_layer_height) - container = quality_group.node_for_global.container + for extruder in global_stack.extruderList: + active_variant_name = extruder.variant.getMetaDataEntry("name") + active_variant_node = machine_node.variants[active_variant_name] + active_material_node = active_variant_node.materials[extruder.material.getMetaDataEntry("base_file")] + nodes.add(active_material_node) - layer_height = default_layer_height - if container and container.hasProperty("layer_height", "value"): - layer_height = container.getProperty("layer_height", "value") - else: - # Look for layer_height in the GlobalStack from material -> definition - container = global_stack.definition - if container and container.hasProperty("layer_height", "value"): - layer_height = container.getProperty("layer_height", "value") + return nodes - if isinstance(layer_height, SettingFunction): - layer_height = layer_height(global_stack) + def _getIntentsForMaterial(self, active_material_node: "MaterialNode", quality_groups: Dict[str, "QualityGroup"]) -> List[Dict[str, Any]]: + extruder_intents = [] # type: List[Dict[str, Any]] - return float(layer_height) + for quality_id, quality_node in active_material_node.qualities.items(): + if quality_node.quality_type not in quality_groups: # Don't add the empty quality type (or anything else that would crash, defensively). + continue + quality_group = quality_groups[quality_node.quality_type] + layer_height = fetchLayerHeight(quality_group) + + for intent_id, intent_node in quality_node.intents.items(): + if intent_node.intent_category != self._intent_category: + continue + extruder_intents.append({"name": quality_group.name, + "quality_type": quality_group.quality_type, + "layer_height": layer_height, + "available": quality_group.is_available, + "intent_category": self._intent_category + }) + return extruder_intents def __repr__(self): return str(self.items) diff --git a/cura/Machines/Models/MachineModelUtils.py b/cura/Machines/Models/MachineModelUtils.py new file mode 100644 index 0000000000..a23b1ff3a5 --- /dev/null +++ b/cura/Machines/Models/MachineModelUtils.py @@ -0,0 +1,37 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import TYPE_CHECKING + +from UM.Settings.SettingFunction import SettingFunction + +if TYPE_CHECKING: + from cura.Machines.QualityGroup import QualityGroup + +layer_height_unit = "" + + +def fetchLayerHeight(quality_group: "QualityGroup") -> float: + from cura.CuraApplication import CuraApplication + global_stack = CuraApplication.getInstance().getMachineManager().activeMachine + + default_layer_height = global_stack.definition.getProperty("layer_height", "value") + + # Get layer_height from the quality profile for the GlobalStack + if quality_group.node_for_global is None: + return float(default_layer_height) + container = quality_group.node_for_global.container + + layer_height = default_layer_height + if container and container.hasProperty("layer_height", "value"): + layer_height = container.getProperty("layer_height", "value") + else: + # Look for layer_height in the GlobalStack from material -> definition + container = global_stack.definition + if container and container.hasProperty("layer_height", "value"): + layer_height = container.getProperty("layer_height", "value") + + if isinstance(layer_height, SettingFunction): + layer_height = layer_height(global_stack) + + return float(layer_height) diff --git a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py index ab40f440ec..9bf1cc08a8 100644 --- a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py +++ b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py @@ -2,17 +2,12 @@ # Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import Qt, QTimer -from typing import TYPE_CHECKING - -from UM.Logger import Logger -from UM.Qt.ListModel import ListModel -from UM.Settings.SettingFunction import SettingFunction import cura.CuraApplication # Imported this way to prevent circular dependencies. +from UM.Logger import Logger +from UM.Qt.ListModel import ListModel from cura.Machines.ContainerTree import ContainerTree - -if TYPE_CHECKING: - from cura.Machines.QualityGroup import QualityGroup +from cura.Machines.Models.MachineModelUtils import fetchLayerHeight # @@ -76,6 +71,12 @@ class QualityProfilesDropDownMenuModel(ListModel): Logger.log("d", "No active GlobalStack, set quality profile model as empty.") return + if not self._layer_height_unit: + unit = global_stack.definition.getProperty("layer_height", "unit") + if not unit: + unit = "" + self._layer_height_unit = unit + # Check for material compatibility if not cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeMaterialsCompatible(): Logger.log("d", "No active material compatibility, set quality profile model as empty.") @@ -86,7 +87,7 @@ class QualityProfilesDropDownMenuModel(ListModel): item_list = [] for quality_group in quality_group_dict.values(): - layer_height = self._fetchLayerHeight(quality_group) + layer_height = fetchLayerHeight(quality_group) item = {"name": quality_group.name, "quality_type": quality_group.quality_type, @@ -102,32 +103,3 @@ class QualityProfilesDropDownMenuModel(ListModel): item_list = sorted(item_list, key = lambda x: x["layer_height"]) self.setItems(item_list) - - def _fetchLayerHeight(self, quality_group: "QualityGroup") -> float: - global_stack = cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeMachine - if not self._layer_height_unit: - unit = global_stack.definition.getProperty("layer_height", "unit") - if not unit: - unit = "" - self._layer_height_unit = unit - - default_layer_height = global_stack.definition.getProperty("layer_height", "value") - - # Get layer_height from the quality profile for the GlobalStack - if quality_group.node_for_global is None: - return float(default_layer_height) - container = quality_group.node_for_global.container - - layer_height = default_layer_height - if container and container.hasProperty("layer_height", "value"): - layer_height = container.getProperty("layer_height", "value") - else: - # Look for layer_height in the GlobalStack from material -> definition - container = global_stack.definition - if container and container.hasProperty("layer_height", "value"): - layer_height = container.getProperty("layer_height", "value") - - if isinstance(layer_height, SettingFunction): - layer_height = layer_height(global_stack) - - return float(layer_height)