Browse Source

Merge branch 'feature_intent' of github.com:Ultimaker/Cura into feature_intent

Jaime van Kessel 5 years ago
parent
commit
08ffafa475

+ 1 - 1
cura/Machines/MaterialManager.py

@@ -255,7 +255,7 @@ class MaterialManager(QObject):
         for result in results:
             container_registry.removeContainer(result.getMetaDataEntry("id", ""))
 
-    @pyqtSlot("QVariant", result=bool)
+    @pyqtSlot("QVariant", result = bool)
     def canMaterialBeRemoved(self, material_node: "MaterialNode"):
         # Check if the material is active in any extruder train. In that case, the material shouldn't be removed!
         # In the future we might enable this again, but right now, it's causing a ton of issues if we do (since it

+ 6 - 0
cura/Machines/Models/BaseMaterialsModel.py

@@ -45,6 +45,7 @@ class BaseMaterialsModel(ListModel):
         # Update this model when switching machines, when adding materials or changing their metadata.
         self._machine_manager.activeStackChanged.connect(self._update)
         ContainerTree.getInstance().materialsChanged.connect(self._materialsListChanged)
+        self._application.getMaterialManagementModel().favoritesChanged.connect(self._update)
 
         self.addRoleName(Qt.UserRole + 1, "root_material_id")
         self.addRoleName(Qt.UserRole + 2, "id")
@@ -115,6 +116,11 @@ class BaseMaterialsModel(ListModel):
             return
         self._update()
 
+    ##  Triggered when the list of favorite materials is changed.
+    def _favoritesChanged(self, material_base_file: str) -> None:
+        if material_base_file in self._available_materials:
+            self._update()
+
     ## This is an abstract method that needs to be implemented by the specific
     #  models themselves.
     def _update(self):

+ 39 - 5
cura/Machines/Models/MaterialManagementModel.py

@@ -2,7 +2,7 @@
 # Cura is released under the terms of the LGPLv3 or higher.
 
 import copy  # To duplicate materials.
-from PyQt5.QtCore import QObject, pyqtSlot  # To allow the preference page proxy to be used from the actual preferences page.
+from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot  # To allow the preference page proxy to be used from the actual preferences page.
 from typing import Any, Dict, Optional, TYPE_CHECKING
 import uuid  # To generate new GUIDs for new materials.
 
@@ -23,6 +23,11 @@ catalog = i18nCatalog("cura")
 #   This class handles the actions in that page, such as creating new materials,
 #   renaming them, etc.
 class MaterialManagementModel(QObject):
+    ##  Triggered when a favorite is added or removed.
+    #   \param The base file of the material is provided as parameter when this
+    #   emits.
+    favoritesChanged = pyqtSignal(str)
+
     ##  Can a certain material be deleted, or is it still in use in one of the
     #   container stacks anywhere?
     #
@@ -34,7 +39,7 @@ class MaterialManagementModel(QObject):
     #   \param material_node The ContainerTree node of the material to check.
     #   \return Whether or not the material can be removed.
     @pyqtSlot("QVariant", result = bool)
-    def canMaterialBeRemoved(self, material_node: "MaterialNode"):
+    def canMaterialBeRemoved(self, material_node: "MaterialNode") -> bool:
         container_registry = CuraContainerRegistry.getInstance()
         ids_to_remove = {metadata.get("id", "") for metadata in container_registry.findInstanceContainersMetadata(base_file = material_node.base_file)}
         for extruder_stack in container_registry.findContainerStacks(type = "extruder_train"):
@@ -80,7 +85,8 @@ class MaterialManagementModel(QObject):
     #   \param new_metadata Metadata for the new material. If not provided, this
     #   will be duplicated from the original material.
     #   \return The root material ID of the duplicate material.
