コード例 #1
0
class SettingOverrideDecorator(SceneNodeDecorator):
    """A decorator that adds a container stack to a Node. This stack should be queried for all settings regarding

    the linked node. The Stack in question will refer to the global stack (so that settings that are not defined by
    this stack still resolve.
    """
    activeExtruderChanged = Signal()
    """Event indicating that the user selected a different extruder."""

    _non_printing_mesh_settings = {
        "anti_overhang_mesh", "infill_mesh", "cutting_mesh"
    }
    """Non-printing meshes

    If these settings are True for any mesh, the mesh does not need a convex hull,
    and is sent to the slicer regardless of whether it fits inside the build volume.
    Note that Support Mesh is not in here because it actually generates
    g-code in the volume of the mesh.
    """
    _non_thumbnail_visible_settings = {
        "anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"
    }

    def __init__(self):
        super().__init__()
        self._stack = PerObjectContainerStack(
            container_id="per_object_stack_" + str(id(self)))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        user_container = InstanceContainer(
            container_id=self._generateUniqueName())
        user_container.setMetaDataEntry("type", "user")
        self._stack.userChanges = user_container
        self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(
            0).getId()

        self._is_non_printing_mesh = False
        self._is_non_thumbnail_visible_mesh = False

        self._stack.propertyChanged.connect(self._onSettingChanged)

        Application.getInstance().getContainerRegistry().addContainer(
            self._stack)

        Application.getInstance().globalContainerStackChanged.connect(
            self._updateNextStack)
        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()

    def _generateUniqueName(self):
        return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()

    def __deepcopy__(self, memo):
        deep_copy = SettingOverrideDecorator()
        """Create a fresh decorator object"""

        instance_container = copy.deepcopy(self._stack.getContainer(0), memo)
        """Copy the instance"""

        # A unique name must be added, or replaceContainer will not replace it
        instance_container.setMetaDataEntry("id", self._generateUniqueName())

        ## Set the copied instance as the first (and only) instance container of the stack.
        deep_copy._stack.replaceContainer(0, instance_container)

        # Properly set the right extruder on the copy
        deep_copy.setActiveExtruder(self._extruder_stack)

        # use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
        # has not been updated yet.
        deep_copy._is_non_printing_mesh = self._evaluateIsNonPrintingMesh()
        deep_copy._is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh(
        )

        return deep_copy

    def getActiveExtruder(self):
        """Gets the currently active extruder to print this object with.

        :return: An extruder's container stack.
        """

        return self._extruder_stack

    def getActiveExtruderChangedSignal(self):
        """Gets the signal that emits if the active extruder changed.

        This can then be accessed via a decorator.
        """

        return self.activeExtruderChanged

    def getActiveExtruderPosition(self):
        """Gets the currently active extruders position

        :return: An extruder's position, or None if no position info is available.
        """

        # for support_meshes, always use the support_extruder
        if self.getStack().getProperty("support_mesh", "value"):
            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()
            if global_container_stack:
                return str(
                    global_container_stack.getProperty("support_extruder_nr",
                                                       "value"))

        containers = ContainerRegistry.getInstance().findContainers(
            id=self.getActiveExtruder())
        if containers:
            container_stack = containers[0]
            return container_stack.getMetaDataEntry("position", default=None)

    def isNonPrintingMesh(self):
        return self._is_non_printing_mesh

    def _evaluateIsNonPrintingMesh(self):
        return any(
            bool(self._stack.getProperty(setting, "value"))
            for setting in self._non_printing_mesh_settings)

    def isNonThumbnailVisibleMesh(self):
        return self._is_non_thumbnail_visible_mesh

    def _evaluateIsNonThumbnailVisibleMesh(self):
        return any(
            bool(self._stack.getProperty(setting, "value"))
            for setting in self._non_thumbnail_visible_settings)

    def _onSettingChanged(
            self, setting_key,
            property_name):  # Reminder: 'property' is a built-in function
        # We're only interested in a few settings and only if it's value changed.
        if property_name == "value":
            # Trigger slice/need slicing if the value has changed.
            self._is_non_printing_mesh = self._evaluateIsNonPrintingMesh()
            self._is_non_thumbnail_visible_mesh = self._evaluateIsNonThumbnailVisibleMesh(
            )
            Application.getInstance().getBackend().needsSlicing()
            Application.getInstance().getBackend().tickle()

    def _updateNextStack(self):
        """Makes sure that the stack upon which the container stack is placed is

        kept up to date.
        """
        if self._extruder_stack:
            extruder_stack = ContainerRegistry.getInstance(
            ).findContainerStacks(id=self._extruder_stack)
            if extruder_stack:
                if self._stack.getNextStack():
                    old_extruder_stack_id = self._stack.getNextStack().getId()
                else:
                    old_extruder_stack_id = ""

                self._stack.setNextStack(extruder_stack[0])
                # Trigger slice/need slicing if the extruder changed.
                if self._stack.getNextStack().getId() != old_extruder_stack_id:
                    Application.getInstance().getBackend().needsSlicing()
                    Application.getInstance().getBackend().tickle()
            else:
                Logger.log(
                    "e",
                    "Extruder stack %s below per-object settings does not exist.",
                    self._extruder_stack)
        else:
            self._stack.setNextStack(
                Application.getInstance().getGlobalContainerStack())

    def setActiveExtruder(self, extruder_stack_id):
        """Changes the extruder with which to print this node.

        :param extruder_stack_id: The new extruder stack to print with.
        """

        self._extruder_stack = extruder_stack_id
        self._updateNextStack()
        ExtruderManager.getInstance().resetSelectedObjectExtruders()
        self.activeExtruderChanged.emit()

    def getStack(self):
        return self._stack
