From c8d65e86f18b89264050a4b51b7246842bbc22be Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Thu, 10 Oct 2019 15:36:14 +0200 Subject: [PATCH 01/12] Truncate/elide/ellipsize the material name in Preferences if it doesn't fit it's parent CURA-6862 --- resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml index b54af103fe..e821dfb955 100644 --- a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml +++ b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml @@ -64,8 +64,10 @@ Item height: childrenRect.height Label { + width: parent.width text: materialProperties.name font: UM.Theme.getFont("large_bold") + elide: Text.ElideRight } } From c147174668ffeb6b5af7b05ffd494361db923fc1 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 14 Oct 2019 11:04:28 +0200 Subject: [PATCH 02/12] Limit profile name to 70% of available width for CustomPrintSetup This leaves 30% for the variant detail info CURA-6862 --- .../Custom/CustomPrintSetup.qml | 122 +++++++++++------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index 2698089d0c..b72341a4d6 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -4,6 +4,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Controls 1.4 as OldControls +import QtQuick.Layouts 1.3 import UM 1.3 as UM import Cura 1.6 as Cura @@ -66,7 +67,6 @@ Item { id: intentSelection onClicked: menu.opened ? menu.close() : menu.open() - text: generateActiveQualityText() anchors.right: parent.right width: UM.Theme.getSize("print_setup_big_item").width @@ -75,18 +75,85 @@ Item baselineOffset: null // If we don't do this, there is a binding loop. WHich is a bit weird, since we override the contentItem anyway... - contentItem: Label + + contentItem: RowLayout { - id: textLabel - text: intentSelection.text - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") + spacing: 0 anchors.left: parent.left + anchors.right: customisedSettings.left anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.verticalCenter: intentSelection.verticalCenter - height: contentHeight - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering + + + + Label + { + id: textLabel + text: qualityName() + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + anchors.verticalCenter: intentSelection.verticalCenter + Layout.margins: 0 + Layout.maximumWidth: parent.width * 0.7 + height: contentHeight + verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering + elide: Text.ElideRight + + function qualityName() { + var resultMap = Cura.MachineManager.activeQualityDisplayNameMap + return resultMap["main"] + } + } + + Label + { + text: activeQualityDetailText() + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_detail") + anchors.verticalCenter: intentSelection.verticalCenter + Layout.margins: 0 + Layout.fillWidth: true + + height: contentHeight + verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering + elide: Text.ElideRight + + function activeQualityDetailText() + { + var resultMap = Cura.MachineManager.activeQualityDisplayNameMap + var resultSuffix = resultMap["suffix"] + var result = "" + + if (Cura.MachineManager.isActiveQualityExperimental) + { + resultSuffix += " (Experimental)" + } + + if (Cura.MachineManager.isActiveQualitySupported) + { + if (Cura.MachineManager.activeQualityLayerHeight > 0) + { + if (resultSuffix) + { + result += " - " + } + if (resultSuffix) + { + result += resultSuffix + } + result += " - " + result += Cura.MachineManager.activeQualityLayerHeight + "mm" + } + } + + return result + } + } + + + + } background: Rectangle @@ -98,41 +165,6 @@ Item color: UM.Theme.getColor("main_background") } - function generateActiveQualityText() - { - var resultMap = Cura.MachineManager.activeQualityDisplayNameMap - var resultMain = resultMap["main"] - var resultSuffix = resultMap["suffix"] - var result = "" - - if (Cura.MachineManager.isActiveQualityExperimental) - { - resultSuffix += " (Experimental)" - } - - if (Cura.MachineManager.isActiveQualitySupported) - { - if (Cura.MachineManager.activeQualityLayerHeight > 0) - { - result = resultMain - if (resultSuffix) - { - result += " - " - } - result += "" - if (resultSuffix) - { - result += resultSuffix - } - result += " - " - result += Cura.MachineManager.activeQualityLayerHeight + "mm" - result += "" - } - } - - return result - } - UM.SimpleButton { id: customisedSettings From 279d5671d8a7ff53477a6b9fb002d3f8d1b183a3 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 14 Oct 2019 12:14:31 +0200 Subject: [PATCH 03/12] Cleanup CustomPrintSetup.qml CURA-6862 --- .../PrintSetupSelector/Custom/CustomPrintSetup.qml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index b72341a4d6..cf27d6d023 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -91,7 +91,6 @@ Item text: qualityName() font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") - anchors.verticalCenter: intentSelection.verticalCenter Layout.margins: 0 Layout.maximumWidth: parent.width * 0.7 height: contentHeight @@ -110,7 +109,6 @@ Item text: activeQualityDetailText() font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_detail") - anchors.verticalCenter: intentSelection.verticalCenter Layout.margins: 0 Layout.fillWidth: true @@ -136,11 +134,7 @@ Item { if (resultSuffix) { - result += " - " - } - if (resultSuffix) - { - result += resultSuffix + result += " - " + resultSuffix } result += " - " result += Cura.MachineManager.activeQualityLayerHeight + "mm" @@ -150,10 +144,6 @@ Item return result } } - - - - } background: Rectangle From 8f668091598f684f5d37f6ab324760266c341af8 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 14 Oct 2019 14:33:50 +0200 Subject: [PATCH 04/12] Always show custom qualities CURA-6882 --- cura/Machines/MachineNode.py | 11 +++-------- .../PrintSetupSelector/Custom/CustomPrintSetup.qml | 5 +++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index e71801fbb1..29968512ce 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -134,6 +134,9 @@ class MachineNode(ContainerNode): groups_by_name[name] = QualityChangesGroup(name, quality_type = quality_changes["quality_type"], intent_category = quality_changes.get("intent_category", "default"), parent = CuraApplication.getInstance()) + # CURA-6882 + # Custom qualities are always available, even if they are based on the "not supported" profile. + groups_by_name[name].is_available = True elif groups_by_name[name].intent_category == "default": # Intent category should be stored as "default" if everything is default or as the intent if any of the extruder have an actual intent. groups_by_name[name].intent_category = quality_changes.get("intent_category", "default") @@ -142,14 +145,6 @@ class MachineNode(ContainerNode): else: # Global profile. groups_by_name[name].metadata_for_global = quality_changes - quality_groups = self.getQualityGroups(variant_names, material_bases, extruder_enabled) - for quality_changes_group in groups_by_name.values(): - if quality_changes_group.quality_type not in quality_groups: - quality_changes_group.is_available = False - else: - # Quality changes group is available iff the quality group it depends on is available. Irrespective of whether the intent category is available. - quality_changes_group.is_available = quality_groups[quality_changes_group.quality_type].is_available - return list(groups_by_name.values()) ## Gets the preferred global quality node, going by the preferred quality diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index 2698089d0c..99fdafdb73 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -110,6 +110,11 @@ Item resultSuffix += " (Experimental)" } + if (Cura.MachineManager.isActiveQualityCustom) + { + result = resultMain + } + if (Cura.MachineManager.isActiveQualitySupported) { if (Cura.MachineManager.activeQualityLayerHeight > 0) From 2e0fd21c60aa587c4de906485961239a37618b6a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 14 Oct 2019 14:47:43 +0200 Subject: [PATCH 05/12] Fix setVariantByName() --- cura/Settings/MachineManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 1fadcf01c5..dec433e8f8 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1514,7 +1514,8 @@ class MachineManager(QObject): if self._global_container_stack is None: return machine_definition_id = self._global_container_stack.definition.id - variant_node = self._variant_manager.getVariantNode(machine_definition_id, variant_name) + machine_node = ContainerTree.getInstance().machines.get(machine_definition_id) + variant_node = machine_node.variants.get(variant_name) self.setVariant(position, variant_node) @pyqtSlot(str, "QVariant") From 54f7892f2d85ba0ff76eb7f82b002527cdb8ac0a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 14 Oct 2019 15:25:28 +0200 Subject: [PATCH 06/12] Sort built-in qualities by layer height for all CURA-6883 --- cura/Machines/Models/QualityManagementModel.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cura/Machines/Models/QualityManagementModel.py b/cura/Machines/Models/QualityManagementModel.py index adaa4309b7..1d30b1753e 100644 --- a/cura/Machines/Models/QualityManagementModel.py +++ b/cura/Machines/Models/QualityManagementModel.py @@ -13,6 +13,7 @@ from cura.Settings.ContainerManager import ContainerManager from cura.Machines.ContainerTree import ContainerTree from cura.Settings.cura_empty_instance_containers import empty_quality_changes_container from cura.Settings.IntentManager import IntentManager +from cura.Machines.Models.MachineModelUtils import fetchLayerHeight from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -295,6 +296,8 @@ class QualityManagementModel(ListModel): if not quality_group.is_available: continue + layer_height = fetchLayerHeight(quality_group) + item = {"name": quality_group.name, "is_read_only": True, "quality_group": quality_group, @@ -302,10 +305,11 @@ class QualityManagementModel(ListModel): "quality_changes_group": None, "intent_category": "default", "section_name": catalog.i18nc("@label", "Default"), + "layer_height": layer_height, # layer_height is only used for sorting } item_list.append(item) - # Sort by quality names - item_list = sorted(item_list, key = lambda x: x["name"].upper()) + # Sort by layer_height for built-in qualities + item_list = sorted(item_list, key = lambda x: x["layer_height"]) # Create intent items (non-default) available_intent_list = IntentManager.getInstance().getCurrentAvailableIntents() From 096b6431c5c253bc33cc1492b3fd88b02d79e5e0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 14 Oct 2019 14:36:07 +0200 Subject: [PATCH 07/12] Fix typos in g-code comments --- resources/definitions/creality_base.def.json | 2 +- resources/definitions/creality_ender5.def.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/creality_base.def.json b/resources/definitions/creality_base.def.json index d7e028f31a..7e91fb4989 100644 --- a/resources/definitions/creality_base.def.json +++ b/resources/definitions/creality_base.def.json @@ -125,7 +125,7 @@ "overrides": { "machine_name": { "default_value": "Creawsome Base Printer" }, "machine_start_gcode": { "default_value": "M201 X500.00 Y500.00 Z100.00 E5000.00 ;Setup machine max acceleration\nM203 X500.00 Y500.00 Z10.00 E50.00 ;Setup machine max feedrate\nM204 P500.00 R1000.00 T500.00 ;Setup Print/Retract/Travel acceleration\nM205 X8.00 Y8.00 Z0.40 E5.00 ;Setup Jerk\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" }, - "machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, + "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, "machine_max_feedrate_x": { "value": 500 }, "machine_max_feedrate_y": { "value": 500 }, diff --git a/resources/definitions/creality_ender5.def.json b/resources/definitions/creality_ender5.def.json index d95f4a1467..c1511884ae 100644 --- a/resources/definitions/creality_ender5.def.json +++ b/resources/definitions/creality_ender5.def.json @@ -4,7 +4,7 @@ "inherits": "creality_base", "overrides": { "machine_name": { "default_value": "Creality Ender-5" }, - "machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y0 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, + "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y0 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, "machine_width": { "default_value": 220 }, "machine_depth": { "default_value": 220 }, "machine_height": { "default_value": 300 }, From 577365cf968a78767a8b45f843f924e8e221191a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 15 Oct 2019 08:19:32 +0200 Subject: [PATCH 08/12] Fx containerAdded handling in VariantNode CURA-6889 --- cura/Machines/VariantNode.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cura/Machines/VariantNode.py b/cura/Machines/VariantNode.py index 27e359afb1..efe15dbec2 100644 --- a/cura/Machines/VariantNode.py +++ b/cura/Machines/VariantNode.py @@ -101,6 +101,14 @@ class VariantNode(ContainerNode): def _materialAdded(self, container: ContainerInterface) -> None: if container.getMetaDataEntry("type") != "material": return # Not interested. + if not ContainerRegistry.getInstance().findContainersMetadata(id = container.getId()): + # CURA-6889 + # containerAdded and removed signals may be triggered in the next event cycle. If a container gets added + # and removed in the same event cycle, in the next cycle, the connections should just ignore the signals. + # The check here makes sure that the container in the signal still exists. + Logger.log("d", "Got container added signal for container [%s] but it no longer exists, do nothing.", + container.getId()) + return if not self.machine.has_materials: return # We won't add any materials. material_definition = container.getMetaDataEntry("definition") From 84080b675b7b5ac1377fc1ec287eb2bb59b75529 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 15 Oct 2019 08:29:53 +0200 Subject: [PATCH 09/12] Fix tests --- tests/Machines/TestVariantNode.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/Machines/TestVariantNode.py b/tests/Machines/TestVariantNode.py index 71257bd972..4e58069be3 100644 --- a/tests/Machines/TestVariantNode.py +++ b/tests/Machines/TestVariantNode.py @@ -104,10 +104,11 @@ def test_variantNodeInit_excludedMaterial(container_registry, machine_node): def test_materialAdded(container_registry, machine_node, metadata, material_result_list): variant_node = createVariantNode("machine_1", machine_node, container_registry) machine_node.exclude_materials = ["material_3"] - with patch("cura.Machines.VariantNode.MaterialNode"): # We're not testing the material node here, so patch it out. - with patch.dict(metadata_dict, metadata): - mocked_container = createMockedInstanceContainer() - variant_node._materialAdded(mocked_container) + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)): + with patch("cura.Machines.VariantNode.MaterialNode"): # We're not testing the material node here, so patch it out. + with patch.dict(metadata_dict, metadata): + mocked_container = createMockedInstanceContainer() + variant_node._materialAdded(mocked_container) assert len(material_result_list) == len(variant_node.materials) for name in material_result_list: From ca21268a121f2d26312156e287d3a3b2874ab031 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 15 Oct 2019 10:26:52 +0200 Subject: [PATCH 10/12] Elide the profile text in the middle for the PrintSetupSelector. This makes sure that at least some part of the name and some part of the variant details (qualityy, diameter) is visible CURA-6862 --- resources/qml/IconWithText.qml | 1 + resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml | 1 + 2 files changed, 2 insertions(+) diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 24b6dc7fe2..b9fe873b25 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -19,6 +19,7 @@ Item property alias color: label.color property alias text: label.text property alias font: label.font + property alias elide: label.elide property real margin: UM.Theme.getSize("narrow_margin").width // These properties can be used in combination with layouts. diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml index a23b87fdbe..1a15980693 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -37,6 +37,7 @@ RowLayout return "" } font: UM.Theme.getFont("medium") + elide: Text.ElideMiddle UM.SettingPropertyProvider { From 71701f15cf9c271d9f77a2055e710fc4417cb503 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 15 Oct 2019 11:38:51 +0200 Subject: [PATCH 11/12] Remove function to reintroduce binding. Also force max width to integer value and comment why there is a plain value like 0.7 in the code. part of CURA-6862 --- .../qml/PrintSetupSelector/Custom/CustomPrintSetup.qml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index cf27d6d023..b559aa711c 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -88,20 +88,15 @@ Item Label { id: textLabel - text: qualityName() + text: Cura.MachineManager.activeQualityDisplayNameMap["main"] font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") Layout.margins: 0 - Layout.maximumWidth: parent.width * 0.7 + Layout.maximumWidth: Math.floor(parent.width * 0.7) // Always leave >= 30% for the rest of the row. height: contentHeight verticalAlignment: Text.AlignVCenter renderType: Text.NativeRendering elide: Text.ElideRight - - function qualityName() { - var resultMap = Cura.MachineManager.activeQualityDisplayNameMap - return resultMap["main"] - } } Label From 36aab5d56bda56a7edb3c1ea4156a693f33d7f26 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 15 Oct 2019 13:04:20 +0200 Subject: [PATCH 12/12] Fix removeMaterials() and optimization CURA-6886 --- .../Models/MaterialManagementModel.py | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/cura/Machines/Models/MaterialManagementModel.py b/cura/Machines/Models/MaterialManagementModel.py index b4f3bb9889..a494e35c89 100644 --- a/cura/Machines/Models/MaterialManagementModel.py +++ b/cura/Machines/Models/MaterialManagementModel.py @@ -8,6 +8,7 @@ import uuid # To generate new GUIDs for new materials. from UM.i18n import i18nCatalog from UM.Logger import Logger +from UM.Signal import postponeSignals, CompressTechnique import cura.CuraApplication # Imported like this to prevent circular imports. from cura.Machines.ContainerTree import ContainerTree @@ -73,8 +74,20 @@ class MaterialManagementModel(QObject): def removeMaterial(self, material_node: "MaterialNode") -> None: container_registry = CuraContainerRegistry.getInstance() materials_this_base_file = container_registry.findContainersMetadata(base_file = material_node.base_file) - for material_metadata in materials_this_base_file: - container_registry.removeContainer(material_metadata["id"]) + + # The material containers belonging to the same material file are supposed to work together. This postponeSignals() + # does two things: + # - optimizing the signal emitting. + # - making sure that the signals will only be emitted after all the material containers have been removed. + with postponeSignals(container_registry.containerRemoved, compress = CompressTechnique.CompressPerParameterValue): + # CURA-6886: Some containers may not have been loaded. If remove one material container, its material file + # will be removed. If later we remove a sub-material container which hasn't been loaded previously, it will + # crash because removeContainer() requires to load the container first, but the material file was already + # gone. + for material_metadata in materials_this_base_file: + container_registry.findInstanceContainers(id = material_metadata["id"]) + for material_metadata in materials_this_base_file: + container_registry.removeContainer(material_metadata["id"]) ## Creates a duplicate of a material with the same GUID and base_file # metadata. @@ -128,15 +141,17 @@ class MaterialManagementModel(QObject): new_container.getMetaData().update(new_metadata) new_containers.append(new_container) - for container_to_add in new_containers: - container_to_add.setDirty(True) - container_registry.addContainer(container_to_add) + # Optimization. Serving the same purpose as the postponeSignals() in removeMaterial() + with postponeSignals(container_registry.containerAdded, compress=CompressTechnique.CompressPerParameterValue): + for container_to_add in new_containers: + container_to_add.setDirty(True) + container_registry.addContainer(container_to_add) - # If the duplicated material was favorite then the new material should also be added to the favorites. - favorites_set = set(application.getPreferences().getValue("cura/favorite_materials").split(";")) - if base_file in favorites_set: - favorites_set.add(new_base_id) - application.getPreferences().setValue("cura/favorite_materials", ";".join(favorites_set)) + # If the duplicated material was favorite then the new material should also be added to the favorites. + favorites_set = set(application.getPreferences().getValue("cura/favorite_materials").split(";")) + if base_file in favorites_set: + favorites_set.add(new_base_id) + application.getPreferences().setValue("cura/favorite_materials", ";".join(favorites_set)) return new_base_id