예제 #1
0
    def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
        """Creates a dictionary of tokens to replace in g-code pieces.

        This indicates what should be replaced in the start and end g-codes.
        :param stack: The stack to get the settings from to replace the tokens with.
        :return: A dictionary of replacement tokens to the values they should be replaced with.
        """

        result = {}
        for key in stack.getAllKeys():
            result[key] = stack.getProperty(key, "value")
            Job.yieldThread()

        # Material identification in addition to non-human-readable GUID
        result["material_id"] = stack.material.getMetaDataEntry(
            "base_file", "")
        result["material_type"] = stack.material.getMetaDataEntry(
            "material", "")
        result["material_name"] = stack.material.getMetaDataEntry("name", "")
        result["material_brand"] = stack.material.getMetaDataEntry("brand", "")

        # Renamed settings.
        result["print_bed_temperature"] = result["material_bed_temperature"]
        result["print_temperature"] = result["material_print_temperature"]
        result["travel_speed"] = result["speed_travel"]

        #Some extra settings.
        result["time"] = time.strftime("%H:%M:%S")
        result["date"] = time.strftime("%d-%m-%Y")
        result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
                         "Sat"][int(time.strftime("%w"))]
        result["initial_extruder_nr"] = CuraApplication.getInstance(
        ).getExtruderManager().getInitialExtruderNr()

        return result
예제 #2
0
    def addExtruder(self, extruder: ContainerStack) -> None:
        extruder_count = self.getProperty("machine_extruder_count", "value")
        if extruder_count and len(self._extruders) + 1 > extruder_count:
            Logger.log(
                "w",
                "Adding extruder {meta} to {id} but its extruder count is {count}"
                .format(id=self.id,
                        count=extruder_count,
                        meta=str(extruder.getMetaData())))
            return

        position = extruder.getMetaDataEntry("position")
        if position is None:
            Logger.log(
                "w",
                "No position defined for extruder {extruder}, cannot add it to stack {stack}",
                extruder=extruder.id,
                stack=self.id)
            return

        if any(item.getId() == extruder.id
               for item in self._extruders.values()):
            Logger.log(
                "w", "Extruder [%s] has already been added to this stack [%s]",
                extruder.id, self._id)
            return
        self._extruders[position] = extruder
예제 #3
0
    def _settingIsOverwritingInheritance(self, key: str, stack: ContainerStack = None) -> bool:
        """Check if a setting has an inheritance function that is overwritten"""

        has_setting_function = False
        if not stack:
            stack = self._active_container_stack
        if not stack:  # No active container stack yet!
            return False

        if self._active_container_stack is None:
            return False
        all_keys = self._active_container_stack.getAllKeys()

        containers = []  # type: List[ContainerInterface]

        has_user_state = stack.getProperty(key, "state") == InstanceState.User
        """Check if the setting has a user state. If not, it is never overwritten."""

        if not has_user_state:
            return False

        # If a setting is not enabled, don't label it as overwritten (It's never visible anyway).
        if not stack.getProperty(key, "enabled"):
            return False

        user_container = stack.getTop()
        """Also check if the top container is not a setting function (this happens if the inheritance is restored)."""

        if user_container and isinstance(user_container.getProperty(key, "value"), SettingFunction):
            return False

        ##  Mash all containers for all the stacks together.
        while stack:
            containers.extend(stack.getContainers())
            stack = stack.getNextStack()
        has_non_function_value = False
        for container in containers:
            try:
                value = container.getProperty(key, "value")
            except AttributeError:
                continue
            if value is not None:
                # If a setting doesn't use any keys, it won't change it's value, so treat it as if it's a fixed value
                has_setting_function = isinstance(value, SettingFunction)
                if has_setting_function:
                    for setting_key in value.getUsedSettingKeys():
                        if setting_key in all_keys:
                            break  # We found an actual setting. So has_setting_function can remain true
                    else:
                        # All of the setting_keys turned out to not be setting keys at all!
                        # This can happen due enum keys also being marked as settings.
                        has_setting_function = False

                if has_setting_function is False:
                    has_non_function_value = True
                    continue

            if has_setting_function:
                break  # There is a setting function somewhere, stop looking deeper.
        return has_setting_function and has_non_function_value
예제 #4
0
    def setNextStack(self, stack: ContainerStack) -> None:
        super().setNextStack(stack)
        stack.addExtruder(self)
        self.addMetaDataEntry("machine", stack.id)

        # 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.
        if self.getMetaDataEntry("position") == "0":
            for key in _EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS:
                setting_value = stack.definitionChanges.getProperty(
                    key, "value")
                if setting_value is None:
                    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)

                stack.definitionChanges.removeInstance(key, postpone_emit=True)
예제 #5
0
    def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
        result = {}
        for key in stack.getAllKeys():
            value = stack.getProperty(key, "value")
            result[key] = value
            Job.yieldThread()

        result["print_bed_temperature"] = result[
            "material_bed_temperature"]  # Renamed settings.
        result["print_bed_temperature_layer_0"] = result[
            "material_bed_temperature_layer_0"]  # Renamed settings.

        result["print_temperature"] = result["material_print_temperature"]
        result["print_chamber_temperature"] = result[
            "default_material_chamber_temperature"]
        result["travel_speed"] = result["speed_travel"]
        result["time"] = time.strftime("%H:%M:%S")  #Some extra settings.
        result["date"] = time.strftime("%d-%m-%Y")
        result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
                         "Sat"][int(time.strftime("%w"))]

        if (result["support_solubile"] == True):
            print("SUPPORT TRUE!!!!")
            result["support_bottom_distance"] = "0.0"
            result["support_top_distance"] = "0.0"

        initial_extruder_stack = CuraApplication.getInstance(
        ).getExtruderManager().getUsedExtruderStacks()[0]
        initial_extruder_nr = initial_extruder_stack.getProperty(
            "extruder_nr", "value")
        result["initial_extruder_nr"] = initial_extruder_nr

        return result
예제 #6
0
    def setNextStack(self, stack: ContainerStack) -> None:
        super().setNextStack(stack)
        stack.addExtruder(self)
        self.addMetaDataEntry("machine", stack.id)

        # For backward compatibility: Register the extruder with the Extruder Manager
        ExtruderManager.getInstance().registerExtruder(self, stack.id)
예제 #7
0
    def __init__(self):
        super().__init__()
        self._settings = None
        self._stack = None

        setting_data = self.getSettingData()
        self._stack = ContainerStack(stack_id = str(id(self)))
        self._stack.setDirty(False)  # This stack does not need to be saved.


        ## Check if the definition of this script already exists. If not, add it to the registry.
        if "key" in setting_data:
            definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = setting_data["key"])
            if definitions:
                # Definition was found
                self._definition = definitions[0]
            else:
                self._definition = DefinitionContainer(setting_data["key"])
                self._definition.deserialize(json.dumps(setting_data))
                ContainerRegistry.getInstance().addContainer(self._definition)
        self._stack.addContainer(self._definition)
        self._instance = InstanceContainer(container_id="ScriptInstanceContainer")
        self._instance.setDefinition(self._definition.getId())
        self._instance.addMetaDataEntry("setting_version", self._definition.getMetaDataEntry("setting_version", default = 0))
        self._stack.addContainer(self._instance)
        self._stack.propertyChanged.connect(self._onPropertyChanged)

        ContainerRegistry.getInstance().addContainer(self._stack)
예제 #8
0
    def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
        result = {}
        for key in stack.getAllKeys():
            value = stack.getProperty(key, "value")
            result[key] = value
            Job.yieldThread()

        result["print_bed_temperature"] = result[
            "material_bed_temperature"]  # Renamed settings.
        result["print_temperature"] = result["material_print_temperature"]
        result["time"] = time.strftime("%H:%M:%S")  # Some extra settings.
        result["date"] = time.strftime("%d-%m-%Y")
        result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
                         "Sat"][int(time.strftime("%w"))]
        printing_mode = result["printing_mode"]
        if printing_mode in ["cylindrical_full", "cylindrical"]:
            result["cylindrical_rotate"] = "G0 A%.2f" % (
                90 * result["machine_a_axis_multiplier"] /
                result["machine_a_axis_divider"])
            result["coordinate_system"] = "G56"
        elif printing_mode in ["spherical_full", "spherical"]:
            result["cylindrical_rotate"] = "G0 A0"
            result["coordinate_system"] = "G55"

        initial_extruder_stack = SteSlicerApplication.getInstance(
        ).getExtruderManager().getUsedExtruderStacks()[0]
        initial_extruder_nr = initial_extruder_stack.getProperty(
            "extruder_nr", "value")
        result["initial_extruder_nr"] = initial_extruder_nr

        return result