コード例 #2
0
class SettingOverrideDecorator(SceneNodeDecorator):
    ##  Event indicating that the user selected a different extruder.
    activeExtruderChanged = Signal()

    ##  Non-printing meshes
    #
    #   If these settings are True for any mesh, the mesh does not need a convex hull,
    #   and is sent to the slicer regardless of whether it fits inside the build volume.
    #   Note that Support Mesh is not in here because it actually generates
    #   g-code in the volume of the mesh.
    _non_printing_mesh_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh"}
    _non_thumbnail_visible_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"}

    def __init__(self):
        super().__init__()
        self._stack = PerObjectContainerStack(container_id = "per_object_stack_" + str(id(self)))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        user_container = InstanceContainer(container_id = self._generateUniqueName())
        user_container.setMetaDataEntry("type", "user")
        self._stack.userChanges = user_container
        self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()

        self._is_non_printing_mesh = False
        self._is_non_thumbnail_visible_mesh = False

        self._stack.propertyChanged.connect(self._onSettingChanged)

        Application.getInstance().getContainerRegistry().addContainer(self._stack)

        Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()

    def _generateUniqueName(self):
        return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()

    def __deepcopy__(self, memo):
        ## Create a fresh decorator object
        deep_copy = SettingOverrideDecorator()

        ## Copy the instance
        instance_container = copy.deepcopy(self._stack.getContainer(0), memo)

        # A unique name must be added, or replaceContainer will not replace it
        instance_container.setMetaDataEntry("id", self._generateUniqueName())

        ## Set the copied instance as the first (and only) instance container of the stack.
        deep_copy._stack.replaceContainer(0, instance_container)

        # Properly set the right extruder on the copy
        deep_copy.setActiveExtruder(self._extruder_stack)

        # use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
        # has not been updated yet.
        deep_copy._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
        deep_copy._is_non_thumbnail_visible_mesh = self.evaluateIsNonThumbnailVisibleMesh()

        return deep_copy

    ##  Gets the currently active extruder to print this object with.
    #
    #   \return An extruder's container stack.
    def getActiveExtruder(self):
        return self._extruder_stack

    ##  Gets the signal that emits if the active extruder changed.
    #
    #   This can then be accessed via a decorator.
    def getActiveExtruderChangedSignal(self):
        return self.activeExtruderChanged

    ##  Gets the currently active extruders position
    #
    #   \return An extruder's position, or None if no position info is available.
    def getActiveExtruderPosition(self):
        containers = ContainerRegistry.getInstance().findContainers(id = self.getActiveExtruder())
        if containers:
            container_stack = containers[0]
            return container_stack.getMetaDataEntry("position", default=None)

    def isNonPrintingMesh(self):
        return self._is_non_printing_mesh

    def evaluateIsNonPrintingMesh(self):
        return any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)

    def isNonThumbnailVisibleMesh(self):
        return self._is_non_thumbnail_visible_mesh

    def evaluateIsNonThumbnailVisibleMesh(self):
        return any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_thumbnail_visible_settings)

    def _onSettingChanged(self, instance, property_name): # Reminder: 'property' is a built-in function
        if property_name == "value":
            # Trigger slice/need slicing if the value has changed.
            self._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
            self._is_non_thumbnail_visible_mesh = self.evaluateIsNonThumbnailVisibleMesh()

            Application.getInstance().getBackend().needsSlicing()
            Application.getInstance().getBackend().tickle()

    ##  Makes sure that the stack upon which the container stack is placed is
    #   kept up to date.
    def _updateNextStack(self):
        if self._extruder_stack:
            extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack)
            if extruder_stack:
                if self._stack.getNextStack():
                    old_extruder_stack_id = self._stack.getNextStack().getId()
                else:
                    old_extruder_stack_id = ""

                self._stack.setNextStack(extruder_stack[0])
                # Trigger slice/need slicing if the extruder changed.
                if self._stack.getNextStack().getId() != old_extruder_stack_id:
                    Application.getInstance().getBackend().needsSlicing()
                    Application.getInstance().getBackend().tickle()
            else:
                Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack)
        else:
            self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())

    ##  Changes the extruder with which to print this node.
    #
    #   \param extruder_stack_id The new extruder stack to print with.
    def setActiveExtruder(self, extruder_stack_id):
        self._extruder_stack = extruder_stack_id
        self._updateNextStack()
        ExtruderManager.getInstance().resetSelectedObjectExtruders()
        self.activeExtruderChanged.emit()

    def getStack(self):
        return self._stack
