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

QualityManager.py « cura - github.com/Ultimaker/Cura.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a304eb1db321be03fe6857b4dffe76d91ccff92e (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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.

import UM.Application
import cura.Settings.ExtruderManager
import UM.Settings.ContainerRegistry

# This collects a lot of quality and quality changes related code which was split between ContainerManager
# and the MachineManager and really needs to usable from both.

class QualityManager:

    ##  Get the singleton instance for this class.
    @classmethod
    def getInstance(cls):
        # Note: Explicit use of class name to prevent issues with inheritance.
        if QualityManager.__instance is None:
            QualityManager.__instance = cls()
        return QualityManager.__instance

    __instance = None

    def __init__(self):
        self._empty_quality_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0]

    ##  Find a quality by name for a specific machine definition and materials.
    #
    #   \param quality_name
    #   \param machine_definition (Optional) \type{ContainerInstance} If nothing is
    #                               specified then the currently selected machine definition is used.
    #   \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then
    #                               the current set of selected materials is used.
    #   \return the matching quality containers \type{List[ContainerInstance]}
    def findQualityByName(self, quality_name, machine_definition=None, material_containers=None):
        criteria = {"type": "quality", "name": quality_name}
        return self._getFilteredContainersForStack(machine_definition, material_containers, **criteria)

    ##  Find a quality changes container by name.
    #
    #   \param quality_changes_name \type{str} the name of the quality changes container.
    #   \param machine_definition (Optional) \type{ContainerInstance} If nothing is
    #                               specified then the currently selected machine definition is used.
    #   \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then
    #                               the current set of selected materials is used.
    #   \return the matching quality changes containers \type{List[ContainerInstance]}
    def findQualityChangesByName(self, quality_changes_name, machine_definition=None):
        criteria = {"type": "quality_changes", "name": quality_changes_name}
        result = self._getFilteredContainersForStack(machine_definition, [], **criteria)

        return result

    ##  Fetch the list of available quality types for this combination of machine definition and materials.
    #
    #   \param machine_definition \type{DefinitionContainer}
    #   \param material_containers \type{List[InstanceContainer]}
    #   \return \type{List[str]}
    def findAllQualityTypesForMachineAndMaterials(self, machine_definition, material_containers):
        # Determine the common set of quality types which can be
        # applied to all of the materials for this machine.
        quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_containers[0])
        common_quality_types = set(quality_type_dict.keys())
        for material_container in material_containers[1:]:
            next_quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_container)
            common_quality_types.intersection_update(set(next_quality_type_dict.keys()))

        return list(common_quality_types)

    ##  Fetches a dict of quality types names to quality profiles for a combination of machine and material.
    #
    #   \param machine_definition \type{DefinitionContainer} the machine definition.
    #   \param material \type{ContainerInstance} the material.
    #   \return \type{Dict[str, ContainerInstance]} the dict of suitable quality type names mapping to qualities.
    def __fetchQualityTypeDictForMaterial(self, machine_definition, material):
        qualities = self.findAllQualitiesForMachineMaterial(machine_definition, material)
        quality_type_dict = {}
        for quality in qualities:
            quality_type_dict[quality.getMetaDataEntry("quality_type")] = quality
        return quality_type_dict

    ##  Find a quality container by quality type.
    #
    #   \param quality_type \type{str} the name of the quality type to search for.
    #   \param machine_definition (Optional) \type{ContainerInstance} If nothing is
    #                               specified then the currently selected machine definition is used.
    #   \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then
    #                               the current set of selected materials is used.
    #   \return the matching quality container \type{ContainerInstance}
    def findQualityByQualityType(self, quality_type, machine_definition=None, material_containers=None):
        criteria = {"type": "quality"}
        if quality_type:
            criteria["quality_type"] = quality_type
        result = self._getFilteredContainersForStack(machine_definition, material_containers, **criteria)

        # Fall back to using generic materials and qualities if nothing could be found.
        if not result and material_containers and len(material_containers) == 1:
            basic_materials = self._getBasicMaterials(material_containers[0])
            result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)

        return result[0] if result else self._empty_quality_container

    ##  Find all suitable qualities for a combination of machine and material.
    #
    #   \param machine_definition \type{DefinitionContainer} the machine definition.
    #   \param material_container \type{ContainerInstance} the material.
    #   \return \type{List[ContainerInstance]} the list of suitable qualities.
    def findAllQualitiesForMachineMaterial(self, machine_definition, material_container):
        criteria = {"type": "quality" }
        result = self._getFilteredContainersForStack(machine_definition, [material_container], **criteria)
        if not result:
            basic_materials = self._getBasicMaterials(material_container)
            result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)

        if not result:
            result = [ self._empty_quality_container ]

        return result

    ##  Find all quality changes for a machine.
    #
    #   \param machine_definition \type{DefinitionContainer} the machine definition.
    #   \return \type{List[InstanceContainer]} the list of quality changes
    def findAllQualityChangesForMachine(self, machine_definition):
        if machine_definition.getMetaDataEntry("has_machine_quality"):
            definition_id = machine_definition.getId()
        else:
            definition_id = "fdmprinter"

        filter_dict = { "type": "quality_changes", "extruder": None, "definition": definition_id }
        quality_changes_list = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**filter_dict)
        return quality_changes_list

    ##  Find all usable qualities for a machine and extruders.
    #
    #   Finds all of the qualities for this combination of machine and extruders.
    #   Only one quality per quality type is returned. i.e. if there are 2 qualities with quality_type=normal
    #   then only one of then is returned (at random).
    #
    #   \param global_container_stack \type{ContainerStack} the global machine definition
    #   \param extruder_stacks \type{List[ContainerStack]} the list of extruder stacks
    #   \return \type{List[InstanceContainer]} the list of the matching qualities
    def findAllUsableQualitiesForMachineAndExtruders(self, global_container_stack, extruder_stacks):
        global_machine_definition = global_container_stack.getBottom()

        if extruder_stacks:
            # Multi-extruder machine detected.
            materials = [stack.findContainer(type="material") for stack in extruder_stacks]
        else:
            # Machine with one extruder.
            materials = [global_container_stack.findContainer(type="material")]

        quality_types = self.findAllQualityTypesForMachineAndMaterials(global_machine_definition, materials)

        # Map the list of quality_types to InstanceContainers
        qualities = self.findAllQualitiesForMachineMaterial(global_machine_definition, materials[0])
        quality_type_dict = {}
        for quality in qualities:
            quality_type_dict[quality.getMetaDataEntry("quality_type")] = quality

        return [quality_type_dict[quality_type] for quality_type in quality_types]

    ##  Fetch more basic versions of a material.
    #
    #   This tries to find a generic or basic version of the given material.
    #   \param material_container \type{InstanceContainer} the material
    #   \return \type{List[InstanceContainer]} a list of the basic materials or an empty list if one could not be found.
    def _getBasicMaterials(self, material_container):
        base_material = material_container.getMetaDataEntry("material")
        if material_container.getDefinition().getMetaDataEntry("has_machine_quality"):
            definition_id = material_container.getDefinition().getMetaDataEntry("quality_definition", material_container.getDefinition().getId())
        else:
            definition_id = "fdmprinter"

        if base_material:
            # There is a basic material specified
            criteria = { "type": "material", "name": base_material, "definition": definition_id }
            containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria)
            containers = [basic_material for basic_material in containers if
                               basic_material.getMetaDataEntry("variant") == material_container.getMetaDataEntry(
                                   "variant")]
            return containers

        return []

    def _getFilteredContainers(self, **kwargs):
        return self._getFilteredContainersForStack(None, None, **kwargs)

    def _getFilteredContainersForStack(self, machine_definition=None, material_containers=None, **kwargs):
        # Fill in any default values.
        if machine_definition is None:
            machine_definition = UM.Application.getInstance().getGlobalContainerStack().getBottom()
            quality_definition_id = machine_definition.getMetaDataEntry("quality_definition")
            if quality_definition_id is not None:
                machine_definition = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id=quality_definition_id)[0]

        if material_containers is None:
            active_stacks = cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
            material_containers = [stack.findContainer(type="material") for stack in active_stacks]

        criteria = kwargs
        filter_by_material = False

        machine_definition = self.getParentMachineDefinition(machine_definition)
        whole_machine_definition = self.getWholeMachineDefinition(machine_definition)
        if whole_machine_definition.getMetaDataEntry("has_machine_quality"):
            definition_id = machine_definition.getMetaDataEntry("quality_definition", whole_machine_definition.getId())
            criteria["definition"] = definition_id

            filter_by_material = whole_machine_definition.getMetaDataEntry("has_materials")
        else:
            criteria["definition"] = "fdmprinter"

        # Stick the material IDs in a set
        if material_containers is None or len(material_containers) == 0:
            filter_by_material = False
        else:
            material_ids = set()
            for material_instance in material_containers:
                if material_instance is not None:
                    material_ids.add(material_instance.getId())

        containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria)

        result = []
        for container in containers:
            # If the machine specifies we should filter by material, exclude containers that do not match any active material.
            if filter_by_material and container.getMetaDataEntry("material") not in material_ids:
                continue
            result.append(container)
        return result

    ##  Get the parent machine definition of a machine definition.
    #
    #    \param machine_definition \type{DefinitionContainer} This may be a normal machine definition or
    #               an extruder definition.
    #    \return  \type{DefinitionContainer} the parent machine definition. If the given machine
    #               definition doesn't have a parent then it is simply returned.
    def getParentMachineDefinition(self, machine_definition):
        container_registry = UM.Settings.ContainerRegistry.getInstance()

        machine_entry = machine_definition.getMetaDataEntry("machine")
        if machine_entry is None:
            # We have a normal (whole) machine defintion
            quality_definition = machine_definition.getMetaDataEntry("quality_definition")
            if quality_definition is not None:
                parent_machine_definition = container_registry.findDefinitionContainers(id=quality_definition)[0]
                return self.getParentMachineDefinition(parent_machine_definition)
            else:
                return machine_definition
        else:
            # This looks like an extruder. Find the rest of the machine.
            whole_machine = container_registry.findDefinitionContainers(id=machine_entry)[0]
            parent_machine = self.getParentMachineDefinition(whole_machine)
            if whole_machine is parent_machine:
                # This extruder already belongs to a 'parent' machine def.
                return machine_definition
            else:
                # Look up the corresponding extruder definition in the parent machine definition.
                extruder_position = machine_definition.getMetaDataEntry("position")
                parent_extruder_id = parent_machine.getMetaDataEntry("machine_extruder_trains")[extruder_position]
                return container_registry.findDefinitionContainers(id=parent_extruder_id)[0]

    ##  Get the whole/global machine definition from an extruder definition.
    #
    #    \param machine_definition \type{DefinitionContainer} This may be a normal machine definition or
    #               an extruder definition.
    #    \return \type{DefinitionContainer}
    def getWholeMachineDefinition(self, machine_definition):
        machine_entry = machine_definition.getMetaDataEntry("machine")
        if machine_entry is None:
            # This already is a 'global' machine definition.
            return machine_definition
        else:
            container_registry = UM.Settings.ContainerRegistry.getInstance()
            whole_machine = container_registry.findDefinitionContainers(id=machine_entry)[0]
            return whole_machine