diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index fa045e9496..178cf31e3f 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -12,6 +12,7 @@ from UM.Scene.Selection import Selection from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Settings.ContainerRegistry import ContainerRegistry # Finding containers by ID. from UM.Settings.SettingFunction import SettingFunction +from UM.Settings.SettingInstance import SettingInstance from UM.Settings.ContainerStack import ContainerStack from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext from typing import Optional, List, TYPE_CHECKING, Union @@ -397,16 +398,67 @@ class ExtruderManager(QObject): extruder_train.setNextStack(global_stack) extruders_changed = True - # FIX: We have to remove those settings here because we know that those values have been copied to all - # the extruders at this point. - for key in ("material_diameter", "machine_nozzle_size"): - if global_stack.definitionChanges.hasProperty(key, "value"): - global_stack.definitionChanges.removeInstance(key, postpone_emit = True) - + self._fixMaterialDiameterAndNozzleSize(global_stack, extruder_trains) if extruders_changed: self.extrudersChanged.emit(global_stack_id) self.setActiveExtruderIndex(0) + def _fixMaterialDiameterAndNozzleSize(self, global_stack, extruder_stack_list): + keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders + + extruder_positions_to_update = set() + for extruder_stack in extruder_stack_list: + for key in keys_to_copy: + # Only copy the value when this extruder doesn't have the value. + if extruder_stack.definitionChanges.hasProperty(key, "value"): + continue + + # + # We cannot add a setting definition of "material_diameter" into the extruder's definition at runtime + # because all other machines which uses "fdmextruder" as the extruder definition will be affected. + # + # The problem is that single extrusion machines have their default material diameter defined in the global + # definitions. Now we automatically create an extruder stack for those machines using "fdmextruder" + # definition, which doesn't have the specific "material_diameter" and "machine_nozzle_size" defined for + # each machine. This results in wrong values which can be found in the MachineSettings dialog. + # + # To solve this, we put "material_diameter" back into the "fdmextruder" definition because modifying it in + # the extruder definition will affect all machines which uses the "fdmextruder" definition. Moreover, now + # we also check the value defined in the machine definition. If present, the value defined in the global + # stack's definition changes container will be copied. Otherwise, we will check if the default values in the + # machine definition and the extruder definition are the same, and if not, the default value in the machine + # definition will be copied to the extruder stack's definition changes. + # + setting_value_in_global_def_changes = global_stack.definitionChanges.getProperty(key, "value") + setting_value_in_global_def = global_stack.definition.getProperty(key, "value") + setting_value = setting_value_in_global_def + if setting_value_in_global_def_changes is not None: + setting_value = setting_value_in_global_def_changes + if setting_value == extruder_stack.definition.getProperty(key, "value"): + continue + + setting_definition = global_stack.getSettingDefinition(key) + new_instance = SettingInstance(setting_definition, extruder_stack.definitionChanges) + new_instance.setProperty("value", setting_value) + new_instance.resetState() # Ensure that the state is not seen as a user state. + extruder_stack.definitionChanges.addInstance(new_instance) + extruder_stack.definitionChanges.setDirty(True) + + # Make sure the material diameter is up to date for the extruder stack. + if key == "material_diameter": + position = int(extruder_stack.getMetaDataEntry("position")) + extruder_positions_to_update.add(position) + + # We have to remove those settings here because we know that those values have been copied to all + # the extruders at this point. + for key in keys_to_copy: + if global_stack.definitionChanges.hasProperty(key, "value"): + global_stack.definitionChanges.removeInstance(key, postpone_emit = True) + + # Update material diameter for extruders + for position in extruder_positions_to_update: + self.updateMaterialForDiameter(position, global_stack = global_stack) + ## Get all extruder values for a certain setting. # # This is exposed to SettingFunction so it can be used in value functions. @@ -492,10 +544,11 @@ class ExtruderManager(QObject): return ExtruderManager.getExtruderValues(key) ## Updates the material container to a material that matches the material diameter set for the printer - def updateMaterialForDiameter(self, extruder_position: int): - global_stack = Application.getInstance().getGlobalContainerStack() + def updateMaterialForDiameter(self, extruder_position: int, global_stack = None): if not global_stack: - return + global_stack = Application.getInstance().getGlobalContainerStack() + if not global_stack: + return if not global_stack.getMetaDataEntry("has_materials", False): return diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 5a3ac85d75..870de62c98 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -41,75 +41,6 @@ class ExtruderStack(CuraContainerStack): # For backward compatibility: Register the extruder with the Extruder Manager ExtruderManager.getInstance().registerExtruder(self, stack.id) - # Now each machine will have at least one extruder stack. If this is the first extruder, the extruder-specific - # settings such as nozzle size and material diameter should be moved from the machine's definition_changes to - # the this extruder's definition_changes. - # - # We do this here because it is tooooo expansive to do it in the version upgrade: During the version upgrade, - # when we are upgrading a definition_changes container file, there is NO guarantee that other files such as - # machine an extruder stack files are upgraded before this, so we cannot read those files assuming they are in - # the latest format. - # - # MORE: - # For single-extrusion machines, nozzle size is saved in the global stack, so the nozzle size value should be - # carried to the first extruder. - # For material diameter, it was supposed to be applied to all extruders, so its value should be copied to all - # extruders. - - keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders - - for key in keys_to_copy: - # Only copy the value when this extruder doesn't have the value. - if self.definitionChanges.hasProperty(key, "value"): - continue - - # WARNING: this might be very dangerous and should be refactored ASAP! - # - # We cannot add a setting definition of "material_diameter" into the extruder's definition at runtime - # because all other machines which uses "fdmextruder" as the extruder definition will be affected. - # - # The problem is that single extrusion machines have their default material diameter defined in the global - # definitions. Now we automatically create an extruder stack for those machines using "fdmextruder" - # definition, which doesn't have the specific "material_diameter" and "machine_nozzle_size" defined for - # each machine. This results in wrong values which can be found in the MachineSettings dialog. - # - # To solve this, we put "material_diameter" back into the "fdmextruder" definition because modifying it in - # the extruder definition will affect all machines which uses the "fdmextruder" definition. Moreover, now - # we also check the value defined in the machine definition. If present, the value defined in the global - # stack's definition changes container will be copied. Otherwise, we will check if the default values in the - # machine definition and the extruder definition are the same, and if not, the default value in the machine - # definition will be copied to the extruder stack's definition changes. - # - setting_value_in_global_def_changes = stack.definitionChanges.getProperty(key, "value") - setting_value_in_global_def = stack.definition.getProperty(key, "value") - setting_value = setting_value_in_global_def - if setting_value_in_global_def_changes is not None: - setting_value = setting_value_in_global_def_changes - if setting_value == self.definition.getProperty(key, "value"): - continue - - setting_definition = stack.getSettingDefinition(key) - new_instance = SettingInstance(setting_definition, self.definitionChanges) - new_instance.setProperty("value", setting_value) - new_instance.resetState() # Ensure that the state is not seen as a user state. - self.definitionChanges.addInstance(new_instance) - self.definitionChanges.setDirty(True) - - # Make sure the material diameter is up to date for the extruder stack. - if key == "material_diameter": - from cura.CuraApplication import CuraApplication - machine_manager = CuraApplication.getInstance().getMachineManager() - position = self.getMetaDataEntry("position", "0") - func = lambda p = position: CuraApplication.getInstance().getExtruderManager().updateMaterialForDiameter(p) - machine_manager.machine_extruder_material_update_dict[stack.getId()].append(func) - - # NOTE: We cannot remove the setting from the global stack's definition changes container because for - # material diameter, it needs to be applied to all extruders, but here we don't know how many extruders - # a machine actually has and how many extruders has already been loaded for that machine, so we have to - # keep this setting for any remaining extruders that haven't been loaded yet. - # - # Those settings will be removed in ExtruderManager which knows all those info. - @override(ContainerStack) def getNextStack(self) -> Optional["GlobalStack"]: return super().getNextStack()