コード例 #3
0
class SettingOverrideDecorator(SceneNodeDecorator):
    ##  Event indicating that the user selected a different extruder.
    activeExtruderChanged = Signal()

    ##  Non-printing meshes
    #
    #   If these settings are True for any mesh, the mesh does not need a convex hull,
    #   and is sent to the slicer regardless of whether it fits inside the build volume.
    #   Note that Support Mesh is not in here because it actually generates
    #   g-code in the volume of the mesh.
    _non_printing_mesh_settings = {"anti_overhang_mesh", "infill_mesh", "cutting_mesh"}

    def __init__(self):
        super().__init__()
        self._stack = PerObjectContainerStack(stack_id = "per_object_stack_" + str(id(self)))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        self._stack.addContainer(InstanceContainer(container_id = "SettingOverrideInstanceContainer"))
        self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()

        self._is_non_printing_mesh = False

        self._stack.propertyChanged.connect(self._onSettingChanged)

        Application.getInstance().getContainerRegistry().addContainer(self._stack)

        Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()

    def __deepcopy__(self, memo):
        ## Create a fresh decorator object
        deep_copy = SettingOverrideDecorator()
        ## Copy the instance
        instance_container = copy.deepcopy(self._stack.getContainer(0), memo)

        ## Set the copied instance as the first (and only) instance container of the stack.
        deep_copy._stack.replaceContainer(0, instance_container)

        # Properly set the right extruder on the copy
        deep_copy.setActiveExtruder(self._extruder_stack)

        # use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
        # has not been updated yet.
        deep_copy._is_non_printing_mesh = any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)

        return deep_copy

    ##  Gets the currently active extruder to print this object with.
    #
    #   \return An extruder's container stack.
    def getActiveExtruder(self):
        return self._extruder_stack

    ##  Gets the signal that emits if the active extruder changed.
    #
    #   This can then be accessed via a decorator.
    def getActiveExtruderChangedSignal(self):
        return self.activeExtruderChanged

    ##  Gets the currently active extruders position
    #
    #   \return An extruder's position, or None if no position info is available.
    def getActiveExtruderPosition(self):
        containers = ContainerRegistry.getInstance().findContainers(id = self.getActiveExtruder())
        if containers:
            container_stack = containers[0]
            return container_stack.getMetaDataEntry("position", default=None)

    def isNonPrintingMesh(self):
        return self._is_non_printing_mesh

    def _onSettingChanged(self, instance, property_name): # Reminder: 'property' is a built-in function
        # Trigger slice/need slicing if the value has changed.
        if property_name == "value":
            self._is_non_printing_mesh = any(bool(self._stack.getProperty(setting, "value")) for setting in self._non_printing_mesh_settings)

            Application.getInstance().getBackend().needsSlicing()
            Application.getInstance().getBackend().tickle()

    ##  Makes sure that the stack upon which the container stack is placed is
    #   kept up to date.
    def _updateNextStack(self):
        if self._extruder_stack:
            extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack)
            if extruder_stack:
                if self._stack.getNextStack():
                    old_extruder_stack_id = self._stack.getNextStack().getId()
                else:
                    old_extruder_stack_id = ""

                self._stack.setNextStack(extruder_stack[0])
                # Trigger slice/need slicing if the extruder changed.
                if self._stack.getNextStack().getId() != old_extruder_stack_id:
                    Application.getInstance().getBackend().needsSlicing()
                    Application.getInstance().getBackend().tickle()
            else:
                Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack)
        else:
            self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())

    ##  Changes the extruder with which to print this node.
    #
    #   \param extruder_stack_id The new extruder stack to print with.
    def setActiveExtruder(self, extruder_stack_id):
        self._extruder_stack = extruder_stack_id
        self._updateNextStack()
        ExtruderManager.getInstance().resetSelectedObjectExtruders()
        self.activeExtruderChanged.emit()

    def getStack(self):
        return self._stack
