Browse Source

Update getQualityGroups to use the new ContainerTree structure

Look how greatly this is now simplified.
The fallbacks for which material nodes to check is not yet implemented. Will do that next.

Contributes to issue CURA-6600.
Ghostkeeper 5 years ago
parent
commit
91e14a90b2
1 changed files with 39 additions and 173 deletions
  1. 39 173
      cura/Machines/QualityManager.py

+ 39 - 173
cura/Machines/QualityManager.py

@@ -14,6 +14,7 @@ from UM.Decorators import deprecated
 import cura.CuraApplication
 from cura.Settings.ExtruderStack import ExtruderStack
 
+from cura.Machines.ContainerTree import ContainerTree  # The implementation that replaces this manager, to keep the deprecated interface working.
 from .QualityGroup import QualityGroup
 from .QualityNode import QualityNode
 
@@ -160,26 +161,6 @@ class QualityManager(QObject):
         # update the cache table
         self._update_timer.start()
 
-    # Updates the given quality groups' availabilities according to which extruders are being used/ enabled.
-    def _updateQualityGroupsAvailability(self, machine: "GlobalStack", quality_group_list) -> None:
-        used_extruders = set()
-        for i in range(machine.getProperty("machine_extruder_count", "value")):
-            if str(i) in machine.extruders and machine.extruders[str(i)].isEnabled:
-                used_extruders.add(str(i))
-
-        # Update the "is_available" flag for each quality group.
-        for quality_group in quality_group_list:
-            is_available = True
-            if quality_group.node_for_global is None:
-                is_available = False
-            if is_available:
-                for position in used_extruders:
-                    if position not in quality_group.nodes_for_extruders:
-                        is_available = False
-                        break
-
-            quality_group.is_available = is_available
-
     # Returns a dict of "custom profile name" -> QualityChangesGroup
     def getQualityChangesGroups(self, machine: "GlobalStack") -> dict:
         machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
@@ -203,160 +184,45 @@ class QualityManager(QObject):
 
         return quality_changes_group_dict
 
+    ##  Gets the quality groups for the current printer.
     #
