diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 908bc4cd6f..0319b2a9ba 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -7,7 +7,7 @@ from UM.Decorators import override from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase from UM.Settings.ContainerStack import ContainerStack from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.Interfaces import ContainerInterface +from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext from . import Exceptions from .CuraContainerStack import CuraContainerStack @@ -57,21 +57,32 @@ class ExtruderStack(CuraContainerStack): # \throws Exceptions.NoGlobalStackError Raised when trying to get a property from an extruder without # having a next stack set. @override(ContainerStack) - def getProperty(self, key: str, property_name: str) -> Any: + def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any: if not self._next_stack: raise Exceptions.NoGlobalStackError("Extruder {id} is missing the next stack!".format(id = self.id)) - if not super().getProperty(key, "settable_per_extruder"): - return self.getNextStack().getProperty(key, property_name) + if context is None: + context = PropertyEvaluationContext() + context.pushContainer(self) - limit_to_extruder = super().getProperty(key, "limit_to_extruder") + if not super().getProperty(key, "settable_per_extruder", context): + result = self.getNextStack().getProperty(key, property_name, context) + context.popContainer() + return result + + limit_to_extruder = super().getProperty(key, "limit_to_extruder", context) + if limit_to_extruder is not None: + limit_to_extruder = str(limit_to_extruder) if (limit_to_extruder is not None and limit_to_extruder != "-1") and self.getMetaDataEntry("position") != str(limit_to_extruder): if str(limit_to_extruder) in self.getNextStack().extruders: - result = self.getNextStack().extruders[str(limit_to_extruder)].getProperty(key, property_name) + result = self.getNextStack().extruders[str(limit_to_extruder)].getProperty(key, property_name, context) if result is not None: + context.popContainer() return result - return super().getProperty(key, property_name) + result = super().getProperty(key, property_name, context) + context.popContainer() + return result @override(CuraContainerStack) def _getMachineDefinition(self) -> ContainerInterface: diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index c09889c9c5..bee82e2272 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -11,6 +11,7 @@ from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase from UM.Settings.ContainerStack import ContainerStack from UM.Settings.SettingInstance import InstanceState from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Settings.Interfaces import PropertyEvaluationContext from UM.Logger import Logger from . import Exceptions @@ -91,29 +92,38 @@ class GlobalStack(CuraContainerStack): # # \return The value of the property for the specified setting, or None if not found. @override(ContainerStack) - def getProperty(self, key: str, property_name: str) -> Any: + def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any: if not self.definition.findDefinitions(key = key): return None # Handle the "resolve" property. if self._shouldResolve(key, property_name): self._resolving_settings.add(key) - resolve = super().getProperty(key, "resolve") + resolve = super().getProperty(key, "resolve", context) self._resolving_settings.remove(key) if resolve is not None: return resolve + if context is None: + context = PropertyEvaluationContext() + context.pushContainer(self) + # Handle the "limit_to_extruder" property. - limit_to_extruder = super().getProperty(key, "limit_to_extruder") + limit_to_extruder = super().getProperty(key, "limit_to_extruder", context) + if limit_to_extruder is not None: + limit_to_extruder = str(limit_to_extruder) if limit_to_extruder is not None and limit_to_extruder != "-1" and limit_to_extruder in self._extruders: - if super().getProperty(key, "settable_per_extruder"): - result = self._extruders[str(limit_to_extruder)].getProperty(key, property_name) + if super().getProperty(key, "settable_per_extruder", context): + result = self._extruders[str(limit_to_extruder)].getProperty(key, property_name, context) if result is not None: + context.popContainer() return result else: Logger.log("e", "Setting {setting} has limit_to_extruder but is not settable per extruder!", setting = key) - return super().getProperty(key, property_name) + result = super().getProperty(key, property_name, context) + context.popContainer() + return result ## Overridden from ContainerStack # diff --git a/cura/Settings/PerObjectContainerStack.py b/cura/Settings/PerObjectContainerStack.py new file mode 100644 index 0000000000..6c54ed46d5 --- /dev/null +++ b/cura/Settings/PerObjectContainerStack.py @@ -0,0 +1,65 @@ +from typing import Any, Optional + +from UM.Application import Application +from UM.Decorators import override +from UM.Settings.Interfaces import PropertyEvaluationContext +from UM.Settings.ContainerStack import ContainerStack +from UM.Settings.SettingInstance import InstanceState + + +class PerObjectContainerStack(ContainerStack): + + @override(ContainerStack) + def getProperty(self, key: str, property_name: str, context: Optional[PropertyEvaluationContext] = None) -> Any: + if context is None: + context = PropertyEvaluationContext() + context.pushContainer(self) + + global_stack = Application.getInstance().getGlobalContainerStack() + + # Return the user defined value if present, otherwise, evaluate the value according to the default routine. + if self.getContainer(0).hasProperty(key, property_name): + if self.getContainer(0)._instances[key].state == InstanceState.User: + result = super().getProperty(key, property_name, context) + context.popContainer() + return result + + # Handle the "limit_to_extruder" property. + limit_to_extruder = super().getProperty(key, "limit_to_extruder", context) + if limit_to_extruder is not None: + limit_to_extruder = str(limit_to_extruder) + + # if this stack has the limit_to_extruder "not overriden", use the original limit_to_extruder as the current + # limit_to_extruder, so the values retrieved will be from the perspective of the original limit_to_extruder + # stack. + if limit_to_extruder == "-1": + if "original_limit_to_extruder" in context.context: + limit_to_extruder = context.context["original_limit_to_extruder"] + + if limit_to_extruder is not None and limit_to_extruder != "-1" and limit_to_extruder in global_stack.extruders: + # set the original limit_to_extruder if this is the first stack that has a non-overriden limit_to_extruder + if "original_limit_to_extruder" not in context.context: + context.context["original_limit_to_extruder"] = limit_to_extruder + + if super().getProperty(key, "settable_per_extruder", context): + result = global_stack.extruders[str(limit_to_extruder)].getProperty(key, property_name, context) + if result is not None: + context.popContainer() + return result + + result = super().getProperty(key, property_name, context) + context.popContainer() + return result + + @override(ContainerStack) + def setNextStack(self, stack: ContainerStack): + super().setNextStack(stack) + + # trigger signal to re-evaluate all default settings + for key, instance in self.getContainer(0)._instances.items(): + # only evaluate default settings + if instance.state != InstanceState.Default: + continue + + self._collectPropertyChanges(key, "value") + self._emitCollectedPropertyChanges() diff --git a/cura/Settings/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py index 503e0b2490..2a69353885 100644 --- a/cura/Settings/SettingOverrideDecorator.py +++ b/cura/Settings/SettingOverrideDecorator.py @@ -5,13 +5,13 @@ import copy from UM.Scene.SceneNodeDecorator import SceneNodeDecorator from UM.Signal import Signal, signalemitter -from UM.Settings.ContainerStack import ContainerStack from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Logger import Logger from UM.Application import Application +from cura.Settings.PerObjectContainerStack import PerObjectContainerStack from cura.Settings.ExtruderManager import ExtruderManager ## A decorator that adds a container stack to a Node. This stack should be queried for all settings regarding @@ -24,7 +24,7 @@ class SettingOverrideDecorator(SceneNodeDecorator): def __init__(self): super().__init__() - self._stack = ContainerStack(stack_id = id(self)) + self._stack = PerObjectContainerStack(stack_id = id(self)) self._stack.setDirty(False) # This stack does not need to be saved. self._stack.addContainer(InstanceContainer(container_id = "SettingOverrideInstanceContainer")) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 07a8fc21bc..28cd2759f6 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -135,6 +135,8 @@ Item { } } + // Specialty provider that only watches global_inherits (we cant filter on what property changed we get events + // so we bypass that to make a dedicated provider). UM.SettingPropertyProvider { id: provider @@ -146,8 +148,6 @@ Item { removeUnusedValue: false } - // Specialty provider that only watches global_inherits (we cant filter on what property changed we get events - // so we bypass that to make a dedicated provider). UM.SettingPropertyProvider { id: inheritStackProvider @@ -156,36 +156,42 @@ Item { watchedProperties: [ "limit_to_extruder" ] } - Binding + Connections { - target: provider - property: "containerStackId" - when: model.settable_per_extruder || (inheritStackProvider.properties.limit_to_extruder != null && inheritStackProvider.properties.limit_to_extruder >= 0); - value: + target: inheritStackProvider + onPropertiesChanged: { - // associate this binding with Cura.MachineManager.activeMachineId in the beginning so this - // binding will be triggered when activeMachineId is changed too. - // Otherwise, if this value only depends on the extruderIds, it won't get updated when the - // machine gets changed. - var activeMachineId = Cura.MachineManager.activeMachineId; + provider.forcePropertiesChanged(); + } + } - if(!model.settable_per_extruder || machineExtruderCount.properties.value == 1) + Connections + { + target: UM.ActiveTool + onPropertiesChanged: + { + // the values cannot be bound with UM.ActiveTool.properties.getValue() calls, + // so here we connect to the signal and update the those values. + if (typeof UM.ActiveTool.properties.getValue("SelectedObjectId") !== "undefined") { - //Not settable per extruder or there only is global, so we must pick global. - return activeMachineId; + const selectedObjectId = UM.ActiveTool.properties.getValue("SelectedObjectId"); + if (addedSettingsModel.visibilityHandler.selectedObjectId != selectedObjectId) + { + addedSettingsModel.visibilityHandler.selectedObjectId = selectedObjectId; + } } - if(inheritStackProvider.properties.limit_to_extruder != null && inheritStackProvider.properties.limit_to_extruder >= 0) + if (typeof UM.ActiveTool.properties.getValue("ContainerID") !== "undefined") { - //We have limit_to_extruder, so pick that stack. - return ExtruderManager.extruderIds[String(inheritStackProvider.properties.limit_to_extruder)]; + const containerId = UM.ActiveTool.properties.getValue("ContainerID"); + if (provider.containerStackId != containerId) + { + provider.containerStackId = containerId; + } + if (inheritStackProvider.containerStackId != containerId) + { + inheritStackProvider.containerStackId = containerId; + } } - if(UM.ActiveTool.properties.getValue("ContainerID")) - { - //We're on an extruder tab. Pick the current extruder. - return UM.ActiveTool.properties.getValue("ContainerID"); - } - //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab. - return activeMachineId; } } } diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index 112edb5049..829a0b5c12 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -142,7 +142,7 @@ Item { { id: linkedSettingIcon; - visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && (!definition.settable_per_extruder || globalPropertyProvider.properties.limit_to_extruder != "-1") && base.showLinkedSettingIcon + visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && (!definition.settable_per_extruder || String(globalPropertyProvider.properties.limit_to_extruder) != "-1") && base.showLinkedSettingIcon height: parent.height; width: height; @@ -222,7 +222,7 @@ Item { } // If the setting does not have a limit_to_extruder property (or is -1), use the active stack. - if(globalPropertyProvider.properties.limit_to_extruder == null || globalPropertyProvider.properties.limit_to_extruder == -1) + if(globalPropertyProvider.properties.limit_to_extruder == null || String(globalPropertyProvider.properties.limit_to_extruder) == "-1") { return Cura.SettingInheritanceManager.settingsWithInheritanceWarning.indexOf(definition.key) >= 0; } @@ -232,7 +232,7 @@ Item { // Observed when loading workspace, probably when SettingItems are removed. return false; } - return Cura.SettingInheritanceManager.getOverridesForExtruder(definition.key, globalPropertyProvider.properties.limit_to_extruder).indexOf(definition.key) >= 0; + return Cura.SettingInheritanceManager.getOverridesForExtruder(definition.key, String(globalPropertyProvider.properties.limit_to_extruder)).indexOf(definition.key) >= 0; } height: parent.height;