Welcome to mirror list, hosted at ThFree Co, Russian Federation.

IntentManager.py « Settings « cura - github.com/Ultimaker/Cura.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 7d3e2659bfcdc098ee26b7ba85f644d4a0a7480f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.

from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
from typing import Any, Dict, List, Set, Tuple, TYPE_CHECKING

from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer

import cura.CuraApplication
from UM.Signal import Signal
from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_intent_container

if TYPE_CHECKING:
    from UM.Settings.InstanceContainer import InstanceContainer


class IntentManager(QObject):
    """Front-end for querying which intents are available for a certain configuration.
    """
    __instance = None

    @classmethod
    def getInstance(cls):
        """This class is a singleton."""

        if not cls.__instance:
            cls.__instance = IntentManager()
        return cls.__instance

    intentCategoryChanged = pyqtSignal() #Triggered when we switch categories.
    intentCategoryChangedSignal = Signal()

    def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]:
        """Gets the metadata dictionaries of all intent profiles for a given

        configuration.

        :param definition_id: ID of the printer.
        :param nozzle_name: Name of the nozzle.
        :param material_base_file: The base_file of the material.
        :return: A list of metadata dictionaries matching the search criteria, or
            an empty list if nothing was found.
        """
        intent_metadatas = []  # type: List[Dict[str, Any]]
        try:
            materials = ContainerTree.getInstance().machines[definition_id].variants[nozzle_name].materials
        except KeyError:
            Logger.log("w", "Unable to find the machine %s or the variant %s", definition_id, nozzle_name)
            materials = {}
        if material_base_file not in materials:
            return intent_metadatas

        material_node = materials[material_base_file]
        for quality_node in material_node.qualities.values():
            for intent_node in quality_node.intents.values():
                intent_metadatas.append(intent_node.getMetadata())
        return intent_metadatas

    def intentCategories(self, definition_id: str, nozzle_id: str, material_id: str) -> List[str]:
        """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.
        """
        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)

    def getCurrentAvailableIntents(self) -> List[Tuple[str, str]]:
        """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.
        """

        application = cura.CuraApplication.CuraApplication.getInstance()
        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 'currentAvailableIntentCategories', which _does_ return default.
        quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
        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]
        current_definition_id = global_stack.definition.getId()
        for extruder_stack in global_stack.extruderList:
            if not extruder_stack.isEnabled:
                continue
            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.get("quality_type") in available_quality_types}

        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"]))
        return list(result)

    def currentAvailableIntentCategories(self) -> List[str]:
        """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).
        :return: List of all categories in the current configurations of all
            extruders.
        """

        global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
        if global_stack is None:
            return ["default"]
        current_definition_id = global_stack.definition.getId()
        final_intent_categories = set()  # type: Set[str]
        for extruder_stack in global_stack.extruderList:
            if not extruder_stack.isEnabled:
                continue
            nozzle_name = extruder_stack.variant.getMetaDataEntry("name")
            material_id = extruder_stack.material.getMetaDataEntry("base_file")
            final_intent_categories.update(self.intentCategories(current_definition_id, nozzle_name, material_id))
        return list(final_intent_categories)

    def getDefaultIntent(self) -> "InstanceContainer":
        """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.
        """
        return empty_intent_container

    @pyqtProperty(str, notify = intentCategoryChanged)
    def currentIntentCategory(self) -> str:
        application = cura.CuraApplication.CuraApplication.getInstance()
        active_extruder_stack = application.getMachineManager().activeStack
        if active_extruder_stack is None:
            return ""
        return active_extruder_stack.intent.getMetaDataEntry("intent_category", "")

    @pyqtSlot(str, str)
    def selectIntent(self, intent_category: str, quality_type: str) -> None:
        """Apply intent on the stacks."""

        Logger.log("i", "Attempting to set intent_category to [%s] and quality type to [%s]", intent_category, quality_type)
        old_intent_category = self.currentIntentCategory
        application = cura.CuraApplication.CuraApplication.getInstance()
        global_stack = application.getGlobalContainerStack()
        if global_stack is None:
            return
        current_definition_id = global_stack.definition.getId()
        machine_node = ContainerTree.getInstance().machines[current_definition_id]
        for extruder_stack in global_stack.extruderList:
            nozzle_name = extruder_stack.variant.getMetaDataEntry("name")
            material_id = extruder_stack.material.getMetaDataEntry("base_file")

            material_node = machine_node.variants[nozzle_name].materials[material_id]

            # Since we want to switch to a certain quality type, check the tree if we have one.
            quality_node = None
            for q_node in material_node.qualities.values():
                if q_node.quality_type == quality_type:
                    quality_node = q_node

            if quality_node is None:
                Logger.log("w", "Unable to find quality_type [%s] for extruder [%s]", quality_type, extruder_stack.getId())
                continue

            # Check that quality node if we can find a matching intent.
            intent_id = None
            for id, intent_node in quality_node.intents.items():
                if intent_node.intent_category == intent_category:
                    intent_id = id
            intent = application.getContainerRegistry().findContainers(id = intent_id)
            if intent:
                extruder_stack.intent = intent[0]
            else:
                extruder_stack.intent = self.getDefaultIntent()
        application.getMachineManager().setQualityGroupByQualityType(quality_type)
        if old_intent_category != intent_category:
            self.intentCategoryChanged.emit()
            self.intentCategoryChangedSignal.emit()