コード例 #4
0
class SettingOverrideDecorator(SceneNodeDecorator):
    ##  Event indicating that the user selected a different extruder.
    activeExtruderChanged = Signal()

    def __init__(self):
        super().__init__()
        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"))

        if ExtruderManager.getInstance().extruderCount > 1:
            self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()
        else:
            self._extruder_stack = None

        self._stack.propertyChanged.connect(self._onSettingChanged)

        ContainerRegistry.getInstance().addContainer(self._stack)

        Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()

    def __deepcopy__(self, memo):
        ## Create a fresh decorator object
        deep_copy = SettingOverrideDecorator()
        ## Copy the instance
        instance_container = copy.deepcopy(self._stack.getContainer(0), memo)

        ## Set the copied instance as the first (and only) instance container of the stack.
        deep_copy._stack.replaceContainer(0, instance_container)

        # Properly set the right extruder on the copy
        deep_copy.setActiveExtruder(self._extruder_stack)

        return deep_copy

    ##  Gets the currently active extruder to print this object with.
    #
    #   \return An extruder's container stack.
    def getActiveExtruder(self):
        return self._extruder_stack

    ##  Gets the signal that emits if the active extruder changed.
    #
    #   This can then be accessed via a decorator.
    def getActiveExtruderChangedSignal(self):
        return self.activeExtruderChanged

    ##  Gets the currently active extruders position
    #
    #   \return An extruder's position, or None if no position info is available.
    def getActiveExtruderPosition(self):
        containers = ContainerRegistry.getInstance().findContainers(id = self.getActiveExtruder())
        if containers:
            container_stack = containers[0]
            return container_stack.getMetaDataEntry("position", default=None)

    def _onSettingChanged(self, instance, property_name): # Reminder: 'property' is a built-in function
        # Trigger slice/need slicing if the value has changed.
        if property_name == "value":
            Application.getInstance().getBackend().needsSlicing()
            Application.getInstance().getBackend().tickle()

    ##  Makes sure that the stack upon which the container stack is placed is
    #   kept up to date.
    def _updateNextStack(self):
        if self._extruder_stack:
            extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id = self._extruder_stack)
            if extruder_stack:
                if self._stack.getNextStack():
                    old_extruder_stack_id = self._stack.getNextStack().getId()
                else:
                    old_extruder_stack_id = ""

                self._stack.setNextStack(extruder_stack[0])
                # Trigger slice/need slicing if the extruder changed.
                if self._stack.getNextStack().getId() != old_extruder_stack_id:
                    Application.getInstance().getBackend().needsSlicing()
                    Application.getInstance().getBackend().tickle()
            else:
                Logger.log("e", "Extruder stack %s below per-object settings does not exist.", self._extruder_stack)
        else:
            self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())

    ##  Changes the extruder with which to print this node.
    #
    #   \param extruder_stack_id The new extruder stack to print with.
    def setActiveExtruder(self, extruder_stack_id):
        self._extruder_stack = extruder_stack_id
        self._updateNextStack()
        ExtruderManager.getInstance().resetSelectedObjectExtruders()
        self.activeExtruderChanged.emit()

    def getStack(self):
        return self._stack
