diff --git a/cura/Machines/Models/MaterialManagementModel.py b/cura/Machines/Models/MaterialManagementModel.py index 8a143f46b2..f00b81e987 100644 --- a/cura/Machines/Models/MaterialManagementModel.py +++ b/cura/Machines/Models/MaterialManagementModel.py @@ -141,6 +141,21 @@ class MaterialManagementModel(QObject): new_container.getMetaData().update(new_metadata) new_containers.append(new_container) + # CURA-6863: Nodes in ContainerTree will be updated upon ContainerAdded signals, one at a time. It will use the + # best fit material container at the time it sees one. For example, if you duplicate and get generic_pva #2, + # if the node update function sees the containers in the following order: + # + # - generic_pva #2 + # - generic_pva #2_um3_aa04 + # + # It will first use "generic_pva #2" because that's the best fit it has ever seen, and later "generic_pva #2_um3_aa04" + # once it sees that. Because things run in the Qt event loop, they don't happen at the same time. This means if + # between those two events, the ContainerTree will have nodes that contain invalid data. + # + # This sort fixes the problem by emitting the most specific containers first. + new_containers = sorted(new_containers, key = lambda x: x.getId(), reverse = True) + + # Optimization. Serving the same purpose as the postponeSignals() in removeMaterial() # postpone the signals emitted when duplicating materials. This is easier on the event loop; changes the # behavior to be like a transaction. Prevents concurrency issues. with postponeSignals(container_registry.containerAdded, compress=CompressTechnique.CompressPerParameterValue): diff --git a/cura/Machines/VariantNode.py b/cura/Machines/VariantNode.py index efe15dbec2..1c5474422e 100644 --- a/cura/Machines/VariantNode.py +++ b/cura/Machines/VariantNode.py @@ -1,15 +1,18 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - from typing import Optional, TYPE_CHECKING from UM.Logger import Logger from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.Interfaces import ContainerInterface from UM.Signal import Signal + +from cura.Settings.cura_empty_instance_containers import empty_variant_container from cura.Machines.ContainerNode import ContainerNode from cura.Machines.MaterialNode import MaterialNode + import UM.FlameProfiler + if TYPE_CHECKING: from typing import Dict from cura.Machines.MachineNode import MachineNode @@ -119,18 +122,18 @@ class VariantNode(ContainerNode): if base_file not in self.materials: # Completely new base file. Always better than not having a file as long as it matches our set-up. if material_definition != "fdmprinter" and material_definition != self.machine.container_id: return - material_variant = container.getMetaDataEntry("variant_name", "empty") - if material_variant != "empty" and material_variant != self.variant_name: + material_variant = container.getMetaDataEntry("variant_name", empty_variant_container.getName()) + if material_variant != self.variant_name: return else: # We already have this base profile. Replace the base profile if the new one is more specific. new_definition = container.getMetaDataEntry("definition") if new_definition == "fdmprinter": return # Just as unspecific or worse. - if new_definition != self.machine.container_id: + material_variant = container.getMetaDataEntry("variant_name") + if new_definition != self.machine.container_id or material_variant != self.variant_name: return # Doesn't match this set-up. original_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = self.materials[base_file].container_id)[0] - original_variant = original_metadata.get("variant_name", "empty") - if original_variant != "empty" or container.getMetaDataEntry("variant_name", "empty") == "empty": + if "variant_name" in original_metadata or material_variant is None: return # Original was already specific or just as unspecific as the new one. if "empty_material" in self.materials: diff --git a/cura/PrinterOutput/Models/ExtruderConfigurationModel.py b/cura/PrinterOutput/Models/ExtruderConfigurationModel.py index 04a3c95afd..4a1cf4916f 100644 --- a/cura/PrinterOutput/Models/ExtruderConfigurationModel.py +++ b/cura/PrinterOutput/Models/ExtruderConfigurationModel.py @@ -25,9 +25,10 @@ class ExtruderConfigurationModel(QObject): return self._position def setMaterial(self, material: Optional[MaterialOutputModel]) -> None: - if self._material != material: - self._material = material - self.extruderConfigurationChanged.emit() + if material is None or self._material == material: + return + self._material = material + self.extruderConfigurationChanged.emit() @pyqtProperty(QObject, fset = setMaterial, notify = extruderConfigurationChanged) def activeMaterial(self) -> Optional[MaterialOutputModel]: diff --git a/resources/qml/Preferences/Materials/MaterialsList.qml b/resources/qml/Preferences/Materials/MaterialsList.qml index 96f6730029..8b82a87820 100644 --- a/resources/qml/Preferences/Materials/MaterialsList.qml +++ b/resources/qml/Preferences/Materials/MaterialsList.qml @@ -114,7 +114,7 @@ Item if (base.toActivateNewMaterial) { var position = Cura.ExtruderManager.activeExtruderIndex - Cura.MachineManager.setMaterial(position, base.currentItem.container_node) + Cura.MachineManager.setMaterialById(position, base.newRootMaterialIdToSwitchTo) } base.newRootMaterialIdToSwitchTo = "" base.toActivateNewMaterial = false diff --git a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml index 2c1e2128e1..3c36e12651 100644 --- a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -21,6 +21,7 @@ Item property var colorsModel: materialType != null ? materialType.colors: null height: childrenRect.height width: parent.width + anchors.left: parent.left Rectangle { id: material_type_header_background