diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c9d2163609..e378224a04 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -85,6 +85,7 @@ from cura.Machines.Models.FirstStartMachineActionsModel import FirstStartMachine from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel from cura.Machines.Models.GlobalStacksModel import GlobalStacksModel from cura.Machines.Models.MaterialBrandsModel import MaterialBrandsModel +from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel from cura.Machines.Models.NozzleModel import NozzleModel from cura.Machines.Models.QualityManagementModel import QualityManagementModel @@ -1054,6 +1055,7 @@ class CuraApplication(QtApplication): qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel") qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel") qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel") + qmlRegisterType(MaterialManagementModel, "Cura", 1, 5, "MaterialManagementModel") qmlRegisterType(DiscoveredPrintersModel, "Cura", 1, 0, "DiscoveredPrintersModel") diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index b19c8b7926..24995e417b 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -1,5 +1,6 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. + from collections import defaultdict import copy import uuid @@ -7,19 +8,16 @@ from typing import Dict, Optional, TYPE_CHECKING, Any, List, cast from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot -from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from UM.Decorators import deprecated from UM.Logger import Logger from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.SettingFunction import SettingFunction from UM.Util import parseBool -import cura.CuraApplication #Imported like this to prevent circular imports. +import cura.CuraApplication # Imported like this to prevent circular imports. from cura.Machines.ContainerTree import ContainerTree from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from .MaterialNode import MaterialNode from .MaterialGroup import MaterialGroup -from .VariantType import VariantType if TYPE_CHECKING: from UM.Settings.DefinitionContainer import DefinitionContainer @@ -260,8 +258,8 @@ class MaterialManager(QObject): # 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 # corrupts the configuration) - root_material_id = material_node.container.getMetaDataEntry("base_file") - ids_to_remove = [metadata.get("id", "") for metadata in CuraContainerRegistry.getInstance().findInstanceContainersMetadata(base_file=root_material_id)] + root_material_id = material_node.base_file + ids_to_remove = {metadata.get("id", "") for metadata in CuraContainerRegistry.getInstance().findInstanceContainersMetadata(base_file = root_material_id)} for extruder_stack in CuraContainerRegistry.getInstance().findContainerStacks(type = "extruder_train"): if extruder_stack.material.getId() in ids_to_remove: diff --git a/cura/Machines/Models/MaterialManagementModel.py b/cura/Machines/Models/MaterialManagementModel.py new file mode 100644 index 0000000000..9cc7cb07c9 --- /dev/null +++ b/cura/Machines/Models/MaterialManagementModel.py @@ -0,0 +1,33 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt5.QtCore import QObject, pyqtSlot # To allow the preference page proxy to be used from the actual preferences page. +from typing import TYPE_CHECKING + +from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To find the sets of materials belonging to each other, and currently loaded extruder stacks. + +if TYPE_CHECKING: + from cura.Machines.MaterialNode import MaterialNode + +## Proxy class to the materials page in the preferences. +# +# This class handles the actions in that page, such as creating new materials, +# renaming them, etc. +class MaterialManagementModel(QObject): + ## Can a certain material be deleted, or is it still in use in one of the + # container stacks anywhere? + # + # We forbid the user from deleting a material if it's in use in any stack. + # Deleting it while it's in use can lead to corrupted stacks. In the + # future we might enable this functionality again (deleting the material + # from those stacks) but for now it is easier to prevent the user from + # doing this. + # \return Whether or not the material can be removed. + @pyqtSlot("QVariant", result = bool) + def canMaterialBeRemoved(self, material_node: "MaterialNode"): + 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"): + if extruder_stack.material.getId() in ids_to_remove: + return False + return True \ No newline at end of file diff --git a/resources/qml/Preferences/Materials/MaterialsPage.qml b/resources/qml/Preferences/Materials/MaterialsPage.qml index 481a256501..80d746351c 100644 --- a/resources/qml/Preferences/Materials/MaterialsPage.qml +++ b/resources/qml/Preferences/Materials/MaterialsPage.qml @@ -7,7 +7,7 @@ import QtQuick.Layouts 1.3 import QtQuick.Dialogs 1.2 import UM 1.2 as UM -import Cura 1.0 as Cura +import Cura 1.5 as Cura Item { @@ -42,6 +42,11 @@ Item name: "cura" } + Cura.MaterialManagementModel + { + id: materialManagement + } + function resetExpandedActiveMaterial() { materialListView.expandActiveMaterial(active_root_material_id) @@ -147,7 +152,7 @@ Item id: removeMenuButton text: catalog.i18nc("@action:button", "Remove") iconName: "list-remove" - enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated && base.materialManager.canMaterialBeRemoved(base.currentItem.container_node) + enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated && materialManagement.canMaterialBeRemoved(base.currentItem.container_node) onClicked: {