コード例 #5
0
class SettingOverrideDecorator(SceneNodeDecorator):
    ##  Event indicating that the user selected a different extruder.
    activeExtruderChanged = Signal()

    def __init__(self):
        super().__init__()
        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"))

        if ExtruderManager.getInstance().extruderCount > 1:
            self._extruder_stack = ExtruderManager.getInstance(
            ).getExtruderStack(0).getId()
        else:
            self._extruder_stack = None

        self._stack.propertyChanged.connect(self._onSettingChanged)

        ContainerRegistry.getInstance().addContainer(self._stack)

        Application.getInstance().globalContainerStackChanged.connect(
            self._updateNextStack)
        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()

    def __deepcopy__(self, memo):
        ## Create a fresh decorator object
        deep_copy = SettingOverrideDecorator()
        ## Copy the instance
        instance_container = copy.deepcopy(self._stack.getContainer(0), memo)

        ## Set the copied instance as the first (and only) instance container of the stack.
        deep_copy._stack.replaceContainer(0, instance_container)

        # Properly set the right extruder on the copy
        deep_copy.setActiveExtruder(self._extruder_stack)

        return deep_copy

    ##  Gets the currently active extruder to print this object with.
    #
    #   \return An extruder's container stack.
    def getActiveExtruder(self):
        return self._extruder_stack

    ##  Gets the signal that emits if the active extruder changed.
    #
    #   This can then be accessed via a decorator.
    def getActiveExtruderChangedSignal(self):
        return self.activeExtruderChanged

    ##  Gets the currently active extruders position
    #
    #   \return An extruder's position, or None if no position info is available.
    def getActiveExtruderPosition(self):
        containers = ContainerRegistry.getInstance().findContainers(
            id=self.getActiveExtruder())
        if containers:
            container_stack = containers[0]
            return container_stack.getMetaDataEntry("position", default=None)

    def _onSettingChanged(
            self, instance,
            property_name):  # Reminder: 'property' is a built-in function
        # Trigger slice/need slicing if the value has changed.
        if property_name == "value":
            Application.getInstance().getBackend().needsSlicing()
            Application.getInstance().getBackend().tickle()

    ##  Makes sure that the stack upon which the container stack is placed is
    #   kept up to date.
    def _updateNextStack(self):
        if self._extruder_stack:
            extruder_stack = ContainerRegistry.getInstance(
            ).findContainerStacks(id=self._extruder_stack)
            if extruder_stack:
                if self._stack.getNextStack():
                    old_extruder_stack_id = self._stack.getNextStack().getId()
                else:
                    old_extruder_stack_id = ""

                self._stack.setNextStack(extruder_stack[0])
                # Trigger slice/need slicing if the extruder changed.
                if self._stack.getNextStack().getId() != old_extruder_stack_id:
                    Application.getInstance().getBackend().needsSlicing()
                    Application.getInstance().getBackend().tickle()
            else:
                Logger.log(
                    "e",
                    "Extruder stack %s below per-object settings does not exist.",
                    self._extruder_stack)
        else:
            self._stack.setNextStack(
                Application.getInstance().getGlobalContainerStack())

    ##  Changes the extruder with which to print this node.
    #
    #   \param extruder_stack_id The new extruder stack to print with.
    def setActiveExtruder(self, extruder_stack_id):
        self._extruder_stack = extruder_stack_id
        self._updateNextStack()
        ExtruderManager.getInstance().resetSelectedObjectExtruders()
        self.activeExtruderChanged.emit()

    def getStack(self):
        return self._stack