예제 #9
0
    def setNextStack(self, stack: ContainerStack) -> None:
        super().setNextStack(stack)
        stack.addExtruder(self)
        self.addMetaDataEntry("machine", stack.id)

        # For backward compatibility: Register the extruder with the Extruder Manager
        ExtruderManager.getInstance().registerExtruder(self, stack.id)
예제 #10
0
    def _buildExtruderMessage(self, stack: ContainerStack) -> None:
        """Create extruder message from stack"""

        message = self._slice_message.addRepeatedMessage("extruders")
        message.id = int(stack.getMetaDataEntry("position"))
        if not self._all_extruders_settings:
            self._cacheAllExtruderSettings()

        if self._all_extruders_settings is None:
            return

        extruder_nr = stack.getProperty("extruder_nr", "value")
        settings = self._all_extruders_settings[str(extruder_nr)].copy()

        # Also send the material GUID. This is a setting in fdmprinter, but we have no interface for it.
        settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")

        # Replace the setting tokens in start and end g-code.
        extruder_nr = stack.getProperty("extruder_nr", "value")
        settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], extruder_nr)
        settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], extruder_nr)

        global_definition = cast(ContainerInterface, cast(ContainerStack, stack.getNextStack()).getBottom())
        own_definition = cast(ContainerInterface, stack.getBottom())

        for key, value in settings.items():
            # Do not send settings that are not settable_per_extruder.
            # Since these can only be set in definition files, we only have to ask there.
            if not global_definition.getProperty(key, "settable_per_extruder") and \
                    not own_definition.getProperty(key, "settable_per_extruder"):
                    continue
            setting = message.getMessage("settings").addRepeatedMessage("settings")
            setting.name = key
            setting.value = str(value).encode("utf-8")
            Job.yieldThread()
예제 #11
0
    def _buildExtruderMessage(self, stack: ContainerStack) -> None:
        message = self._slice_message.addRepeatedMessage("extruders")
        message.id = int(stack.getMetaDataEntry("position"))
        if not self._all_extruders_settings:
            self._cacheAllExtruderSettings()

        if self._all_extruders_settings is None:
            return

        extruder_nr = stack.getProperty("extruder_nr", "value")
        settings = self._all_extruders_settings[str(extruder_nr)].copy()

        # Also send the material GUID. This is a setting in fdmprinter, but we have no interface for it.
        settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")

        # Replace the setting tokens in start and end g-code.
        extruder_nr = stack.getProperty("extruder_nr", "value")
        settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], extruder_nr)
        settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], extruder_nr)

        for key, value in settings.items():
            # Do not send settings that are not settable_per_extruder.
            if not stack.getProperty(key, "settable_per_extruder"):
                continue
            setting = message.getMessage("settings").addRepeatedMessage("settings")
            setting.name = key
            setting.value = str(value).encode("utf-8")
            Job.yieldThread()