-    def duplicateMaterialByBaseFile(self, base_file: str, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
+    def duplicateMaterialByBaseFile(self, base_file: str, new_base_id: Optional[str] = None,
+                                    new_metadata: Optional[Dict[str, Any]] = None) -> Optional[str]:
         container_registry = CuraContainerRegistry.getInstance()
 
         root_materials = container_registry.findContainers(id = base_file)
@@ -144,7 +150,8 @@ class MaterialManagementModel(QObject):
     #   will be duplicated from the original material.
     #   \return The root material ID of the duplicate material.
     @pyqtSlot("QVariant", result = str)
-    def duplicateMaterial(self, material_node: "MaterialNode", new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
+    def duplicateMaterial(self, material_node: "MaterialNode", new_base_id: Optional[str] = None,
+                          new_metadata: Optional[Dict[str, Any]] = None) -> Optional[str]:
         return self.duplicateMaterialByBaseFile(material_node.base_file, new_base_id, new_metadata)
 
     ##  Create a new material by cloning the preferred material for the current
@@ -178,4 +185,31 @@ class MaterialManagementModel(QObject):
                         }
 
         self.duplicateMaterial(preferred_material_node, new_base_id = new_id, new_metadata = new_metadata)
-        return new_id
+        return new_id
+
+    ##  Adds a certain material to the favorite materials.
+    #   \param material_base_file The base file of the material to add.
+    @pyqtSlot(str)
+    def addFavorite(self, material_base_file: str) -> None:
+        application = cura.CuraApplication.CuraApplication.getInstance()
+        favorites = application.getPreferences().getValue("cura/favorite_materials").split(";")
+        if material_base_file not in favorites:
+            favorites.append(material_base_file)
+            application.getPreferences().setValue("cura/favorite_materials", ";".join(favorites))
+            application.saveSettings()
+            self.favoritesChanged.emit(material_base_file)
+
+    ##  Removes a certain material from the favorite materials.
+    #
+    #   If the material was not in the favorite materials, nothing happens.
+    @pyqtSlot(str)
+    def removeFavorite(self, material_base_file: str) -> None:
+        application = cura.CuraApplication.CuraApplication.getInstance()
+        favorites = application.getPreferences().getValue("cura/favorite_materials").split(";")
+        try:
+            favorites.remove(material_base_file)
+            application.getPreferences().setValue("cura/favorite_materials", ";".join(favorites))
+            application.saveSettings()
+            self.favoritesChanged.emit(material_base_file)
+        except ValueError:  # Material was not in the favorites list.
+            Logger.log("w", "Material {material_base_file} was already not a favorite material.".format(material_base_file = material_base_file))

+ 0 - 1
cura/Machines/QualityManager.py

@@ -48,7 +48,6 @@ class QualityManager(QObject):
     def __init__(self, parent = None) -> None:
         super().__init__(parent)
         application = cura.CuraApplication.CuraApplication.getInstance()
-        self._material_manager = application.getMaterialManager()
         self._container_registry = application.getContainerRegistry()
 
         self._empty_quality_container = application.empty_quality_container

+ 0 - 1
cura/Settings/ContainerManager.py

@@ -23,7 +23,6 @@ from UM.Settings.InstanceContainer import InstanceContainer
 
 import cura.CuraApplication
 from cura.Machines.ContainerTree import ContainerTree
-from cura.Machines.MaterialManager import MaterialManager
 
 if TYPE_CHECKING:
     from cura.CuraApplication import CuraApplication

+ 1 - 20
cura/Settings/CuraContainerStack.py

@@ -1,7 +1,7 @@
 # Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
-from typing import Any, cast, Dict, List, Optional
+from typing import Any, cast, List, Optional
 from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject
 
 from UM.Application import Application
@@ -364,22 +364,3 @@ class _ContainerIndexes:
 
     # Reverse lookup: type -> index
     TypeIndexMap = dict([(v, k) for k, v in IndexTypeMap.items()])
-
-    # Mapping to old values before Intent introduction. Used for reading older versions of input files.
-    IndexToOldIndexMap = {
-        UserChanges: 0,
-        QualityChanges: 1,
-        Intent: -1,  # Wasn't there in the old 'format'!
-        Quality: 2,
-        Material: 3,
-        Variant: 4,
-        DefinitionChanges: 5,
-        Definition: 6,
-    }
-
-    # Reverse lookup: old index -> new index
-    OldIndexToIndexMap = dict([(v, k) for k, v in IndexToOldIndexMap.items()])
-
-    @classmethod
-    def getIndexMapping(cls, setting_version: int) -> Dict[int, int]:
-        return dict([(x, x) for x in list(range(99))]) if setting_version >= 10 else cls.IndexToOldIndexMap

+ 0 - 1
cura/Settings/CuraStackBuilder.py

@@ -10,7 +10,6 @@ from UM.Settings.InstanceContainer import InstanceContainer
 
 from cura.Machines.ContainerTree import ContainerTree
 from cura.Machines.MachineNode import MachineNode
-from cura.Machines.MaterialManager import MaterialManager
 from .GlobalStack import GlobalStack
 from .ExtruderStack import ExtruderStack
 

+ 7 - 5
cura/UI/MachineSettingsManager.py

@@ -2,11 +2,12 @@
 # Cura is released under the terms of the LGPLv3 or higher.
 
 from typing import Optional, TYPE_CHECKING
-
 from PyQt5.QtCore import QObject, pyqtSlot
 
 from UM.i18n import i18nCatalog
 
+from cura.Machines.ContainerTree import ContainerTree
+
 if TYPE_CHECKING:
     from cura.CuraApplication import CuraApplication
 
@@ -42,7 +43,7 @@ class MachineSettingsManager(QObject):
         # it was moved to the machine manager instead. Now this method just calls the machine manager.
         self._application.getMachineManager().setActiveMachineExtruderCount(extruder_count)
 
-    # Function for the Machine Settings panel (QML) to update after the usre changes "Number of Extruders".
+    # Function for the Machine Settings panel (QML) to update after the user changes "Number of Extruders".
     #
     # fieldOfView: The Ultimaker 2 family (not 2+) does not have materials in Cura by default, because the material is
     # to be set on the printer. But when switching to Marlin flavor, the printer firmware can not change/insert material
@@ -51,8 +52,6 @@ class MachineSettingsManager(QObject):
     @pyqtSlot()
     def updateHasMaterialsMetadata(self):
         machine_manager = self._application.getMachineManager()
-        material_manager = self._application.getMaterialManager()
-
         global_stack = machine_manager.activeMachine
 
         definition = global_stack.definition
@@ -76,7 +75,10 @@ class MachineSettingsManager(QObject):
         # set materials
         for position in extruder_positions:
             if has_materials:
-                material_node = material_manager.getDefaultMaterial(global_stack, position, None)
+                extruder = global_stack.extruderList[int(position)]
+                approximate_diameter = extruder.getApproximateMaterialDiameter()
+                variant_node = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[extruder.variant.getName()]
+                material_node = variant_node.preferredMaterial(approximate_diameter)
             machine_manager.setMaterial(position, material_node)
 
         self.forceUpdate()

+ 24 - 56
plugins/3MFReader/ThreeMFWorkspaceReader.py

@@ -371,8 +371,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
         # Get quality type
         parser = ConfigParser(interpolation = None)
         parser.read_string(serialized)
-        index_map_version = _ContainerIndexes.getIndexMapping(int(parser["metadata"]["setting_version"]))
-        quality_container_id = parser["containers"][str(index_map_version[_ContainerIndexes.Quality])]
+        quality_container_id = parser["containers"][str(_ContainerIndexes.Quality)]
         quality_type = "empty_quality"
         if quality_container_id not in ("empty", "empty_quality"):
             quality_type = instance_container_info_dict[quality_container_id].parser["metadata"]["quality_type"]
@@ -382,11 +381,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
         serialized = GlobalStack._updateSerialized(serialized, global_stack_file)
         parser = ConfigParser(interpolation = None)
         parser.read_string(serialized)
-        index_map_version = _ContainerIndexes.getIndexMapping(int(parser["metadata"]["setting_version"]))
-        definition_changes_id = parser["containers"][str(index_map_version[_ContainerIndexes.DefinitionChanges])]
+        definition_changes_id = parser["containers"][str(_ContainerIndexes.DefinitionChanges)]
         if definition_changes_id not in ("empty", "empty_definition_changes"):
             self._machine_info.definition_changes_info = instance_container_info_dict[definition_changes_id]
-        user_changes_id = parser["containers"][str(index_map_version[_ContainerIndexes.UserChanges])]
+        user_changes_id = parser["containers"][str(_ContainerIndexes.UserChanges)]
         if user_changes_id not in ("empty", "empty_user_changes"):
             self._machine_info.user_changes_info = instance_container_info_dict[user_changes_id]
 
@@ -396,8 +394,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
 
             extruder_info = ExtruderInfo()
             extruder_info.position = position
-            variant_id = parser["containers"][str(index_map_version[_ContainerIndexes.Variant])]
-            material_id = parser["containers"][str(index_map_version[_ContainerIndexes.Material])]
+            variant_id = parser["containers"][str(_ContainerIndexes.Variant)]
+            material_id = parser["containers"][str(_ContainerIndexes.Material)]
             if variant_id not in ("empty", "empty_variant"):
                 extruder_info.variant_info = instance_container_info_dict[variant_id]
             if material_id not in ("empty", "empty_material"):
@@ -405,7 +403,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                 extruder_info.root_material_id = root_material_id
             self._machine_info.extruder_info_dict[position] = extruder_info
         else:
-            variant_id = parser["containers"][str(index_map_version[_ContainerIndexes.Variant])]
+            variant_id = parser["containers"][str(_ContainerIndexes.Variant)]
             if variant_id not in ("empty", "empty_variant"):
                 self._machine_info.variant_info = instance_container_info_dict[variant_id]
         QCoreApplication.processEvents()  # Ensure that the GUI does not freeze.
@@ -417,14 +415,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
             serialized = ExtruderStack._updateSerialized(serialized, extruder_stack_file)
             parser = ConfigParser(interpolation = None)
             parser.read_string(serialized)
-            index_map_version = _ContainerIndexes.getIndexMapping(int(parser["metadata"]["setting_version"]))
 
             # The check should be done for the extruder stack that's associated with the existing global stack,
             # and those extruder stacks may have different IDs.
             # So we check according to the positions
             position = parser["metadata"]["position"]
-            variant_id = parser["containers"][str(index_map_version[_ContainerIndexes.Variant])]
-            material_id = parser["containers"][str(index_map_version[_ContainerIndexes.Material])]
+            variant_id = parser["containers"][str(_ContainerIndexes.Variant)]
+            material_id = parser["containers"][str(_ContainerIndexes.Material)]
 
             extruder_info = ExtruderInfo()
             extruder_info.position = position
@@ -438,11 +435,11 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                 root_material_id = reverse_material_id_dict[material_id]
                 extruder_info.root_material_id = root_material_id
 
-            definition_changes_id = parser["containers"][str(index_map_version[_ContainerIndexes.DefinitionChanges])]
+            definition_changes_id = parser["containers"][str(_ContainerIndexes.DefinitionChanges)]
             if definition_changes_id not in ("empty", "empty_definition_changes"):
                 extruder_info.definition_changes_info = instance_container_info_dict[definition_changes_id]
 
-            user_changes_id = parser["containers"][str(index_map_version[_ContainerIndexes.UserChanges])]
+            user_changes_id = parser["containers"][str(_ContainerIndexes.UserChanges)]
             if user_changes_id not in ("empty", "empty_user_changes"):
                 extruder_info.user_changes_info = instance_container_info_dict[user_changes_id]
             self._machine_info.extruder_info_dict[position] = extruder_info
@@ -575,7 +572,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
     @call_on_qt_thread
     def read(self, file_name):
         application = CuraApplication.getInstance()
-        material_manager = application.getMaterialManager()
 
         archive = zipfile.ZipFile(file_name, "r")
 
@@ -673,7 +669,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                         if self._resolve_strategies["material"] == "override":
                             # Remove the old materials and then deserialize the one from the project
                             root_material_id = material_container.getMetaDataEntry("base_file")
-                            material_manager.removeMaterialByRootId(root_material_id)
+                            application.getContainerRegistry().removeContainer(root_material_id)
                         elif self._resolve_strategies["material"] == "new":
                             # Note that we *must* deserialize it with a new ID, as multiple containers will be
                             # auto created & added.
@@ -727,8 +723,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
         if self._machine_info.quality_changes_info is None:
             return
 
-        application = CuraApplication.getInstance()
-
         # If we have custom profiles, load them
         quality_changes_name = self._machine_info.quality_changes_info.name
         if self._machine_info.quality_changes_info is not None:
@@ -923,43 +917,27 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
                         extruder_stack.userChanges.setProperty(key, "value", value)
 
     def _applyVariants(self, global_stack, extruder_stack_dict):
-        application = CuraApplication.getInstance()
-        variant_manager = application.getVariantManager()
+        machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
 
+        # Take the global variant from the machine info if available.
         if self._machine_info.variant_info is not None:
-            parser = self._machine_info.variant_info.parser
-            variant_name = parser["general"]["name"]
-
-            variant_type = VariantType.BUILD_PLATE
-
-            node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
-            if node is not None and node.container is not None:
-                global_stack.variant = node.container
+            variant_name = self._machine_info.variant_info.parser["general"]["name"]
+            global_stack.variant = machine_node.variants[variant_name].container
 
         for position, extruder_stack in extruder_stack_dict.items():
             if position not in self._machine_info.extruder_info_dict:
                 continue
             extruder_info = self._machine_info.extruder_info_dict[position]
             if extruder_info.variant_info is None:
-                # If there is no variant_info, try to use the default variant. Otherwise, leave it be.
-                machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
-                node = machine_node.variants[machine_node.preferred_variant_name]
-                if node is not None and node.container is not None:
-                    extruder_stack.variant = node.container
-                continue
-            parser = extruder_info.variant_info.parser
-
-            variant_name = parser["general"]["name"]
-            variant_type = VariantType.NOZZLE
-
-            node = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[variant_name]
-            if node is not None and node.container is not None:
-                extruder_stack.variant = node.container
+                # If there is no variant_info, try to use the default variant. Otherwise, any available variant.
+                node = machine_node.variants.get(machine_node.preferred_variant_name, next(iter(machine_node.variants.values())))
+            else:
+                variant_name = extruder_info.variant_info.parser["general"]["name"]
+                node = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[variant_name]
+            extruder_stack.variant = node.container
 
     def _applyMaterials(self, global_stack, extruder_stack_dict):
-        application = CuraApplication.getInstance()
-        material_manager = application.getMaterialManager()
-
+        machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
         for position, extruder_stack in extruder_stack_dict.items():
             if position not in self._machine_info.extruder_info_dict:
                 continue
@@ -970,18 +948,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
             root_material_id = extruder_info.root_material_id
             root_material_id = self._old_new_materials.get(root_material_id, root_material_id)
 
-            build_plate_id = global_stack.variant.getId()
-
-            # get material diameter of this extruder
-            machine_material_diameter = extruder_stack.getCompatibleMaterialDiameter()
-            material_node = material_manager.getMaterialNode(global_stack.definition.getId(),
-                                                             extruder_stack.variant.getName(),
-                                                             build_plate_id,
-                                                             machine_material_diameter,
-                                                             root_material_id)
-
-            if material_node is not None and material_node.container is not None:
-                extruder_stack.material = material_node.container  # type: InstanceContainer
+            material_node = machine_node.variants[extruder_stack.variant.getName()].materials[root_material_id]
+            extruder_stack.material = material_node.container  # type: InstanceContainer
 
     def _applyChangesToMachine(self, global_stack, extruder_stack_dict):
         # Clear all first

+ 4 - 2
plugins/Toolbox/src/Toolbox.py

@@ -360,12 +360,14 @@ class Toolbox(QObject, Extension):
     @pyqtSlot()
     def resetMaterialsQualitiesAndUninstall(self) -> None:
         application = CuraApplication.getInstance()
-        material_manager = application.getMaterialManager()
         machine_manager = application.getMachineManager()
         container_tree = ContainerTree.getInstance()
 
         for global_stack, extruder_nr, container_id in self._package_used_materials:
-            default_material_node = material_manager.getDefaultMaterial(global_stack, extruder_nr, global_stack.extruders[extruder_nr].variant.getName())
+            extruder = global_stack.extruderList[int(extruder_nr)]
+            approximate_diameter = extruder.getApproximateMaterialDiameter()
+            variant_node = container_tree.machines[global_stack.definition.getId()].variants[extruder.variant.getName()]
+            default_material_node = variant_node.preferredMaterial(approximate_diameter)
             machine_manager.setMaterial(extruder_nr, default_material_node, global_stack = global_stack)
         for global_stack, extruder_nr, container_id in self._package_used_qualities:
             variant_names = [extruder.variant.getName() for extruder in global_stack.extruderList]

Some files were not shown because too many files changed in this diff