コード例 #6
0
class SettingOverrideDecorator(SceneNodeDecorator):
    ##  Event indicating that the user selected a different extruder.
    activeExtruderChanged = Signal()

    ##  Non-printing meshes
    #
    #   If these settings are True for any mesh, the mesh does not need a convex hull,
    #   and is sent to the slicer regardless of whether it fits inside the build volume.
    #   Note that Support Mesh is not in here because it actually generates
    #   g-code in the volume of the mesh.
    _non_printing_mesh_settings = {
        "anti_overhang_mesh", "infill_mesh", "cutting_mesh"
    }
    _non_thumbnail_visible_settings = {
        "anti_overhang_mesh", "infill_mesh", "cutting_mesh", "support_mesh"
    }

    def __init__(self):
        super().__init__()
        self._stack = PerObjectContainerStack(
            container_id="per_object_stack_" + str(id(self)))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        user_container = InstanceContainer(
            container_id=self._generateUniqueName())
        user_container.addMetaDataEntry("type", "user")
        self._stack.userChanges = user_container
        self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(
            0)

        self._is_non_printing_mesh = False
        self._is_non_thumbnail_visible_mesh = False

        self._stack.propertyChanged.connect(self._onSettingChanged)

        Application.getInstance().getContainerRegistry().addContainer(
            self._stack)

        Application.getInstance().globalContainerStackChanged.connect(
            self._onNumberOfExtrudersEnabledChanged)
        Application.getInstance().getMachineManager(
        ).numberExtrudersEnabledChanged.connect(
            self._onNumberOfExtrudersEnabledChanged)

        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()

    def _generateUniqueName(self):
        return "SettingOverrideInstanceContainer-%s" % uuid.uuid1()

    def _onNumberOfExtrudersEnabledChanged(self, *args, **kwargs):
        if not parseBool(
                self._extruder_stack.getMetaDataEntry("enabled", "True")):
            # switch to the first extruder that's available
            global_stack = Application.getInstance().getMachineManager(
            ).activeMachine
            if global_stack is None:
                return
            for _, extruder in sorted(list(global_stack.extruders.items())):
                if parseBool(extruder.getMetaDataEntry("enabled", "True")):
                    self._extruder_stack = extruder
                    self._updateNextStack()
                    break

    def __deepcopy__(self, memo):
        ## Create a fresh decorator object
        deep_copy = SettingOverrideDecorator()

        ## Copy the instance
        instance_container = copy.deepcopy(self._stack.getContainer(0), memo)

        # A unique name must be added, or replaceContainer will not replace it
        instance_container.setMetaDataEntry("id", self._generateUniqueName())

        ## Set the copied instance as the first (and only) instance container of the stack.
        deep_copy._stack.replaceContainer(0, instance_container)

        # Properly set the right extruder on the copy
        if self._extruder_stack is None:
            deep_copy._extruder_stack = None
        else:
            deep_copy.setActiveExtruder(self._extruder_stack.getId())

        # use value from the stack because there can be a delay in signal triggering and "_is_non_printing_mesh"
        # has not been updated yet.
        deep_copy._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
        deep_copy._is_non_thumbnail_visible_mesh = self.evaluateIsNonThumbnailVisibleMesh(
        )

        return deep_copy

    ##  Gets the currently active extruder to print this object with.
    #
    #   \return An extruder's container stack.
    def getActiveExtruder(self):
        return None if self._extruder_stack is None else self._extruder_stack.getId(
        )

    ##  Gets the signal that emits if the active extruder changed.
    #
    #   This can then be accessed via a decorator.
    def getActiveExtruderChangedSignal(self):
        return self.activeExtruderChanged

    ##  Gets the currently active extruders position
    #
    #   \return An extruder's position, or None if no position info is available.
    def getActiveExtruderPosition(self):
        containers = ContainerRegistry.getInstance().findContainers(
            id=self.getActiveExtruder())
        if containers:
            container_stack = containers[0]
            return container_stack.getMetaDataEntry("position", default=None)

    def isNonPrintingMesh(self):
        return self._is_non_printing_mesh

    def evaluateIsNonPrintingMesh(self):
        return any(
            bool(self._stack.getProperty(setting, "value"))
            for setting in self._non_printing_mesh_settings)

    def isNonThumbnailVisibleMesh(self):
        return self._is_non_thumbnail_visible_mesh

    def evaluateIsNonThumbnailVisibleMesh(self):
        return any(
            bool(self._stack.getProperty(setting, "value"))
            for setting in self._non_thumbnail_visible_settings)

    def _onSettingChanged(
            self, instance,
            property_name):  # Reminder: 'property' is a built-in function
        if property_name == "value":
            # Trigger slice/need slicing if the value has changed.
            self._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
            self._is_non_thumbnail_visible_mesh = self.evaluateIsNonThumbnailVisibleMesh(
            )

            Application.getInstance().getBackend().needsSlicing()
            Application.getInstance().getBackend().tickle()

    ##  Makes sure that the stack upon which the container stack is placed is
    #   kept up to date.
    def _updateNextStack(self):
        if self._extruder_stack:
            if self._stack.getNextStack():
                old_extruder_stack_id = self._stack.getNextStack().getId()
            else:
                old_extruder_stack_id = ""

            self._stack.setNextStack(self._extruder_stack)
            # Trigger slice/need slicing if the extruder changed.
            if self._stack.getNextStack().getId() != old_extruder_stack_id:
                Application.getInstance().getBackend().needsSlicing()
                Application.getInstance().getBackend().tickle()
        else:
            global_stack = Application.getInstance().getGlobalContainerStack()
            if global_stack:
                self._stack.setNextStack(cast(CuraContainerStack,
                                              global_stack))

    ##  Changes the extruder with which to print this node.
    #
    #   \param extruder_stack_id The new extruder stack to print with.
    def setActiveExtruder(self, extruder_stack_id: str) -> None:
        if self._extruder_stack is None or self._extruder_stack.getId(
        ) == extruder_stack_id:
            return

        global_stack = Application.getInstance().getMachineManager(
        ).activeMachine
        if global_stack is None:
            return

        for extruder in global_stack.extruders.values():
            if extruder.getId() == extruder_stack_id:
                self._extruder_stack = extruder
                break
        self._updateNextStack()
        ExtruderManager.getInstance().resetSelectedObjectExtruders()
        self.activeExtruderChanged.emit()

    def getStack(self):
        return self._stack