-    # 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 known via the field QualityGroup.is_available.
-    # For more details, see QualityGroup.
-    #
-    def getQualityGroups(self, machine: "GlobalStack") -> Dict[str, QualityGroup]:
-        machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
-
-        # To find the quality container for the GlobalStack, check in the following fall-back manner:
-        #   (1) the machine-specific node
-        #   (2) the generic node
-        machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
-
-        # Check if this machine has specific quality profiles for its extruders, if so, when looking up extruder
-        # qualities, we should not fall back to use the global qualities.
-        has_extruder_specific_qualities = False
-        if machine_node:
-            if machine_node.children_map:
-                has_extruder_specific_qualities = True
-
-        default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)
-
-        nodes_to_check = []  # type: List[QualityNode]
-        if machine_node is not None:
-            nodes_to_check.append(machine_node)
-        if default_machine_node is not None:
-            nodes_to_check.append(default_machine_node)
-
-        # Iterate over all quality_types in the machine node
-        quality_group_dict = {}
-        for node in nodes_to_check:
-            if node and node.quality_type_map:
-                quality_node = list(node.quality_type_map.values())[0]
-                is_global_quality = parseBool(quality_node.getMetaDataEntry("global_quality", False))
-                if not is_global_quality:
-                    continue
-
-                for quality_type, quality_node in node.quality_type_map.items():
-                    quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
-                    quality_group.setGlobalNode(quality_node)
-                    quality_group_dict[quality_type] = quality_group
-                break
-
-        buildplate_name = machine.getBuildplateName()
-
-        # Iterate over all extruders to find quality containers for each extruder
-        for position, extruder in machine.extruders.items():
-            nozzle_name = None
-            if extruder.variant.getId() != "empty_variant":
-                nozzle_name = extruder.variant.getName()
-
-            # This is a list of root material IDs to use for searching for suitable quality profiles.
-            # The root material IDs in this list are in prioritized order.
-            root_material_id_list = []
-            has_material = False  # flag indicating whether this extruder has a material assigned
-            root_material_id = None
-            if extruder.material.getId() != "empty_material":
-                has_material = True
-                root_material_id = extruder.material.getMetaDataEntry("base_file")
-                # Convert possible generic_pla_175 -> generic_pla
-                root_material_id = self._material_manager.getRootMaterialIDWithoutDiameter(root_material_id)
-                root_material_id_list.append(root_material_id)
-
-                # Also try to get the fallback materials
-                fallback_ids = self._material_manager.getFallBackMaterialIdsByMaterial(extruder.material)
-
-                if fallback_ids:
-                    root_material_id_list.extend(fallback_ids)
-
-                # Weed out duplicates while preserving the order.
-                seen = set()  # type: Set[str]
-                root_material_id_list = [x for x in root_material_id_list if x not in seen and not seen.add(x)]  # type: ignore
-
-            # Here we construct a list of nodes we want to look for qualities with the highest priority first.
-            # The use case is that, when we look for qualities for a machine, we first want to search in the following
-            # order:
-            #   1. machine-nozzle-buildplate-and-material-specific qualities if exist
-            #   2. machine-nozzle-and-material-specific qualities if exist
-            #   3. machine-nozzle-specific qualities if exist
-            #   4. machine-material-specific qualities if exist
-            #   5. machine-specific global qualities if exist, otherwise generic global qualities
-            #      NOTE: We DO NOT fail back to generic global qualities if machine-specific global qualities exist.
-            #            This is because when a machine defines its own global qualities such as Normal, Fine, etc.,
-            #            it is intended to maintain those specific qualities ONLY. If we still fail back to the generic
-            #            global qualities, there can be unimplemented quality types e.g. "coarse", and this is not
-            #            correct.
-            # Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into
-            # the list with priorities as the order. Later, we just need to loop over each node in this list and fetch
-            # qualities from there.
-            node_info_list_0 = [nozzle_name, buildplate_name, root_material_id]  # type: List[Optional[str]]
-            nodes_to_check = []
-
-            # This function tries to recursively find the deepest (the most specific) branch and add those nodes to
-            # the search list in the order described above. So, by iterating over that search node list, we first look
-            # in the more specific branches and then the less specific (generic) ones.
-            def addNodesToCheck(node: Optional[QualityNode], nodes_to_check_list: List[QualityNode], node_info_list, node_info_idx: int) -> None:
-                if node is None:
-                    return
-
-                if node_info_idx < len(node_info_list):
-                    node_name = node_info_list[node_info_idx]
-                    if node_name is not None:
-                        current_node = node.getChildNode(node_name)
-                        if current_node is not None and has_material:
-                            addNodesToCheck(current_node, nodes_to_check_list, node_info_list, node_info_idx + 1)
-
-                if has_material:
-                    for rmid in root_material_id_list:
-                        material_node = node.getChildNode(rmid)
-                        if material_node:
-                            nodes_to_check_list.append(material_node)
-                            break
-
-                nodes_to_check_list.append(node)
-
-            addNodesToCheck(machine_node, nodes_to_check, node_info_list_0, 0)
-
-            # The last fall back will be the global qualities (either from the machine-specific node or the generic
-            # node), but we only use one. For details see the overview comments above.
-
-            if machine_node is not None and machine_node.quality_type_map:
-                nodes_to_check += [machine_node]
-            elif default_machine_node is not None:
-                nodes_to_check += [default_machine_node]
-
-            for node_idx, node in enumerate(nodes_to_check):
-                if node and node.quality_type_map:
-                    if has_extruder_specific_qualities:
-                        # Only include variant qualities; skip non global qualities
-                        quality_node = list(node.quality_type_map.values())[0]
-                        is_global_quality = parseBool(quality_node.getMetaDataEntry("global_quality", False))
-                        if is_global_quality:
-                            continue
-
-                    for quality_type, quality_node in node.quality_type_map.items():
-                        if quality_type not in quality_group_dict:
-                            quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type)
-                            quality_group_dict[quality_type] = quality_group
-
-                        quality_group = quality_group_dict[quality_type]
-                        if position not in quality_group.nodes_for_extruders:
-                            quality_group.setExtruderNode(position, quality_node)
-
-                # If the machine has its own specific qualities, for extruders, it should skip the global qualities
-                # and use the material/variant specific qualities.
-                if has_extruder_specific_qualities:
-                    if node_idx == len(nodes_to_check) - 1:
-                        break
-
-        # Update availabilities for each quality group
-        self._updateQualityGroupsAvailability(machine, quality_group_dict.values())
-
-        return quality_group_dict
+    #   Both available and unavailable quality groups will be included. Whether
+    #   a quality group is available can be known via the field
+    #   ``QualityGroup.is_available``. For more details, see QualityGroup.
+    #   \return A dictionary with quality types as keys and the quality groups
+    #   for those types as values.
+    def getQualityGroups(self, global_stack: "GlobalStack") -> Dict[str, QualityGroup]:
+        definition_id = global_stack.definition.getId()
+        machine_node = ContainerTree.getInstance().machines[definition_id]
+
+        # For each extruder, find which quality profiles are available. Later we'll intersect the quality types.
+        qualities_per_type_per_extruder = {}  # type: Dict[str, Dict[str, QualityNode]]
+        for extruder_nr, extruder in global_stack.extruders.items():
+            if not extruder.isEnabled:
+                continue # No qualities available in this extruder. It'll get skipped when intersecting the quality types.
+            nozzle_name = extruder.variant.getName()
+            material_base = extruder.material.getMetaDataEntry("base_file")
+            if nozzle_name not in machine_node.variants or material_base not in machine_node.variants[nozzle_name].materials:
+                # The printer has no variant/material-specific quality profiles. Return the global quality profiles.
+                qualities_per_type_per_extruder[extruder_nr] = machine_node.global_qualities
+            else:
+                # Use the actually specialised quality profiles.
+                qualities_per_type_per_extruder[extruder_nr] = machine_node.variants[nozzle_name].materials[material_base].qualities
+
+        # Create the quality group for each available type.
+        quality_groups = {}
+        for quality_type, global_quality_node in machine_node.global_qualities.items():
+            quality_groups[quality_type].node_for_global = global_quality_node
+            quality_groups[quality_type] = QualityGroup(name = global_quality_node.getMetaDataEntry("name", "Unnamed profile"), quality_type = quality_type)
+            for extruder, qualities_per_type in qualities_per_type_per_extruder:
+                quality_groups[quality_type].nodes_for_extruders[extruder] = qualities_per_type[quality_type]
+
+        available_quality_types = set(quality_groups.keys())
+        for qualities_per_type in qualities_per_type_per_extruder.values():
+            available_quality_types.intersection_update(qualities_per_type.keys())
+        for quality_type in available_quality_types:
+            quality_groups[quality_type].is_available = True
+        return quality_groups
 
     def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> Dict[str, QualityGroup]:
         machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)