예제 #12
0
    def _checkStackForErrors(self, stack: ContainerStack) -> bool:

        top_of_stack = cast(InstanceContainer, stack.getTop())  # Cache for efficiency.
        changed_setting_keys = top_of_stack.getAllKeys()

        # Add all relations to changed settings as well.
        for key in top_of_stack.getAllKeys():
            instance = top_of_stack.getInstance(key)
            if instance is None:
                continue
            self._addRelations(changed_setting_keys, instance.definition.relations)
            Job.yieldThread()

        for changed_setting_key in changed_setting_keys:
            validation_state = stack.getProperty(changed_setting_key, "validationState")

            if validation_state is None:
                definition = cast(SettingDefinition, stack.getSettingDefinition(changed_setting_key))
                validator_type = SettingDefinition.getValidatorForType(definition.type)
                if validator_type:
                    validator = validator_type(changed_setting_key)
                    validation_state = validator(stack)
            if validation_state in (
            ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
                Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", changed_setting_key, validation_state)
                return True
            Job.yieldThread()

        return False
예제 #13
0
    def _buildExtruderMessage(self, stack: ContainerStack) -> None:
        message = self._arcus_message.addRepeatedMessage("extruders")
        message.id = int(stack.getMetaDataEntry("position"))

        settings = self._buildReplacementTokens(stack)

        # Also send the material GUID. This is a setting in fdmprinter, but we have no interface for it.
        settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")

        # Replace the setting tokens in start and end g-code.
        extruder_nr = stack.getProperty("extruder_nr", "value")
        settings["machine_extruder_start_code"] = self._expandGcodeTokens(
            settings["machine_extruder_start_code"], extruder_nr)
        settings["machine_extruder_end_code"] = self._expandGcodeTokens(
            settings["machine_extruder_end_code"], extruder_nr)
        settings["machine_fiber_cut_code"] = self._expandGcodeTokens(
            settings["machine_fiber_cut_code"], extruder_nr)
        settings["machine_fiber_prime_code"] = self._expandGcodeTokens(
            settings["machine_fiber_prime_code"], extruder_nr)

        for key, value in settings.items():
            # Do not send settings that are not settable_per_extruder.
            if not stack.getProperty(key, "settable_per_extruder"):
                continue
            setting = message.getMessage("settings").addRepeatedMessage(
                "settings")
            setting.name = key
            setting.value = str(value).encode("utf-8")
            Job.yieldThread()
예제 #14
0
 def _buildGlobalInheritsStackMessage(self, stack: ContainerStack) -> None:
     for key in stack.getAllKeys():
         extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
         if extruder_position >= 0:  # Set to a specific extruder.
             setting_extruder = self._slice_message.addRepeatedMessage("limit_to_extruder")
             setting_extruder.name = key
             setting_extruder.extruder = extruder_position
         Job.yieldThread()
예제 #15
0
 def _buildGlobalInheritsStackMessage(self, stack: ContainerStack) -> None:
     for key in stack.getAllKeys():
         extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
         if extruder_position >= 0:  # Set to a specific extruder.
             setting_extruder = self._slice_message.addRepeatedMessage("limit_to_extruder")
             setting_extruder.name = key
             setting_extruder.extruder = extruder_position
         Job.yieldThread()
    def __init__(self, parent: QObject = None) -> None:
        super().__init__(parent)

        self._container_ids = []

        self._stack = ContainerStack("CustomStack" + str(id(self)))
        self._stack_id = self._stack.id
        self._stack.setDirty(False) # never save this stack

        Application.getInstance().getContainerRegistry().addContainer(self._stack)
예제 #17
0
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(id="SettingOverrideStack")
        self._instance = InstanceContainer(
            id="SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

        Application.getInstance().globalContainerStackChanged.connect(
            self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()
예제 #18
0
    def setNextStack(self, stack: ContainerStack) -> None:
        super().setNextStack(stack)
        stack.addExtruder(self)
        self.addMetaDataEntry("machine", stack.id)

        # 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:
            # Since material_diameter is not on the extruder definition, we need to add it here
            # WARNING: this might be very dangerous and should be refactored ASAP!
            definition = stack.getSettingDefinition(key)
            if definition:
                self.definition.addDefinition(definition)

            # Only copy the value when this extruder doesn't have the value.
            if self.definitionChanges.hasProperty(key, "value"):
                continue

            setting_value = stack.definitionChanges.getProperty(key, "value")
            if setting_value is None:
                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":
                position = self.getMetaDataEntry("position", "0")
                Application.getInstance().getExtruderManager(
                ).updateMaterialForDiameter(position)
예제 #19
0
    def _checkStackForErrors(self, stack: ContainerStack) -> bool:
        if stack is None:
            return False

        for key in stack.getAllKeys():
            validation_state = stack.getProperty(key, "validationState")
            if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
                Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", key, validation_state)
                return True
            Job.yieldThread()
        return False
예제 #20
0
    def _checkStackForErrors(self, stack: ContainerStack) -> bool:
        if stack is None:
            return False

        for key in stack.getAllKeys():
            validation_state = stack.getProperty(key, "validationState")
            if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
                Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", key, validation_state)
                return True
            Job.yieldThread()
        return False
예제 #21
0
def test_serialize_with_ignored_metadata_keys(container_stack):
    ignored_metadata_keys = {"secret"}
    registry = ContainerRegistry.getInstance(
    )  # All containers need to be registered in order to be recovered again after deserialising.

    # Case with one subcontainer.
    container = InstanceContainer(str(uuid.uuid4()))
    registry.addContainer(container)
    container_stack.addContainer(container)

    # Case with two subcontainers.
    container = InstanceContainer(str(uuid.uuid4()))
    registry.addContainer(container)
    container_stack.addContainer(
        container)  # Already had one, if all previous assertions were correct.

    # Case with all types of subcontainers.
    container = DefinitionContainer(str(uuid.uuid4()))
    registry.addContainer(container)
    container_stack.addContainer(container)
    container = ContainerStack(str(uuid.uuid4()))
    registry.addContainer(container)
    container_stack.addContainer(container)

    # With some metadata.
    container_stack.getMetaData()["foo"] = "bar"
    for key in ignored_metadata_keys:
        container_stack.getMetaData()[key] = "something"
    _test_serialize_cycle(container_stack,
                          ignored_metadata_keys=ignored_metadata_keys)

    # With a changed name.
    container_stack.setName("Fred")
    _test_serialize_cycle(container_stack,
                          ignored_metadata_keys=ignored_metadata_keys)

    # A name with special characters, to test the encoding.
    container_stack.setName("ルベン")
    _test_serialize_cycle(container_stack,
                          ignored_metadata_keys=ignored_metadata_keys)

    # Just to bully the one who implements this, a name with special characters in JSON and CFG.
    container_stack.setName("=,\"")
    _test_serialize_cycle(container_stack,
                          ignored_metadata_keys=ignored_metadata_keys)

    # A container that is not in the registry.
    container_stack.addContainer(DefinitionContainer(str(uuid.uuid4())))
    serialised = container_stack.serialize()
    container_stack = ContainerStack(str(
        uuid.uuid4()))  # Completely fresh container stack.
    with pytest.raises(Exception):
        container_stack.deserialize(serialised)
예제 #22
0
def test_findContainerStacks(container_registry, data):
    for container in data["containers"]: # Fill the registry with container stacks.
        container = container.copy()
        container_id = container["id"]
        del container["id"]
        container_stack = ContainerStack(container_id)
        for key, value in container.items(): # Copy data into metadata.
            container_stack.getMetaData()[key] = value
        container_registry.addContainer(container_stack)

    results = container_registry.findContainerStacks(**data["filter"]) # The actual function call we're testing.

    _verifyMetaDataMatches(results, data["result"])
예제 #23
0
def test_findContainerStacks(container_registry, data):
    for container in data["containers"]: # Fill the registry with container stacks.
        container = container.copy()
        container_id = container["id"]
        del container["id"]
        container_stack = ContainerStack(container_id)
        for key, value in container.items(): # Copy data into metadata.
            container_stack.getMetaData()[key] = value
        container_registry.addContainer(container_stack)

    results = container_registry.findContainerStacks(**data["filter"]) # The actual function call we're testing.

    _verifyMetaDataMatches(results, data["result"])
예제 #24
0
    def _settingIsOverwritingInheritance(self, key: str, stack: ContainerStack = None) -> bool:
        has_setting_function = False
        if not stack:
            stack = self._active_container_stack
        if not stack: #No active container stack yet!
            return False
        containers = []

        ## Check if the setting has a user state. If not, it is never overwritten.
        has_user_state = stack.getProperty(key, "state") == InstanceState.User
        if not has_user_state:
            return False

        ## If a setting is not enabled, don't label it as overwritten (It's never visible anyway).
        if not stack.getProperty(key, "enabled"):
            return False

        ## Also check if the top container is not a setting function (this happens if the inheritance is restored).
        if isinstance(stack.getTop().getProperty(key, "value"), SettingFunction):
            return False

        ##  Mash all containers for all the stacks together.
        while stack:
            containers.extend(stack.getContainers())
            stack = stack.getNextStack()
        has_non_function_value = False
        for container in containers:
            try:
                value = container.getProperty(key, "value")
            except AttributeError:
                continue
            if value is not None:
                # If a setting doesn't use any keys, it won't change it's value, so treat it as if it's a fixed value
                has_setting_function = isinstance(value, SettingFunction)
                if has_setting_function:
                    for setting_key in value.getUsedSettingKeys():
                        if setting_key in self._active_container_stack.getAllKeys():
                            break # We found an actual setting. So has_setting_function can remain true
                    else:
                        # All of the setting_keys turned out to not be setting keys at all!
                        # This can happen due enum keys also being marked as settings.
                        has_setting_function = False

                if has_setting_function is False:
                    has_non_function_value = True
                    continue

            if has_setting_function:
                break  # There is a setting function somewhere, stop looking deeper.
        return has_setting_function and has_non_function_value
예제 #25
0
def test_serialize(container_stack):
    registry = ContainerRegistry.getInstance()  # All containers need to be registered in order to be recovered again after deserialising.

    # First test the empty container stack.
    _test_serialize_cycle(container_stack)

    # Case with one subcontainer.
    container = InstanceContainer(str(uuid.uuid4()))
    registry.addContainer(container)
    container_stack.addContainer(container)
    _test_serialize_cycle(container_stack)

    # Case with two subcontainers.
    container = InstanceContainer(str(uuid.uuid4()))
    registry.addContainer(container)
    container_stack.addContainer(container)  # Already had one, if all previous assertions were correct.
    _test_serialize_cycle(container_stack)

    # Case with all types of subcontainers.
    container = DefinitionContainer(str(uuid.uuid4()))
    registry.addContainer(container)
    container_stack.addContainer(container)
    container = ContainerStack(str(uuid.uuid4()))
    registry.addContainer(container)
    container_stack.addContainer(container)
    _test_serialize_cycle(container_stack)

    # With some metadata.
    container_stack.getMetaData()["foo"] = "bar"
    _test_serialize_cycle(container_stack)

    # With a changed name.
    container_stack.setName("Fred")
    _test_serialize_cycle(container_stack)

    # A name with special characters, to test the encoding.
    container_stack.setName("ルベン")
    _test_serialize_cycle(container_stack)

    # Just to bully the one who implements this, a name with special characters in JSON and CFG.
    container_stack.setName("=,\"")
    _test_serialize_cycle(container_stack)

    # A container that is not in the registry.
    container_stack.addContainer(DefinitionContainer(str(uuid.uuid4())))
    serialised = container_stack.serialize()
    container_stack = ContainerStack(str(uuid.uuid4())) # Completely fresh container stack.
    with pytest.raises(Exception):
        container_stack.deserialize(serialised)
예제 #26
0
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(stack_id=id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        self._instance = InstanceContainer(
            container_id="SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

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

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

        Application.getInstance().globalContainerStackChanged.connect(
            self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()
예제 #27
0
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(stack_id = id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)
        self._extruder_stack = None #Stack upon which our stack is based.

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

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

        Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()
예제 #28
0
    def addExtruder(self, extruder: ContainerStack) -> None:
        extruder_count = self.getProperty("machine_extruder_count", "value")

        if extruder_count <= 1:
            Logger.log(
                "i",
                "Not adding extruder[%s] to [%s] because it is a single-extrusion machine.",
                extruder.id, self.id)
            return

        position = extruder.getMetaDataEntry("position")
        if position is None:
            Logger.log(
                "w",
                "No position defined for extruder {extruder}, cannot add it to stack {stack}",
                extruder=extruder.id,
                stack=self.id)
            return

        if any(item.getId() == extruder.id
               for item in self._extruders.values()):
            Logger.log(
                "w", "Extruder [%s] has already been added to this stack [%s]",
                extruder.id, self._id)
            return

        self._extruders[position] = extruder
        Logger.log("i", "Extruder[%s] added to [%s] at position [%s]",
                   extruder.id, self.id, position)
예제 #29
0
    def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
        result = {}
        for key in stack.getAllKeys():
            value = stack.getProperty(key, "value")
            result[key] = value
            Job.yieldThread()

        result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings.
        result["print_temperature"] = result["material_print_temperature"]
        result["travel_speed"] = result["speed_travel"]
        result["time"] = time.strftime("%H:%M:%S") #Some extra settings.
        result["date"] = time.strftime("%d-%m-%Y")
        result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))]
        result["initial_extruder_nr"] = CuraApplication.getInstance().getExtruderManager().getInitialExtruderNr()

        return result
예제 #30
0
class SettingOverrideDecorator(SceneNodeDecorator):
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(id = "SettingOverrideStack")
        self._instance = InstanceContainer(id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()

    def _onGlobalContainerStackChanged(self):
        ## Ensure that the next stack is always the global stack.
        self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())

    def getStack(self):
        return self._stack
예제 #31
0
    def __init__(self):
        super().__init__()
        self._settings = None
        self._stack = None

        setting_data = self.getSettingData()
        self._stack = ContainerStack(stack_id=id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.


        ## Check if the definition of this script already exists. If not, add it to the registry.
        if "key" in setting_data:
            definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = setting_data["key"])
            if definitions:
                # Definition was found
                self._definition = definitions[0]
            else:
                self._definition = DefinitionContainer(setting_data["key"])
                self._definition.deserialize(json.dumps(setting_data))
                ContainerRegistry.getInstance().addContainer(self._definition)
        self._stack.addContainer(self._definition)
        self._instance = InstanceContainer(container_id="ScriptInstanceContainer")
        self._instance.setDefinition(self._definition)
        self._stack.addContainer(self._instance)

        ContainerRegistry.getInstance().addContainer(self._stack)
예제 #32
0
    def addExtruder(self, extruder: ContainerStack) -> None:
        """Add an extruder to the list of extruders of this stack.

        :param extruder: The extruder to add.

        :raise Exceptions.TooManyExtrudersError: Raised when trying to add an extruder while we
            already have the maximum number of extruders.
        """

        position = extruder.getMetaDataEntry("position")
        if position is None:
            Logger.log(
                "w",
                "No position defined for extruder {extruder}, cannot add it to stack {stack}",
                extruder=extruder.id,
                stack=self.id)
            return

        if any(item.getId() == extruder.id
               for item in self._extruders.values()):
            Logger.log(
                "w", "Extruder [%s] has already been added to this stack [%s]",
                extruder.id, self.getId())
            return

        self._extruders[position] = extruder
        self.extrudersChanged.emit()
        Logger.log("i", "Extruder[%s] added to [%s] at position [%s]",
                   extruder.id, self.id, position)
예제 #33
0
    def _checkStackForErrors(self, stack: ContainerStack) -> bool:
        if stack is None:
            return False

        # if there are no per-object settings we don't need to check the other settings here
        stack_top = stack.getTop()
        if stack_top is None or not stack_top.getAllKeys():
            return False

        for key in stack.getAllKeys():
            validation_state = stack.getProperty(key, "validationState")
            if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid):
                Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", key, validation_state)
                return True
            Job.yieldThread()
        return False
예제 #34
0
파일: Script.py 프로젝트: TinkerGnome/Cura
    def initialize(self) -> None:
        setting_data = self.getSettingData()
        self._stack = ContainerStack(stack_id=str(id(self)))
        self._stack.setDirty(False)  # This stack does not need to be saved.

        ## Check if the definition of this script already exists. If not, add it to the registry.
        if "key" in setting_data:
            definitions = ContainerRegistry.getInstance().findDefinitionContainers(id=setting_data["key"])
            if definitions:
                # Definition was found
                self._definition = definitions[0]
            else:
                self._definition = DefinitionContainer(setting_data["key"])
                try:
                    self._definition.deserialize(json.dumps(setting_data))
                    ContainerRegistry.getInstance().addContainer(self._definition)
                except ContainerFormatError:
                    self._definition = None
                    return
        if self._definition is None:
            return
        self._stack.addContainer(self._definition)
        self._instance = InstanceContainer(container_id="ScriptInstanceContainer")
        self._instance.setDefinition(self._definition.getId())
        self._instance.setMetaDataEntry("setting_version",
                                        self._definition.getMetaDataEntry("setting_version", default=0))
        self._stack.addContainer(self._instance)
        self._stack.propertyChanged.connect(self._onPropertyChanged)

        ContainerRegistry.getInstance().addContainer(self._stack)
예제 #35
0
    def _buildGlobalInheritsStackMessage(self, stack: ContainerStack) -> None:
        """Sends for some settings which extruder they should fallback to if not set.

        This is only set for settings that have the limit_to_extruder
        property.

        :param stack: The global stack with all settings, from which to read the
            limit_to_extruder property.
        """

        for key in stack.getAllKeys():
            extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
            if extruder_position >= 0:  # Set to a specific extruder.
                setting_extruder = self._slice_message.addRepeatedMessage("limit_to_extruder")
                setting_extruder.name = key
                setting_extruder.extruder = extruder_position
            Job.yieldThread()
예제 #36
0
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(id = "SettingOverrideStack")
        self._instance = InstanceContainer(id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()
예제 #37
0
def _test_serialize_cycle(container_stack, ignored_metadata_keys: Optional[set] = None):
    metadata = {key: value for key, value in container_stack.getMetaData().items()}
    containers = container_stack.getContainers()

    serialised = container_stack.serialize(ignored_metadata_keys = ignored_metadata_keys)
    container_stack = ContainerStack(str(uuid.uuid4()))  # Completely fresh container stack.
    container_stack.deserialize(serialised)

    # Remove ignored keys from metadata dict
    if ignored_metadata_keys:
        for key in ignored_metadata_keys:
            if key in metadata:
                del metadata[key]

    # ID and nextStack are allowed to be different.
    assert metadata.items() <= container_stack.getMetaData().items()
    assert containers == container_stack.getContainers()
예제 #38
0
    def _convertContainerStack(
        self, container: ContainerStack
    ) -> Union[ExtruderStack.ExtruderStack, GlobalStack.GlobalStack]:
        assert type(container) == ContainerStack

        container_type = container.getMetaDataEntry("type")
        if container_type not in ("extruder_train", "machine"):
            # It is not an extruder or machine, so do nothing with the stack
            return container

        Logger.log("d",
                   "Converting ContainerStack {stack} to {type}",
                   stack=container.getId(),
                   type=container_type)

        if container_type == "extruder_train":
            new_stack = ExtruderStack.ExtruderStack(container.getId())
        else:
            new_stack = GlobalStack.GlobalStack(container.getId())

        container_contents = container.serialize()
        new_stack.deserialize(container_contents)

        # Delete the old configuration file so we do not get double stacks
        if os.path.isfile(container.getPath()):
            os.remove(container.getPath())

        return new_stack
예제 #39
0
def test_deserializeInvalidMetadata():
    # No version
    serialised = """
            [general]
            name = Test
            id = testid
            """
    with pytest.raises(InvalidContainerStackError):
        ContainerStack.deserializeMetadata(serialised, "testid")

    # No name
    serialised = """
           [general]
           id = testid
           version = {version}
           """.format(version=ContainerStack.Version)
    with pytest.raises(InvalidContainerStackError):
        ContainerStack.deserializeMetadata(serialised, "testid")
예제 #40
0
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(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()
예제 #41
0
def test_deserializeInvalidMetadata():
    # No version
    serialised = """
            [general]
            name = Test
            id = testid
            """
    with pytest.raises(InvalidContainerStackError):
        ContainerStack.deserializeMetadata(serialised, "testid")

    # No name
    serialised = """
           [general]
           id = testid
           version = {version}
           """.format(version=ContainerStack.Version)
    with pytest.raises(InvalidContainerStackError):
        ContainerStack.deserializeMetadata(serialised, "testid")
예제 #42
0
class SettingOverrideDecorator(SceneNodeDecorator):
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(id="SettingOverrideStack")
        self._instance = InstanceContainer(
            id="SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

        Application.getInstance().globalContainerStackChanged.connect(
            self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()

    def _onGlobalContainerStackChanged(self):
        ## Ensure that the next stack is always the global stack.
        self._stack.setNextStack(
            Application.getInstance().getGlobalContainerStack())

    def getStack(self):
        return self._stack
예제 #43
0
    def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
        result = {}
        for key in stack.getAllKeys():
            value = stack.getProperty(key, "value")
            result[key] = value
            Job.yieldThread()

        result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings.
        result["print_temperature"] = result["material_print_temperature"]
        result["travel_speed"] = result["speed_travel"]
        result["time"] = time.strftime("%H:%M:%S") #Some extra settings.
        result["date"] = time.strftime("%d-%m-%Y")
        result["day"] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][int(time.strftime("%w"))]

        initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
        initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value")
        result["initial_extruder_nr"] = initial_extruder_nr

        return result
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(stack_id = id(self))
        self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

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

        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()
예제 #45
0
	def load_definitions(self):
		"""
		Load all the setting definitions into a custom container stack.

		This container stack also contains extra entries for the articles that
		are not settings.

		The result is stored in self._container_stack.
		"""
		if self._container_stack:
			return  # Already done earlier. Don't re-load.
		with open(os.path.join(os.path.dirname(__file__), "resources", "settings_guide_definitions.def.json")) as f:
			definitions_serialised = f.read()
		definition_container = DefinitionContainer("settings_guide_definitions")
		definition_container.deserialize(definitions_serialised)
		ContainerRegistry.getInstance().addContainer(definition_container)
		self._container_stack = ContainerStack("settings_guide_stack")
		self._container_stack.addContainer(definition_container)
		ContainerRegistry.getInstance().addContainer(self._container_stack)
예제 #46
0
파일: GlobalStack.py 프로젝트: olemis/Cura
    def addExtruder(self, extruder: ContainerStack) -> None:
        position = extruder.getMetaDataEntry("position")
        if position is None:
            Logger.log("w", "No position defined for extruder {extruder}, cannot add it to stack {stack}", extruder = extruder.id, stack = self.id)
            return

        if any(item.getId() == extruder.id for item in self._extruders.values()):
            Logger.log("w", "Extruder [%s] has already been added to this stack [%s]", extruder.id, self.getId())
            return

        self._extruders[position] = extruder
        Logger.log("i", "Extruder[%s] added to [%s] at position [%s]", extruder.id, self.id, position)
예제 #47
0
class SettingOverrideDecorator(SceneNodeDecorator):
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(stack_id = id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

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

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

        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()

    def __deepcopy__(self, memo):
        ## Create a fresh decorator object
        deep_copy = SettingOverrideDecorator()
        ## Copy the instance
        deep_copy._instance = copy.deepcopy(self._instance, memo)
        ## Set the copied instance as the first (and only) instance container of the stack.
        deep_copy._stack.replaceContainer(0, deep_copy._instance)
        return deep_copy

    def _onSettingChanged(self, instance, property):
        if property == "value":  # Only reslice if the value has changed.
            Application.getInstance().getBackend().forceSlice()

    def _onGlobalContainerStackChanged(self):
        ## Ensure that the next stack is always the global stack.
        self._stack.setNextStack(Application.getInstance().getGlobalContainerStack())

    def getStack(self):
        return self._stack
예제 #48
0
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(stack_id = id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

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

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

        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
        self._onGlobalContainerStackChanged()
예제 #49
0
def test_deserializeMetadata():
    serialised = """
        [general]
        name = Test
        id = testid
        version = {version}
        
        [metadata]
        foo = bar
        """.format(version=ContainerStack.Version)
    metadata = ContainerStack.deserializeMetadata(serialised, "testid")[0]
    assert metadata["name"] == "Test"
    assert metadata["id"] == "testid"
    assert metadata["version"] == str(ContainerStack.Version)
예제 #50
0
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(stack_id = id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)
        self._extruder_stack = None #Stack upon which our stack is based.

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

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

        Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()
예제 #51
0
    def addExtruder(self, extruder: ContainerStack) -> None:
        extruder_count = self.getProperty("machine_extruder_count", "value")
        if extruder_count and len(self._extruders) + 1 > extruder_count:
            Logger.log("w", "Adding extruder {meta} to {id} but its extruder count is {count}".format(id = self.id, count = extruder_count, meta = str(extruder.getMetaData())))
            return

        position = extruder.getMetaDataEntry("position")
        if position is None:
            Logger.log("w", "No position defined for extruder {extruder}, cannot add it to stack {stack}", extruder = extruder.id, stack = self.id)
            return

        if any(item.getId() == extruder.id for item in self._extruders.values()):
            Logger.log("w", "Extruder [%s] has already been added to this stack [%s]", extruder.id, self._id)
            return
        self._extruders[position] = extruder
예제 #52
0
def _test_serialize_cycle(container_stack):
    name = container_stack.getName()
    metadata = container_stack.getMetaData()
    containers = container_stack.getContainers()

    serialised = container_stack.serialize()
    container_stack = ContainerStack(uuid.uuid4().int) # Completely fresh container stack.
    container_stack.deserialize(serialised)

    #ID and nextStack are allowed to be different.
    assert name == container_stack.getName()
    assert metadata == container_stack.getMetaData()
    assert containers == container_stack.getContainers()
예제 #53
0
파일: GlobalStack.py 프로젝트: hroncok/Cura
    def addExtruder(self, extruder: ContainerStack) -> None:
        extruder_count = self.getProperty("machine_extruder_count", "value")

        if extruder_count <= 1:
            Logger.log("i", "Not adding extruder[%s] to [%s] because it is a single-extrusion machine.",
                       extruder.id, self.id)
            return

        position = extruder.getMetaDataEntry("position")
        if position is None:
            Logger.log("w", "No position defined for extruder {extruder}, cannot add it to stack {stack}", extruder = extruder.id, stack = self.id)
            return

        if any(item.getId() == extruder.id for item in self._extruders.values()):
            Logger.log("w", "Extruder [%s] has already been added to this stack [%s]", extruder.id, self._id)
            return

        self._extruders[position] = extruder
        Logger.log("i", "Extruder[%s] added to [%s] at position [%s]", extruder.id, self.id, position)
예제 #54
0
    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(stack_id = id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

        if cura.Settings.ExtruderManager.getInstance().extruderCount > 1:
            self._extruder_stack = cura.Settings.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()
예제 #55
0
    def read(self, file_name):
        # Load all the nodes / meshdata of the workspace
        nodes = self._3mf_mesh_reader.read(file_name)
        if nodes is None:
            nodes = []

        archive = zipfile.ZipFile(file_name, "r")

        cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]

        # Create a shadow copy of the preferences (we don't want all of the preferences, but we do want to re-use its
        # parsing code.
        temp_preferences = Preferences()
        temp_preferences.readFromFile(io.TextIOWrapper(archive.open("Cura/preferences.cfg")))  # We need to wrap it, else the archive parser breaks.

        # Copy a number of settings from the temp preferences to the global
        global_preferences = Preferences.getInstance()
        global_preferences.setValue("general/visible_settings", temp_preferences.getValue("general/visible_settings"))
        global_preferences.setValue("cura/categories_expanded", temp_preferences.getValue("cura/categories_expanded"))
        Application.getInstance().expandedCategoriesChanged.emit()  # Notify the GUI of the change

        self._id_mapping = {}

        # We don't add containers right away, but wait right until right before the stack serialization.
        # We do this so that if something goes wrong, it's easier to clean up.
        containers_to_add = []

        # TODO: For the moment we use pretty naive existence checking. If the ID is the same, we assume in quite a few
        # TODO: cases that the container loaded is the same (most notable in materials & definitions).
        # TODO: It might be possible that we need to add smarter checking in the future.
        Logger.log("d", "Workspace loading is checking definitions...")
        # Get all the definition files & check if they exist. If not, add them.
        definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
        for definition_container_file in definition_container_files:
            container_id = self._stripFileToId(definition_container_file)
            definitions = self._container_registry.findDefinitionContainers(id=container_id)
            if not definitions:
                definition_container = DefinitionContainer(container_id)
                definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8"))
                self._container_registry.addContainer(definition_container)

        Logger.log("d", "Workspace loading is checking materials...")
        material_containers = []
        # Get all the material files and check if they exist. If not, add them.
        xml_material_profile = self._getXmlProfileClass()
        if self._material_container_suffix is None:
            self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).suffixes[0]
        if xml_material_profile:
            material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
            for material_container_file in material_container_files:
                container_id = self._stripFileToId(material_container_file)
                materials = self._container_registry.findInstanceContainers(id=container_id)
                if not materials:
                    material_container = xml_material_profile(container_id)
                    material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
                    containers_to_add.append(material_container)
                else:
                    if not materials[0].isReadOnly():  # Only create new materials if they are not read only.
                        if self._resolve_strategies["material"] == "override":
                            materials[0].deserialize(archive.open(material_container_file).read().decode("utf-8"))
                        elif self._resolve_strategies["material"] == "new":
                            # Note that we *must* deserialize it with a new ID, as multiple containers will be
                            # auto created & added.
                            material_container = xml_material_profile(self.getNewId(container_id))
                            material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
                            containers_to_add.append(material_container)
                            material_containers.append(material_container)

        Logger.log("d", "Workspace loading is checking instance containers...")
        # Get quality_changes and user profiles saved in the workspace
        instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
        user_instance_containers = []
        quality_changes_instance_containers = []
        for instance_container_file in instance_container_files:
            container_id = self._stripFileToId(instance_container_file)
            instance_container = InstanceContainer(container_id)

            # Deserialize InstanceContainer by converting read data from bytes to string
            instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
            container_type = instance_container.getMetaDataEntry("type")
            if container_type == "user":
                # Check if quality changes already exists.
                user_containers = self._container_registry.findInstanceContainers(id=container_id)
                if not user_containers:
                    containers_to_add.append(instance_container)
                else:
                    if self._resolve_strategies["machine"] == "override":
                        user_containers[0].deserialize(archive.open(instance_container_file).read().decode("utf-8"))
                    elif self._resolve_strategies["machine"] == "new":
                        # The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
                        extruder_id = instance_container.getMetaDataEntry("extruder", None)
                        if extruder_id:
                            new_id = self.getNewId(extruder_id) + "_current_settings"
                            instance_container._id = new_id
                            instance_container.setName(new_id)
                            instance_container.setMetaDataEntry("extruder", self.getNewId(extruder_id))
                            containers_to_add.append(instance_container)

                        machine_id = instance_container.getMetaDataEntry("machine", None)
                        if machine_id:
                            new_id = self.getNewId(machine_id) + "_current_settings"
                            instance_container._id = new_id
                            instance_container.setName(new_id)
                            instance_container.setMetaDataEntry("machine", self.getNewId(machine_id))
                            containers_to_add.append(instance_container)
                user_instance_containers.append(instance_container)
            elif container_type == "quality_changes":
                # Check if quality changes already exists.
                quality_changes = self._container_registry.findInstanceContainers(id = container_id)
                if not quality_changes:
                    containers_to_add.append(instance_container)
                else:
                    if self._resolve_strategies["quality_changes"] == "override":
                        quality_changes[0].deserialize(archive.open(instance_container_file).read().decode("utf-8"))
                    elif self._resolve_strategies["quality_changes"] is None:
                        # The ID already exists, but nothing in the values changed, so do nothing.
                        pass
                quality_changes_instance_containers.append(instance_container)
            else:
                continue

        # Add all the containers right before we try to add / serialize the stack
        for container in containers_to_add:
            self._container_registry.addContainer(container)

        # Get the stack(s) saved in the workspace.
        Logger.log("d", "Workspace loading is checking stacks containers...")
        container_stack_files = [name for name in cura_file_names if name.endswith(self._container_stack_suffix)]
        global_stack = None
        extruder_stacks = []
        container_stacks_added = []
        try:
            for container_stack_file in container_stack_files:
                container_id = self._stripFileToId(container_stack_file)

                # Check if a stack by this ID already exists;
                container_stacks = self._container_registry.findContainerStacks(id=container_id)
                if container_stacks:
                    stack = container_stacks[0]
                    if self._resolve_strategies["machine"] == "override":
                        container_stacks[0].deserialize(archive.open(container_stack_file).read().decode("utf-8"))
                    elif self._resolve_strategies["machine"] == "new":
                        new_id = self.getNewId(container_id)
                        stack = ContainerStack(new_id)
                        stack.deserialize(archive.open(container_stack_file).read().decode("utf-8"))

                        # Ensure a unique ID and name
                        stack._id = new_id

                        # Extruder stacks are "bound" to a machine. If we add the machine as a new one, the id of the
                        # bound machine also needs to change.
                        if stack.getMetaDataEntry("machine", None):
                            stack.setMetaDataEntry("machine", self.getNewId(stack.getMetaDataEntry("machine")))

                        if stack.getMetaDataEntry("type") != "extruder_train":
                            # Only machines need a new name, stacks may be non-unique
                            stack.setName(self._container_registry.uniqueName(stack.getName()))
                        container_stacks_added.append(stack)
                        self._container_registry.addContainer(stack)
                    else:
                        Logger.log("w", "Resolve strategy of %s for machine is not supported", self._resolve_strategies["machine"])
                else:
                    stack = ContainerStack(container_id)
                    # Deserialize stack by converting read data from bytes to string
                    stack.deserialize(archive.open(container_stack_file).read().decode("utf-8"))
                    container_stacks_added.append(stack)
                    self._container_registry.addContainer(stack)

                if stack.getMetaDataEntry("type") == "extruder_train":
                    extruder_stacks.append(stack)
                else:
                    global_stack = stack
        except:
            Logger.log("W", "We failed to serialize the stack. Trying to clean up.")
            # Something went really wrong. Try to remove any data that we added. 
            for container in containers_to_add:
                self._container_registry.getInstance().removeContainer(container.getId())

            for container in container_stacks_added:
                self._container_registry.getInstance().removeContainer(container.getId())

            return None

        if self._resolve_strategies["machine"] == "new":
            # A new machine was made, but it was serialized with the wrong user container. Fix that now.
            for container in user_instance_containers:
                extruder_id = container.getMetaDataEntry("extruder", None)
                if extruder_id:
                    for extruder in extruder_stacks:
                        if extruder.getId() == extruder_id:
                            extruder.replaceContainer(0, container)
                            continue
                machine_id = container.getMetaDataEntry("machine", None)
                if machine_id:
                    if global_stack.getId() == machine_id:
                        global_stack.replaceContainer(0, container)
                        continue

        if self._resolve_strategies["quality_changes"] == "new":
            # Quality changes needs to get a new ID, added to registry and to the right stacks
            for container in quality_changes_instance_containers:
                old_id = container.getId()
                container.setName(self._container_registry.uniqueName(container.getName()))
                # We're not really supposed to change the ID in normal cases, but this is an exception.
                container._id = self.getNewId(container.getId())

                # The container was not added yet, as it didn't have an unique ID. It does now, so add it.
                self._container_registry.addContainer(container)

                # Replace the quality changes container
                old_container = global_stack.findContainer({"type": "quality_changes"})
                if old_container.getId() == old_id:
                    quality_changes_index = global_stack.getContainerIndex(old_container)
                    global_stack.replaceContainer(quality_changes_index, container)
                    continue

                for stack in extruder_stacks:
                    old_container = stack.findContainer({"type": "quality_changes"})
                    if old_container.getId() == old_id:
                        quality_changes_index = stack.getContainerIndex(old_container)
                        stack.replaceContainer(quality_changes_index, container)

        if self._resolve_strategies["material"] == "new":
            for material in material_containers:
                old_material = global_stack.findContainer({"type": "material"})
                if old_material.getId() in self._id_mapping:
                    material_index = global_stack.getContainerIndex(old_material)
                    global_stack.replaceContainer(material_index, material)
                    continue

                for stack in extruder_stacks:
                    old_material = stack.findContainer({"type": "material"})
                    if old_material.getId() in self._id_mapping:
                        material_index = stack.getContainerIndex(old_material)
                        stack.replaceContainer(material_index, material)
                        continue

        for stack in extruder_stacks:
            ExtruderManager.getInstance().registerExtruder(stack, global_stack.getId())
        else:
            # Machine has no extruders, but it needs to be registered with the extruder manager.
            ExtruderManager.getInstance().registerExtruder(None, global_stack.getId())

        Logger.log("d", "Workspace loading is notifying rest of the code of changes...")
        # Notify everything/one that is to notify about changes.
        for container in global_stack.getContainers():
            global_stack.containersChanged.emit(container)

        for stack in extruder_stacks:
            stack.setNextStack(global_stack)
            for container in stack.getContainers():
                stack.containersChanged.emit(container)

        # Actually change the active machine.
        Application.getInstance().setGlobalContainerStack(global_stack)
        return nodes
예제 #56
0
class Script:
    def __init__(self):
        super().__init__()
        self._settings = None
        self._stack = None

        setting_data = self.getSettingData()
        self._stack = ContainerStack(stack_id=id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.


        ## Check if the definition of this script already exists. If not, add it to the registry.
        if "key" in setting_data:
            definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = setting_data["key"])
            if definitions:
                # Definition was found
                self._definition = definitions[0]
            else:
                self._definition = DefinitionContainer(setting_data["key"])
                self._definition.deserialize(json.dumps(setting_data))
                ContainerRegistry.getInstance().addContainer(self._definition)
        self._stack.addContainer(self._definition)
        self._instance = InstanceContainer(container_id="ScriptInstanceContainer")
        self._instance.setDefinition(self._definition)
        self._stack.addContainer(self._instance)

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

    settingsLoaded = Signal()

    ##  Needs to return a dict that can be used to construct a settingcategory file. 
    #   See the example script for an example.
    #   It follows the same style / guides as the Uranium settings.
    def getSettingData(self):
        raise NotImplementedError()

    def getDefinitionId(self):
        if self._stack:
            return self._stack.getBottom().getId()

    def getStackId(self):
        if self._stack:
            return self._stack.getId()

    ##  Convenience function that retrieves value of a setting from the stack.
    def getSettingValueByKey(self, key):
        return self._stack.getProperty(key, "value")

    ##  Convenience function that finds the value in a line of g-code.
    #   When requesting key = x from line "G1 X100" the value 100 is returned.
    def getValue(self, line, key, default = None):
        if not key in line or (';' in line and line.find(key) > line.find(';')):
            return default
        sub_part = line[line.find(key) + 1:]
        m = re.search('^[0-9]+\.?[0-9]*', sub_part)
        if m is None:
            return default
        try:
            return float(m.group(0))
        except:
            return default

    ##  This is called when the script is executed. 
    #   It gets a list of g-code strings and needs to return a (modified) list.
    def execute(self, data):
        raise NotImplementedError()
예제 #57
0
    def createExtruderTrain(self, extruder_definition: DefinitionContainerInterface, machine_definition: DefinitionContainerInterface,
                            position, machine_id: str) -> None:
        # Cache some things.
        container_registry = ContainerRegistry.getInstance()
        machine_definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(machine_definition)

        # Create a container stack for this extruder.
        extruder_stack_id = container_registry.uniqueName(extruder_definition.getId())
        container_stack = ContainerStack(extruder_stack_id)
        container_stack.setName(extruder_definition.getName())  # Take over the display name to display the stack with.
        container_stack.addMetaDataEntry("type", "extruder_train")
        container_stack.addMetaDataEntry("machine", machine_id)
        container_stack.addMetaDataEntry("position", position)
        container_stack.addContainer(extruder_definition)

        # Find the variant to use for this extruder.
        variant = container_registry.findInstanceContainers(id = "empty_variant")[0]
        if machine_definition.getMetaDataEntry("has_variants"):
            # First add any variant. Later, overwrite with preference if the preference is valid.
            variants = container_registry.findInstanceContainers(definition = machine_definition_id, type = "variant")
            if len(variants) >= 1:
                variant = variants[0]
            preferred_variant_id = machine_definition.getMetaDataEntry("preferred_variant")
            if preferred_variant_id:
                preferred_variants = container_registry.findInstanceContainers(id = preferred_variant_id, definition = machine_definition_id, type = "variant")
                if len(preferred_variants) >= 1:
                    variant = preferred_variants[0]
                else:
                    Logger.log("w", "The preferred variant \"%s\" of machine %s doesn't exist or is not a variant profile.", preferred_variant_id, machine_id)
                    # And leave it at the default variant.
        container_stack.addContainer(variant)

        # Find a material to use for this variant.
        material = container_registry.findInstanceContainers(id = "empty_material")[0]
        if machine_definition.getMetaDataEntry("has_materials"):
            # First add any material. Later, overwrite with preference if the preference is valid.
            machine_has_variant_materials = machine_definition.getMetaDataEntry("has_variant_materials", default = False)
            if machine_has_variant_materials or machine_has_variant_materials == "True":
                materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id, variant = variant.getId())
            else:
                materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id)
            if len(materials) >= 1:
                material = materials[0]
            preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
            if preferred_material_id:
                global_stack = ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
                if global_stack:
                    approximate_material_diameter = str(round(global_stack[0].getProperty("material_diameter", "value")))
                else:
                    approximate_material_diameter = str(round(machine_definition.getProperty("material_diameter", "value")))

                search_criteria = { "type": "material",  "id": preferred_material_id, "approximate_diameter": approximate_material_diameter}
                if machine_definition.getMetaDataEntry("has_machine_materials"):
                    search_criteria["definition"] = machine_definition_id

                    if machine_definition.getMetaDataEntry("has_variants") and variant:
                        search_criteria["variant"] = variant.id
                else:
                    search_criteria["definition"] = "fdmprinter"

                preferred_materials = container_registry.findInstanceContainers(**search_criteria)
                if len(preferred_materials) >= 1:
                    # In some cases we get multiple materials. In that case, prefer materials that are marked as read only.
                    read_only_preferred_materials = [preferred_material for preferred_material in preferred_materials if preferred_material.isReadOnly()]
                    if len(read_only_preferred_materials) >= 1:
                        material = read_only_preferred_materials[0]
                    else:
                        material = preferred_materials[0]
                else:
                    Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id)
                    # And leave it at the default material.
        container_stack.addContainer(material)

        # Find a quality to use for this extruder.
        quality = container_registry.getEmptyInstanceContainer()

        search_criteria = { "type": "quality" }
        if machine_definition.getMetaDataEntry("has_machine_quality"):
            search_criteria["definition"] = machine_definition_id
            if machine_definition.getMetaDataEntry("has_materials") and material:
                search_criteria["material"] = material.id
        else:
            search_criteria["definition"] = "fdmprinter"

        preferred_quality = machine_definition.getMetaDataEntry("preferred_quality")
        if preferred_quality:
            search_criteria["id"] = preferred_quality

        containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
        if not containers and preferred_quality:
            Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality, machine_id)
            search_criteria.pop("id", None)
            containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
        if containers:
            quality = containers[0]

        container_stack.addContainer(quality)

        empty_quality_changes = container_registry.findInstanceContainers(id = "empty_quality_changes")[0]
        container_stack.addContainer(empty_quality_changes)

        user_profile = container_registry.findInstanceContainers(type = "user", extruder = extruder_stack_id)
        if user_profile: # There was already a user profile, loaded from settings.
            user_profile = user_profile[0]
        else:
            user_profile = InstanceContainer(extruder_stack_id + "_current_settings")  # Add an empty user profile.
            user_profile.addMetaDataEntry("type", "user")
            user_profile.addMetaDataEntry("extruder", extruder_stack_id)
            from cura.CuraApplication import CuraApplication
            user_profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
            user_profile.setDefinition(machine_definition)
            container_registry.addContainer(user_profile)
        container_stack.addContainer(user_profile)

        # regardless of what the next stack is, we have to set it again, because of signal routing.
        container_stack.setNextStack(Application.getInstance().getGlobalContainerStack())

        container_registry.addContainer(container_stack)
예제 #58
0
def test_deserialize_containers(container_stack, container_registry):
    container = InstanceContainer("a")
    container_registry.addContainer(container)

    serialised = """
    [general]
    name = Test
    id = testid
    version = {version}

    [containers]
    0 = a
    """.format(version = ContainerStack.Version)  # Test case where there is a container.

    container_stack.deserialize(serialised)
    assert container_stack.getContainers() == [container]

    container_stack = ContainerStack(str(uuid.uuid4()))
    serialised = """
    [general]
    name = Test
    id = testid
    version = {version}

    [containers]
    """.format(version = ContainerStack.Version)  # Test case where there is no container.

    container_stack.deserialize(serialised)
    assert container_stack.getContainers() == []

    container_stack = ContainerStack(str(uuid.uuid4()))
    serialised = """
    [general]
    name = Test
    id = testid
    version = {version}

    [containers]
    0 = a
    1 = a
    """.format(version = ContainerStack.Version)  # Test case where there are two of the same containers.

    container_stack.deserialize(serialised)
    assert container_stack.getContainers() == [container, container]

    container_stack = ContainerStack(str(uuid.uuid4()))
    serialised = """
    [general]
    name = Test
    id = testid
    version = {version}

    [containers]
    0 = a
    1 = b
    """.format(version = ContainerStack.Version)  # Test case where a container doesn't exist.

    with pytest.raises(Exception):
        container_stack.deserialize(serialised)

    container_stack = ContainerStack(str(uuid.uuid4()))
    container_b = InstanceContainer("b")  # Add the missing container and try again.
    ContainerRegistry.getInstance().addContainer(container_b)
    container_stack.deserialize(serialised)
    assert container_stack.getContainers() == [container, container_b]
예제 #59
0
class SettingOverrideDecorator(SceneNodeDecorator):
    ##  Event indicating that the user selected a different extruder.
    activeExtruderChanged = Signal()

    def __init__(self):
        super().__init__()
        self._stack = ContainerStack(stack_id = id(self))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        self._instance = InstanceContainer(container_id = "SettingOverrideInstanceContainer")
        self._stack.addContainer(self._instance)

        if cura.Settings.ExtruderManager.getInstance().extruderCount > 1:
            self._extruder_stack = cura.Settings.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
        deep_copy._instance = copy.deepcopy(self._instance, memo)

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

        ## Set the copied instance as the first (and only) instance container of the stack.
        deep_copy._stack.replaceContainer(0, deep_copy._instance)
        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 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
        if property_name == "value":  # Only reslice if the value has changed.
            Application.getInstance().getBackend().forceSlice()

    ##  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])
                if self._stack.getNextStack().getId() != old_extruder_stack_id: #Only reslice if the extruder changed.
                    Application.getInstance().getBackend().forceSlice()
            else:
                UM.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()
        self.activeExtruderChanged.emit()

    def getStack(self):
        return self._stack
예제 #60
0
파일: Script.py 프로젝트: TinkerGnome/Cura
class Script:
    def __init__(self) -> None:
        super().__init__()
        self._stack = None  # type: Optional[ContainerStack]
        self._definition = None  # type: Optional[DefinitionContainerInterface]
        self._instance = None  # type: Optional[InstanceContainer]

    def initialize(self) -> None:
        setting_data = self.getSettingData()
        self._stack = ContainerStack(stack_id=str(id(self)))
        self._stack.setDirty(False)  # This stack does not need to be saved.

        ## Check if the definition of this script already exists. If not, add it to the registry.
        if "key" in setting_data:
            definitions = ContainerRegistry.getInstance().findDefinitionContainers(id=setting_data["key"])
            if definitions:
                # Definition was found
                self._definition = definitions[0]
            else:
                self._definition = DefinitionContainer(setting_data["key"])
                try:
                    self._definition.deserialize(json.dumps(setting_data))
                    ContainerRegistry.getInstance().addContainer(self._definition)
                except ContainerFormatError:
                    self._definition = None
                    return
        if self._definition is None:
            return
        self._stack.addContainer(self._definition)
        self._instance = InstanceContainer(container_id="ScriptInstanceContainer")
        self._instance.setDefinition(self._definition.getId())
        self._instance.setMetaDataEntry("setting_version",
                                        self._definition.getMetaDataEntry("setting_version", default=0))
        self._stack.addContainer(self._instance)
        self._stack.propertyChanged.connect(self._onPropertyChanged)

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

    settingsLoaded = Signal()
    valueChanged = Signal()  # Signal emitted whenever a value of a setting is changed

    def _onPropertyChanged(self, key: str, property_name: str) -> None:
        if property_name == "value":
            self.valueChanged.emit()

            # Property changed: trigger reslice
            # To do this we use the global container stack propertyChanged.
            # Re-slicing is necessary for setting changes in this plugin, because the changes
            # are applied only once per "fresh" gcode
            global_container_stack = Application.getInstance().getGlobalContainerStack()
            if global_container_stack is not None:
                global_container_stack.propertyChanged.emit(key, property_name)

    ##  Needs to return a dict that can be used to construct a settingcategory file.
    #   See the example script for an example.
    #   It follows the same style / guides as the Uranium settings.
    #   Scripts can either override getSettingData directly, or use getSettingDataString
    #   to return a string that will be parsed as json. The latter has the benefit over
    #   returning a dict in that the order of settings is maintained.
    def getSettingData(self) -> Dict[str, Any]:
        setting_data_as_string = self.getSettingDataString()
        setting_data = json.loads(setting_data_as_string, object_pairs_hook = collections.OrderedDict)
        return setting_data

    def getSettingDataString(self) -> str:
        raise NotImplementedError()

    def getDefinitionId(self) -> Optional[str]:
        if self._stack:
            bottom = self._stack.getBottom()
            if bottom is not None:
                return bottom.getId()
        return None

    def getStackId(self) -> Optional[str]:
        if self._stack:
            return self._stack.getId()
        return None

    ##  Convenience function that retrieves value of a setting from the stack.
    def getSettingValueByKey(self, key: str) -> Any:
        if self._stack is not None:
            return self._stack.getProperty(key, "value")
        return None

    ##  Convenience function that finds the value in a line of g-code.
    #   When requesting key = x from line "G1 X100" the value 100 is returned.
    def getValue(self, line: str, key: str, default = None) -> Any:
        if not key in line or (';' in line and line.find(key) > line.find(';')):
            return default
        sub_part = line[line.find(key) + 1:]
        m = re.search('^-?[0-9]+\.?[0-9]*', sub_part)
        if m is None:
            return default
        try:
            return int(m.group(0))
        except ValueError: #Not an integer.
            try:
                return float(m.group(0))
            except ValueError: #Not a number at all.
                return default

    ##  Convenience function to produce a line of g-code.
    #
    #   You can put in an original g-code line and it'll re-use all the values
    #   in that line.
    #   All other keyword parameters are put in the result in g-code's format.
    #   For instance, if you put ``G=1`` in the parameters, it will output
    #   ``G1``. If you put ``G=1, X=100`` in the parameters, it will output
    #   ``G1 X100``. The parameters G and M will always be put first. The
    #   parameters T and S will be put second (or first if there is no G or M).
    #   The rest of the parameters will be put in arbitrary order.
    #   \param line The original g-code line that must be modified. If not
    #   provided, an entirely new g-code line will be produced.
    #   \return A line of g-code with the desired parameters filled in.
    def putValue(self, line: str = "", **kwargs) -> str:
        #Strip the comment.
        comment = ""
        if ";" in line:
            comment = line[line.find(";"):]
            line = line[:line.find(";")] #Strip the comment.

        #Parse the original g-code line.
        for part in line.split(" "):
            if part == "":
                continue
            parameter = part[0]
            if parameter in kwargs:
                continue #Skip this one. The user-provided parameter overwrites the one in the line.
            value = part[1:]
            kwargs[parameter] = value

        #Write the new g-code line.
        result = ""
        priority_parameters = ["G", "M", "T", "S", "F", "X", "Y", "Z", "E"] #First some parameters that get priority. In order of priority!
        for priority_key in priority_parameters:
            if priority_key in kwargs:
                if result != "":
                    result += " "
                result += priority_key + str(kwargs[priority_key])
                del kwargs[priority_key]
        for key, value in kwargs.items():
            if result != "":
                result += " "
            result += key + str(value)

        #Put the comment back in.
        if comment != "":
            if result != "":
                result += " "
            result += ";" + comment

        return result

    ##  This is called when the script is executed. 
    #   It gets a list of g-code strings and needs to return a (modified) list.
    def execute(self, data: List[str]) -> List[str]:
        raise NotImplementedError()