Example #1
0
def test_roundtrip_instance(tmpdir, process_count, loaded_container_registry):
    definition = loaded_container_registry.findDefinitionContainers(id = "inherits")[0]

    instance_container = InstanceContainer("test_container")
    instance_container.setName("Test Instance Container")
    instance_container.setDefinition(definition)
    instance_container.addMetaDataEntry("test", "test")
    instance_container.setProperty("test_setting_1", "value", 20)

    temp_file = tmpdir.join("instance_container_test")

    mp_run(process_count, write_data, temp_file, instance_container)

    assert len(list(tmpdir.listdir())) == 1

    results = mp_run(process_count, read_data, temp_file)

    for result in results:
        deserialized_container = InstanceContainer("test_container")
        deserialized_container.setDefinition(definition)
        deserialized_container.deserialize(result)

        assert deserialized_container.getName() == instance_container.getName()
        assert deserialized_container.getMetaData() == instance_container.getMetaData()
        assert deserialized_container.getProperty("test_setting_1", "value") == instance_container.getProperty("test_setting_1", "value")
Example #2
0
 def _loadProfile(self, serialized, profile_id):
     # Create an empty profile.
     profile = InstanceContainer(profile_id)
     profile.addMetaDataEntry("type", "quality_changes")
     try:
         profile.deserialize(serialized)
     except Exception as e:  # Parsing error. This is not a (valid) Cura profile then.
         Logger.log("e", "Error while trying to parse profile: %s", str(e))
         return None
     return profile
Example #3
0
    def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]:
        profile.setDirty(True)  # Ensure the profiles are correctly saved

        new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile"))
        profile._id = new_id
        profile.setName(new_name)

        if "type" in profile.getMetaData():
            profile.setMetaDataEntry("type", "quality_changes")
        else:
            profile.addMetaDataEntry("type", "quality_changes")

        quality_type = profile.getMetaDataEntry("quality_type")
        if not quality_type:
            return catalog.i18nc("@info:status", "Profile is missing a quality type.")

        quality_type_criteria = {"quality_type": quality_type}
        if self._machineHasOwnQualities():
            profile.setDefinition(self._activeQualityDefinition().getId())
            if self._machineHasOwnMaterials():
                active_material_id = self._activeMaterialId()
                if active_material_id and active_material_id != "empty":  # only update if there is an active material
                    profile.addMetaDataEntry("material", active_material_id)
                    quality_type_criteria["material"] = active_material_id

            quality_type_criteria["definition"] = profile.getDefinition().getId()

        else:
            profile.setDefinition(fdmprinter)
            quality_type_criteria["definition"] = "fdmprinter"

        machine_definition = Application.getInstance().getGlobalContainerStack().getBottom()
        del quality_type_criteria["definition"]

        # materials = None

        if "material" in quality_type_criteria:
            # materials = ContainerRegistry.getInstance().findInstanceContainers(id = quality_type_criteria["material"])
            del quality_type_criteria["material"]

        # Do not filter quality containers here with materials because we are trying to import a profile, so it should
        # NOT be restricted by the active materials on the current machine.
        materials = None

        # Check to make sure the imported profile actually makes sense in context of the current configuration.
        # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as
        # successfully imported but then fail to show up.
        from cura.QualityManager import QualityManager
        qualities = QualityManager.getInstance()._getFilteredContainersForStack(machine_definition, materials, **quality_type_criteria)
        if not qualities:
            return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type)

        ContainerRegistry.getInstance().addContainer(profile)

        return None
Example #4
0
    def _createDefinitionChangesContainer(self, container_stack, container_name, container_index = None):
        definition_changes_container = InstanceContainer(container_name)
        definition = container_stack.getBottom()
        definition_changes_container.setDefinition(definition)
        definition_changes_container.addMetaDataEntry("type", "definition_changes")
        definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)

        self._container_registry.addContainer(definition_changes_container)
        container_stack.definitionChanges = definition_changes_container

        return definition_changes_container
Example #5
0
    def _createDefinitionChangesContainer(self, global_container_stack, container_index = None):
        definition_changes_container = InstanceContainer(global_container_stack.getName() + "_settings")
        definition = global_container_stack.getBottom()
        definition_changes_container.setDefinition(definition)
        definition_changes_container.addMetaDataEntry("type", "definition_changes")

        self._container_registry.addContainer(definition_changes_container)
        # Insert definition_changes between the definition and the variant
        global_container_stack.insertContainer(-1, definition_changes_container)

        return definition_changes_container
Example #6
0
 def _loadProfile(self, serialized, profile_id):
     # Create an empty profile.
     profile = InstanceContainer(profile_id)
     profile.addMetaDataEntry("type", "quality_changes")
     try:
         profile.deserialize(serialized)
     except ContainerFormatError as e:
         Logger.log("e", "Error in the format of a container: %s", str(e))
         return None
     except Exception as e:
         Logger.log("e", "Error while trying to parse profile: %s", str(e))
         return None
     return profile
Example #7
0
    def createDefinitionChangesContainer(cls, container_stack, container_name):
        from cura.CuraApplication import CuraApplication

        unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name)

        definition_changes_container = InstanceContainer(unique_container_name)
        definition_changes_container.setDefinition(container_stack.getBottom().getId())
        definition_changes_container.addMetaDataEntry("type", "definition_changes")
        definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)

        ContainerRegistry.getInstance().addContainer(definition_changes_container)
        container_stack.definitionChanges = definition_changes_container

        return definition_changes_container
Example #8
0
 def read(self, file_name):
     # Create an empty profile.
     profile = InstanceContainer(os.path.basename(os.path.splitext(file_name)[0]))
     profile.addMetaDataEntry("type", "quality")
     try:
         with open(file_name) as f:  # Open file for reading.
             serialized = f.read()
     except IOError as e:
         Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
         return None
     
     try:
         profile.deserialize(serialized)
     except Exception as e:  # Parsing error. This is not a (valid) Cura profile then.
         Logger.log("e", "Error while trying to parse profile: %s", str(e))
         return None
     return profile
Example #9
0
    def __init__(self):
        super().__init__()
        self._stack = PerObjectContainerStack(container_id = "per_object_stack_" + str(id(self)))
        self._stack.setDirty(False)  # This stack does not need to be saved.
        user_container = InstanceContainer(container_id = self._generateUniqueName())
        user_container.addMetaDataEntry("type", "user")
        self._stack.userChanges = user_container
        self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()

        self._is_non_printing_mesh = False

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

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

        Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack)
        self.activeExtruderChanged.connect(self._updateNextStack)
        self._updateNextStack()
Example #10
0
    def read(self, file_name):
        if file_name.split(".")[-1] != "gcode":
            return None

        prefix = ";SETTING_" + str(GCodeProfileReader.version) + " "
        prefix_length = len(prefix)

        # Loading all settings from the file.
        # They are all at the end, but Python has no reverse seek any more since Python3.
        # TODO: Consider moving settings to the start?
        serialized = ""  # Will be filled with the serialized profile.
        try:
            with open(file_name) as f:
                for line in f:
                    if line.startswith(prefix):
                        # Remove the prefix and the newline from the line and add it to the rest.
                        serialized += line[prefix_length : -1]
        except IOError as e:
            Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
            return None

        # Un-escape the serialized profile.
        pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys()))

        # Perform the replacement with a regular expression.
        serialized = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialized)
        Logger.log("i", "Serialized the following from %s: %s" %(file_name, repr(serialized)))

        # Create an empty profile - the id will be changed later
        profile = InstanceContainer("")
        profile.addMetaDataEntry("type", "quality")
        try:
            profile.deserialize(serialized)
        except Exception as e:  # Not a valid g-code file.
            Logger.log("e", "Unable to serialise the profile: %s", str(e))
            return None

        #Creating a unique name using the filename of the GCode
        new_name = catalog.i18nc("@label", "Custom profile (%s)") %(os.path.splitext(os.path.basename(file_name))[0])
        profile.setName(new_name)
        profile._id = new_name

        return profile
Example #11
0
    def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]:
        profile.setReadOnly(False)
        profile.setDirty(True)  # Ensure the profiles are correctly saved

        new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile"))
        profile._id = new_id
        profile.setName(new_name)

        if "type" in profile.getMetaData():
            profile.setMetaDataEntry("type", "quality_changes")
        else:
            profile.addMetaDataEntry("type", "quality_changes")

        quality_type = profile.getMetaDataEntry("quality_type")
        if not quality_type:
            return catalog.i18nc("@info:status", "Profile is missing a quality type.")

        quality_type_criteria = {"quality_type": quality_type}
        if self._machineHasOwnQualities():
            profile.setDefinition(self._activeQualityDefinition())
            if self._machineHasOwnMaterials():
                active_material_id = self._activeMaterialId()
                if active_material_id:  # only update if there is an active material
                    profile.addMetaDataEntry("material", active_material_id)
                    quality_type_criteria["material"] = active_material_id

            quality_type_criteria["definition"] = profile.getDefinition().getId()

        else:
            profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
            quality_type_criteria["definition"] = "fdmprinter"

        # Check to make sure the imported profile actually makes sense in context of the current configuration.
        # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as
        # successfully imported but then fail to show up.
        qualities = self.findInstanceContainers(**quality_type_criteria)
        if not qualities:
            return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type)

        ContainerRegistry.getInstance().addContainer(profile)

        return None
Example #12
0
    def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainer, **kwargs) -> GlobalStack:
        stack = GlobalStack(new_stack_id)
        stack.setDefinition(definition)

        user_container = InstanceContainer(new_stack_id + "_user")
        user_container.addMetaDataEntry("type", "user")
        user_container.addMetaDataEntry("machine", new_stack_id)
        from cura.CuraApplication import CuraApplication
        user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
        user_container.setDefinition(definition)

        stack.setUserChanges(user_container)

        # Important! The order here matters, because that allows the stack to
        # assume the material and variant have already been set.
        if "definition_changes" in kwargs:
            stack.setDefinitionChangesById(kwargs["definition_changes"])

        if "variant" in kwargs:
            stack.setVariantById(kwargs["variant"])

        if "material" in kwargs:
            stack.setMaterialById(kwargs["material"])

        if "quality" in kwargs:
            stack.setQualityById(kwargs["quality"])

        if "quality_changes" in kwargs:
            stack.setQualityChangesById(kwargs["quality_changes"])

        registry = ContainerRegistry.getInstance()
        registry.addContainer(stack)
        registry.addContainer(user_container)

        return stack
Example #13
0
    def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]:
        profile.setDirty(True)  # Ensure the profiles are correctly saved

        new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile"))
        profile.setMetaDataEntry("id", new_id)
        profile.setName(new_name)

        # Set the unique Id to the profile, so it's generating a new one even if the user imports the same profile
        # It also solves an issue with importing profiles from G-Codes
        profile.setMetaDataEntry("id", new_id)
        profile.setMetaDataEntry("definition", machine_definition_id)

        if "type" in profile.getMetaData():
            profile.setMetaDataEntry("type", "quality_changes")
        else:
            profile.addMetaDataEntry("type", "quality_changes")

        quality_type = profile.getMetaDataEntry("quality_type")
        if not quality_type:
            return catalog.i18nc("@info:status", "Profile is missing a quality type.")

        global_stack = Application.getInstance().getGlobalContainerStack()
        definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition)
        profile.setDefinition(definition_id)

        # Check to make sure the imported profile actually makes sense in context of the current configuration.
        # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as
        # successfully imported but then fail to show up.
        quality_manager = CuraApplication.getInstance()._quality_manager
        quality_group_dict = quality_manager.getQualityGroupsForMachineDefinition(global_stack)
        if quality_type not in quality_group_dict:
            return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type)

        ContainerRegistry.getInstance().addContainer(profile)

        return None
Example #14
0
    def _createQualityChanges(self, quality_container, new_name, machine_definition, extruder_id):
        base_id = machine_definition.getId() if extruder_id is None else extruder_id

        # Create a new quality_changes container for the quality.
        quality_changes = InstanceContainer(self._createUniqueId(base_id, new_name))
        quality_changes.setName(new_name)
        quality_changes.addMetaDataEntry("type", "quality_changes")
        quality_changes.addMetaDataEntry("quality_type", quality_container.getMetaDataEntry("quality_type"))

        # If we are creating a container for an extruder, ensure we add that to the container
        if extruder_id is not None:
            quality_changes.addMetaDataEntry("extruder", extruder_id)

        # If the machine specifies qualities should be filtered, ensure we match the current criteria.
        if not machine_definition.getMetaDataEntry("has_machine_quality"):
            quality_changes.setDefinition(self._container_registry.findContainers(id = "fdmprinter")[0])
        else:
            quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition))

        from cura.CuraApplication import CuraApplication
        quality_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
        return quality_changes
Example #15
0
    def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str,
                                   is_global_stack: bool) -> "InstanceContainer":
        from cura.CuraApplication import CuraApplication

        unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name)

        container = InstanceContainer(unique_container_name)
        container.setDefinition(definition_id)
        container.addMetaDataEntry("type", "user")
        container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)

        metadata_key_to_add = "machine" if is_global_stack else "extruder"
        container.addMetaDataEntry(metadata_key_to_add, stack_id)

        return container
Example #16
0
    def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition_id: str, **kwargs) -> ExtruderStack:
        stack = ExtruderStack(new_stack_id)
        stack.setName(definition.getName())
        stack.setDefinition(definition)
        stack.addMetaDataEntry("position", definition.getMetaDataEntry("position"))

        if "next_stack" in kwargs:
            # Add stacks before containers are added, since they may trigger a setting update.
            stack.setNextStack(kwargs["next_stack"])

        user_container = InstanceContainer(new_stack_id + "_user")
        user_container.addMetaDataEntry("type", "user")
        user_container.addMetaDataEntry("extruder", new_stack_id)
        from cura.CuraApplication import CuraApplication
        user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
        user_container.setDefinition(machine_definition_id)

        stack.setUserChanges(user_container)

        # Important! The order here matters, because that allows the stack to
        # assume the material and variant have already been set.
        if "definition_changes" in kwargs:
            stack.setDefinitionChangesById(kwargs["definition_changes"])
        else:
            stack.setDefinitionChanges(cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings"))

        if "variant" in kwargs:
            stack.setVariantById(kwargs["variant"])

        if "material" in kwargs:
            stack.setMaterialById(kwargs["material"])

        if "quality" in kwargs:
            stack.setQualityById(kwargs["quality"])

        if "quality_changes" in kwargs:
            stack.setQualityChangesById(kwargs["quality_changes"])

        # Only add the created containers to the registry after we have set all the other
        # properties. This makes the create operation more transactional, since any problems
        # setting properties will not result in incomplete containers being added.
        registry = ContainerRegistry.getInstance()
        registry.addContainer(stack)
        registry.addContainer(user_container)

        return stack
Example #17
0
    def addExtruderStackForSingleExtrusionMachine(
            self,
            machine,
            extruder_id,
            new_global_quality_changes=None,
            create_new_ids=True):
        new_extruder_id = extruder_id

        application = CuraApplication.getInstance()

        extruder_definitions = self.findDefinitionContainers(
            id=new_extruder_id)
        if not extruder_definitions:
            Logger.log("w",
                       "Could not find definition containers for extruder %s",
                       new_extruder_id)
            return

        extruder_definition = extruder_definitions[0]
        unique_name = self.uniqueName(
            machine.getName() + " " + new_extruder_id
        ) if create_new_ids else machine.getName() + " " + new_extruder_id

        extruder_stack = ExtruderStack.ExtruderStack(unique_name,
                                                     parent=machine)
        extruder_stack.setName(extruder_definition.getName())
        extruder_stack.setDefinition(extruder_definition)
        extruder_stack.addMetaDataEntry(
            "position", extruder_definition.getMetaDataEntry("position"))

        # create a new definition_changes container for the extruder stack
        definition_changes_id = self.uniqueName(
            extruder_stack.getId() + "_settings"
        ) if create_new_ids else extruder_stack.getId() + "_settings"
        definition_changes_name = definition_changes_id
        definition_changes = InstanceContainer(definition_changes_id,
                                               parent=application)
        definition_changes.setName(definition_changes_name)
        definition_changes.addMetaDataEntry("setting_version",
                                            CuraApplication.SettingVersion)
        definition_changes.addMetaDataEntry("type", "definition_changes")
        definition_changes.addMetaDataEntry("definition",
                                            extruder_definition.getId())

        # move definition_changes settings if exist
        for setting_key in definition_changes.getAllKeys():
            if machine.definition.getProperty(setting_key,
                                              "settable_per_extruder"):
                setting_value = machine.definitionChanges.getProperty(
                    setting_key, "value")
                if setting_value is not None:
                    # move it to the extruder stack's definition_changes
                    setting_definition = machine.getSettingDefinition(
                        setting_key)
                    new_instance = SettingInstance(setting_definition,
                                                   definition_changes)
                    new_instance.setProperty("value", setting_value)
                    new_instance.resetState(
                    )  # Ensure that the state is not seen as a user state.
                    definition_changes.addInstance(new_instance)
                    definition_changes.setDirty(True)

                    machine.definitionChanges.removeInstance(
                        setting_key, postpone_emit=True)

        self.addContainer(definition_changes)
        extruder_stack.setDefinitionChanges(definition_changes)

        # create empty user changes container otherwise
        user_container_id = self.uniqueName(
            extruder_stack.getId() +
            "_user") if create_new_ids else extruder_stack.getId() + "_user"
        user_container_name = user_container_id
        user_container = InstanceContainer(user_container_id,
                                           parent=application)
        user_container.setName(user_container_name)
        user_container.addMetaDataEntry("type", "user")
        user_container.addMetaDataEntry("machine", machine.getId())
        user_container.addMetaDataEntry("setting_version",
                                        CuraApplication.SettingVersion)
        user_container.setDefinition(machine.definition.getId())
        user_container.setMetaDataEntry(
            "position", extruder_stack.getMetaDataEntry("position"))

        if machine.userChanges:
            # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes
            # container to the extruder stack.
            for user_setting_key in machine.userChanges.getAllKeys():
                settable_per_extruder = machine.getProperty(
                    user_setting_key, "settable_per_extruder")
                if settable_per_extruder:
                    setting_value = machine.getProperty(
                        user_setting_key, "value")

                    setting_definition = machine.getSettingDefinition(
                        user_setting_key)
                    new_instance = SettingInstance(setting_definition,
                                                   definition_changes)
                    new_instance.setProperty("value", setting_value)
                    new_instance.resetState(
                    )  # Ensure that the state is not seen as a user state.
                    user_container.addInstance(new_instance)
                    user_container.setDirty(True)

                    machine.userChanges.removeInstance(user_setting_key,
                                                       postpone_emit=True)

        self.addContainer(user_container)
        extruder_stack.setUserChanges(user_container)

        empty_variant = application.empty_variant_container
        empty_material = application.empty_material_container
        empty_quality = application.empty_quality_container

        if machine.variant.getId() not in ("empty", "empty_variant"):
            variant = machine.variant
        else:
            variant = empty_variant
        extruder_stack.variant = variant

        if machine.material.getId() not in ("empty", "empty_material"):
            material = machine.material
        else:
            material = empty_material
        extruder_stack.material = material

        if machine.quality.getId() not in ("empty", "empty_quality"):
            quality = machine.quality
        else:
            quality = empty_quality
        extruder_stack.quality = quality

        machine_quality_changes = machine.qualityChanges
        if new_global_quality_changes is not None:
            machine_quality_changes = new_global_quality_changes

        if machine_quality_changes.getId() not in ("empty",
                                                   "empty_quality_changes"):
            extruder_quality_changes_container = self.findInstanceContainers(
                name=machine_quality_changes.getName(), extruder=extruder_id)
            if extruder_quality_changes_container:
                extruder_quality_changes_container = extruder_quality_changes_container[
                    0]

                quality_changes_id = extruder_quality_changes_container.getId()
                extruder_stack.qualityChanges = self.findInstanceContainers(
                    id=quality_changes_id)[0]
            else:
                # Some extruder quality_changes containers can be created at runtime as files in the qualities
                # folder. Those files won't be loaded in the registry immediately. So we also need to search
                # the folder to see if the quality_changes exists.
                extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(
                    machine_quality_changes.getName())
                if extruder_quality_changes_container:
                    quality_changes_id = extruder_quality_changes_container.getId(
                    )
                    extruder_quality_changes_container.addMetaDataEntry(
                        "position",
                        extruder_definition.getMetaDataEntry("position"))
                    extruder_stack.qualityChanges = self.findInstanceContainers(
                        id=quality_changes_id)[0]
                else:
                    # if we still cannot find a quality changes container for the extruder, create a new one
                    container_name = machine_quality_changes.getName()
                    container_id = self.uniqueName(extruder_stack.getId() +
                                                   "_qc_" + container_name)
                    extruder_quality_changes_container = InstanceContainer(
                        container_id, parent=application)
                    extruder_quality_changes_container.setName(container_name)
                    extruder_quality_changes_container.addMetaDataEntry(
                        "type", "quality_changes")
                    extruder_quality_changes_container.addMetaDataEntry(
                        "setting_version", CuraApplication.SettingVersion)
                    extruder_quality_changes_container.addMetaDataEntry(
                        "position",
                        extruder_definition.getMetaDataEntry("position"))
                    extruder_quality_changes_container.addMetaDataEntry(
                        "quality_type",
                        machine_quality_changes.getMetaDataEntry(
                            "quality_type"))
                    extruder_quality_changes_container.setDefinition(
                        machine_quality_changes.getDefinition().getId())

                    self.addContainer(extruder_quality_changes_container)
                    extruder_stack.qualityChanges = extruder_quality_changes_container

            if not extruder_quality_changes_container:
                Logger.log(
                    "w",
                    "Could not find quality_changes named [%s] for extruder [%s]",
                    machine_quality_changes.getName(), extruder_stack.getId())
            else:
                # move all per-extruder settings to the extruder's quality changes
                for qc_setting_key in machine_quality_changes.getAllKeys():
                    settable_per_extruder = machine.getProperty(
                        qc_setting_key, "settable_per_extruder")
                    if settable_per_extruder:
                        setting_value = machine_quality_changes.getProperty(
                            qc_setting_key, "value")

                        setting_definition = machine.getSettingDefinition(
                            qc_setting_key)
                        new_instance = SettingInstance(setting_definition,
                                                       definition_changes)
                        new_instance.setProperty("value", setting_value)
                        new_instance.resetState(
                        )  # Ensure that the state is not seen as a user state.
                        extruder_quality_changes_container.addInstance(
                            new_instance)
                        extruder_quality_changes_container.setDirty(True)

                        machine_quality_changes.removeInstance(
                            qc_setting_key, postpone_emit=True)
        else:
            extruder_stack.qualityChanges = self.findInstanceContainers(
                id="empty_quality_changes")[0]

        self.addContainer(extruder_stack)

        # Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have
        # per-extruder settings in the container for the machine instead of the extruder.
        if machine_quality_changes.getId() not in ("empty",
                                                   "empty_quality_changes"):
            quality_changes_machine_definition_id = machine_quality_changes.getDefinition(
            ).getId()
        else:
            whole_machine_definition = machine.definition
            machine_entry = machine.definition.getMetaDataEntry("machine")
            if machine_entry is not None:
                container_registry = ContainerRegistry.getInstance()
                whole_machine_definition = container_registry.findDefinitionContainers(
                    id=machine_entry)[0]

            quality_changes_machine_definition_id = "fdmprinter"
            if whole_machine_definition.getMetaDataEntry(
                    "has_machine_quality"):
                quality_changes_machine_definition_id = machine.definition.getMetaDataEntry(
                    "quality_definition", whole_machine_definition.getId())
        qcs = self.findInstanceContainers(
            type="quality_changes",
            definition=quality_changes_machine_definition_id)
        qc_groups = {}  # map of qc names -> qc containers
        for qc in qcs:
            qc_name = qc.getName()
            if qc_name not in qc_groups:
                qc_groups[qc_name] = []
            qc_groups[qc_name].append(qc)
            # try to find from the quality changes cura directory too
            quality_changes_container = self._findQualityChangesContainerInCuraFolder(
                machine_quality_changes.getName())
            if quality_changes_container:
                qc_groups[qc_name].append(quality_changes_container)

        for qc_name, qc_list in qc_groups.items():
            qc_dict = {"global": None, "extruders": []}
            for qc in qc_list:
                extruder_position = qc.getMetaDataEntry("position")
                if extruder_position is not None:
                    qc_dict["extruders"].append(qc)
                else:
                    qc_dict["global"] = qc
            if qc_dict["global"] is not None and len(
                    qc_dict["extruders"]) == 1:
                # move per-extruder settings
                for qc_setting_key in qc_dict["global"].getAllKeys():
                    settable_per_extruder = machine.getProperty(
                        qc_setting_key, "settable_per_extruder")
                    if settable_per_extruder:
                        setting_value = qc_dict["global"].getProperty(
                            qc_setting_key, "value")

                        setting_definition = machine.getSettingDefinition(
                            qc_setting_key)
                        new_instance = SettingInstance(setting_definition,
                                                       definition_changes)
                        new_instance.setProperty("value", setting_value)
                        new_instance.resetState(
                        )  # Ensure that the state is not seen as a user state.
                        qc_dict["extruders"][0].addInstance(new_instance)
                        qc_dict["extruders"][0].setDirty(True)

                        qc_dict["global"].removeInstance(qc_setting_key,
                                                         postpone_emit=True)

        # Set next stack at the end
        extruder_stack.setNextStack(machine)

        return extruder_stack
Example #18
0
class Script:
    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)

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

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

            # Property changed: trigger reslice
            # To do this we use the global container stack propertyChanged.
            # Reslicing is necessary for setting changes in this plugin, because the changes
            # are applied only once per "fresh" gcode
            global_container_stack = Application.getInstance().getGlobalContainerStack()
            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):
        setting_data = self.getSettingDataString()
        if type(setting_data) == str:
            setting_data = json.loads(setting_data, object_pairs_hook = collections.OrderedDict)
        return setting_data

    def getSettingDataString(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

    ##  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 = "", **kwargs):
        #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):
        raise NotImplementedError()
Example #19
0
    def read(self, file_name):
        if file_name.split(".")[-1] != "ini":
            return None
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        if not global_container_stack:
            return None

        multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
        if multi_extrusion:
            Logger.log("e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name)
            raise Exception("Unable to import legacy profile. Multi extrusion is not supported")

        Logger.log("i", "Importing legacy profile from file " + file_name + ".")
        container_registry = ContainerRegistry.getInstance()
        profile_id = container_registry.uniqueName("Imported Legacy Profile")
        profile = InstanceContainer(profile_id)  # Create an empty profile.

        parser = configparser.ConfigParser(interpolation = None)
        try:
            parser.read([file_name])  # Parse the INI file.
        except Exception as e:
            Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e))
            return None

        # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile".
        # Since importing multiple machine profiles is out of scope, just import the first section we find.
        section = ""
        for found_section in parser.sections():
            if found_section.startswith("profile"):
                section = found_section
                break
        if not section:  # No section starting with "profile" was found. Probably not a proper INI file.
            return None

        try:
            with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", encoding = "utf-8") as f:
                dict_of_doom = json.load(f)  # Parse the Dictionary of Doom.
        except IOError as e:
            Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e))
            return None
        except Exception as e:
            Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e))
            return None

        defaults = self.prepareDefaults(dict_of_doom)
        legacy_settings = self.prepareLocals(parser, section, defaults) #Gets the settings from the legacy profile.

        #Check the target version in the Dictionary of Doom with this application version.
        if "target_version" not in dict_of_doom:
            Logger.log("e", "Dictionary of Doom has no target version. Is it the correct JSON file?")
            return None
        if InstanceContainer.Version != dict_of_doom["target_version"]:
            Logger.log("e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version))
            return None

        if "translation" not in dict_of_doom:
            Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
            return None
        current_printer_definition = global_container_stack.definition
        quality_definition = current_printer_definition.getMetaDataEntry("quality_definition")
        if not quality_definition:
            quality_definition = current_printer_definition.getId()
        profile.setDefinition(quality_definition)
        for new_setting in dict_of_doom["translation"]:  # Evaluate all new settings that would get a value from the translations.
            old_setting_expression = dict_of_doom["translation"][new_setting]
            compiled = compile(old_setting_expression, new_setting, "eval")
            try:
                new_value = eval(compiled, {"math": math}, legacy_settings)  # Pass the legacy settings as local variables to allow access to in the evaluation.
                value_using_defaults = eval(compiled, {"math": math}, defaults)  #Evaluate again using only the default values to try to see if they are default.
            except Exception:  # Probably some setting name that was missing or something else that went wrong in the ini file.
                Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.")
                continue
            definitions = current_printer_definition.findDefinitions(key = new_setting)
            if definitions:
                if new_value != value_using_defaults and definitions[0].default_value != new_value:  # Not equal to the default in the new Cura OR the default in the legacy Cura.
                    profile.setProperty(new_setting, "value", new_value)  # Store the setting in the profile!

        if len(profile.getAllKeys()) == 0:
            Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")

        profile.addMetaDataEntry("type", "profile")
        # don't know what quality_type it is based on, so use "normal" by default
        profile.addMetaDataEntry("quality_type", "normal")
        profile.setName(profile_id)
        profile.setDirty(True)

        #Serialise and deserialise in order to perform the version upgrade.
        parser = configparser.ConfigParser(interpolation=None)
        data = profile.serialize()
        parser.read_string(data)
        parser["general"]["version"] = "1"
        if parser.has_section("values"):
            parser["settings"] = parser["values"]
            del parser["values"]
        stream = io.StringIO()
        parser.write(stream)
        data = stream.getvalue()
        profile.deserialize(data)

        # The definition can get reset to fdmprinter during the deserialization's upgrade. Here we set the definition
        # again.
        profile.setDefinition(quality_definition)

        #We need to return one extruder stack and one global stack.
        global_container_id = container_registry.uniqueName("Global Imported Legacy Profile")
        global_profile = profile.duplicate(new_id = global_container_id, new_name = profile_id) #Needs to have the same name as the extruder profile.
        global_profile.setDirty(True)

        profile_definition = "fdmprinter"
        from UM.Util import parseBool
        if parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", "False")):
            profile_definition = global_container_stack.getMetaDataEntry("quality_definition")
            if not profile_definition:
                profile_definition = global_container_stack.definition.getId()
        global_profile.setDefinition(profile_definition)

        return [global_profile]
Example #20
0
    def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id):
        new_extruder_id = extruder_id

        extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
        if not extruder_definitions:
            Logger.log("w", "Could not find definition containers for extruder %s", new_extruder_id)
            return

        extruder_definition = extruder_definitions[0]
        unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id)

        extruder_stack = ExtruderStack.ExtruderStack(unique_name)
        extruder_stack.setName(extruder_definition.getName())
        extruder_stack.setDefinition(extruder_definition)
        extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
        extruder_stack.setNextStack(machine)

        # create empty user changes container otherwise
        user_container = InstanceContainer(extruder_stack.id + "_user")
        user_container.addMetaDataEntry("type", "user")
        user_container.addMetaDataEntry("machine", extruder_stack.getId())
        from cura.CuraApplication import CuraApplication
        user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
        user_container.setDefinition(machine.definition)

        if machine.userChanges:
            # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes
            # container to the extruder stack.
            for user_setting_key in machine.userChanges.getAllKeys():
                settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder")
                if settable_per_extruder:
                    user_container.addInstance(machine.userChanges.getInstance(user_setting_key))
                    machine.userChanges.removeInstance(user_setting_key, postpone_emit = True)

        extruder_stack.setUserChanges(user_container)
        self.addContainer(user_container)

        variant_id = "default"
        if machine.variant.getId() not in ("empty", "empty_variant"):
            variant_id = machine.variant.getId()
        else:
            variant_id = "empty_variant"
        extruder_stack.setVariantById(variant_id)

        material_id = "default"
        if machine.material.getId() not in ("empty", "empty_material"):
            material_id = machine.material.getId()
        else:
            material_id = "empty_material"
        extruder_stack.setMaterialById(material_id)

        quality_id = "default"
        if machine.quality.getId() not in ("empty", "empty_quality"):
            quality_id = machine.quality.getId()
        else:
            quality_id = "empty_quality"
        extruder_stack.setQualityById(quality_id)

        if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
            extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id)
            if extruder_quality_changes_container:
                extruder_quality_changes_container = extruder_quality_changes_container[0]
                quality_changes_id = extruder_quality_changes_container.getId()
                extruder_stack.setQualityChangesById(quality_changes_id)
            else:
                # Some extruder quality_changes containers can be created at runtime as files in the qualities
                # folder. Those files won't be loaded in the registry immediately. So we also need to search
                # the folder to see if the quality_changes exists.
                extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName())
                if extruder_quality_changes_container:
                    quality_changes_id = extruder_quality_changes_container.getId()
                    extruder_stack.setQualityChangesById(quality_changes_id)

            if not extruder_quality_changes_container:
                Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]",
                           machine.qualityChanges.getName(), extruder_stack.getId())
        else:
            extruder_stack.setQualityChangesById("empty_quality_changes")

        self.addContainer(extruder_stack)

        return extruder_stack
    def read(self, file_name):
        if file_name.split(".")[-1] != "ini":
            return None
        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return None

        multi_extrusion = global_container_stack.getProperty(
            "machine_extruder_count", "value") > 1
        if multi_extrusion:
            Logger.log(
                "e",
                "Unable to import legacy profile %s. Multi extrusion is not supported",
                file_name)
            raise Exception(
                "Unable to import legacy profile. Multi extrusion is not supported"
            )

        Logger.log("i",
                   "Importing legacy profile from file " + file_name + ".")
        container_registry = ContainerRegistry.getInstance()
        profile_id = container_registry.uniqueName("Imported Legacy Profile")
        profile = InstanceContainer(profile_id)  # Create an empty profile.

        parser = configparser.ConfigParser(interpolation=None)
        try:
            parser.read([file_name])  # Parse the INI file.
        except Exception as e:
            Logger.log("e", "Unable to open legacy profile %s: %s", file_name,
                       str(e))
            return None

        # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile".
        # Since importing multiple machine profiles is out of scope, just import the first section we find.
        section = ""
        for found_section in parser.sections():
            if found_section.startswith("profile"):
                section = found_section
                break
        if not section:  # No section starting with "profile" was found. Probably not a proper INI file.
            return None

        try:
            with open(
                    os.path.join(
                        PluginRegistry.getInstance().getPluginPath(
                            "LegacyProfileReader"), "DictionaryOfDoom.json"),
                    "r", -1, "utf-8") as f:
                dict_of_doom = json.load(f)  # Parse the Dictionary of Doom.
        except IOError as e:
            Logger.log("e",
                       "Could not open DictionaryOfDoom.json for reading: %s",
                       str(e))
            return None
        except Exception as e:
            Logger.log("e", "Could not parse DictionaryOfDoom.json: %s",
                       str(e))
            return None

        defaults = self.prepareDefaults(dict_of_doom)
        legacy_settings = self.prepareLocals(
            parser, section,
            defaults)  #Gets the settings from the legacy profile.

        #Check the target version in the Dictionary of Doom with this application version.
        if "target_version" not in dict_of_doom:
            Logger.log(
                "e",
                "Dictionary of Doom has no target version. Is it the correct JSON file?"
            )
            return None
        if InstanceContainer.Version != dict_of_doom["target_version"]:
            Logger.log(
                "e",
                "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!",
                dict_of_doom["target_version"], str(InstanceContainer.Version))
            return None

        if "translation" not in dict_of_doom:
            Logger.log(
                "e",
                "Dictionary of Doom has no translation. Is it the correct JSON file?"
            )
            return None
        current_printer_definition = global_container_stack.definition
        quality_definition = current_printer_definition.getMetaDataEntry(
            "quality_definition")
        if not quality_definition:
            quality_definition = current_printer_definition.getId()
        profile.setDefinition(quality_definition)
        for new_setting in dict_of_doom[
                "translation"]:  # Evaluate all new settings that would get a value from the translations.
            old_setting_expression = dict_of_doom["translation"][new_setting]
            compiled = compile(old_setting_expression, new_setting, "eval")
            try:
                new_value = eval(
                    compiled, {"math": math}, legacy_settings
                )  # Pass the legacy settings as local variables to allow access to in the evaluation.
                value_using_defaults = eval(
                    compiled, {"math": math}, defaults
                )  #Evaluate again using only the default values to try to see if they are default.
            except Exception:  # Probably some setting name that was missing or something else that went wrong in the ini file.
                Logger.log(
                    "w", "Setting " + new_setting +
                    " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile."
                )
                continue
            definitions = current_printer_definition.findDefinitions(
                key=new_setting)
            if definitions:
                if new_value != value_using_defaults and definitions[
                        0].default_value != new_value:  # Not equal to the default in the new Cura OR the default in the legacy Cura.
                    profile.setProperty(
                        new_setting, "value",
                        new_value)  # Store the setting in the profile!

        if len(profile.getAllKeys()) == 0:
            Logger.log(
                "i",
                "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile."
            )

        profile.addMetaDataEntry("type", "profile")
        # don't know what quality_type it is based on, so use "normal" by default
        profile.addMetaDataEntry("quality_type", "normal")
        profile.setName(profile_id)
        profile.setDirty(True)

        #Serialise and deserialise in order to perform the version upgrade.
        parser = configparser.ConfigParser(interpolation=None)
        data = profile.serialize()
        parser.read_string(data)
        parser["general"]["version"] = "1"
        if parser.has_section("values"):
            parser["settings"] = parser["values"]
            del parser["values"]
        stream = io.StringIO()
        parser.write(stream)
        data = stream.getvalue()
        profile.deserialize(data)

        # The definition can get reset to fdmprinter during the deserialization's upgrade. Here we set the definition
        # again.
        profile.setDefinition(quality_definition)

        #We need to return one extruder stack and one global stack.
        global_container_id = container_registry.uniqueName(
            "Global Imported Legacy Profile")
        global_profile = profile.duplicate(
            new_id=global_container_id, new_name=profile_id
        )  #Needs to have the same name as the extruder profile.
        global_profile.setDirty(True)

        profile_definition = "fdmprinter"
        from UM.Util import parseBool
        if parseBool(
                global_container_stack.getMetaDataEntry(
                    "has_machine_quality", "False")):
            profile_definition = global_container_stack.getMetaDataEntry(
                "quality_definition")
            if not profile_definition:
                profile_definition = global_container_stack.definition.getId()
        global_profile.setDefinition(profile_definition)

        return [global_profile]
Example #22
0
    def createExtruderTrain(self, extruder_definition: DefinitionContainer,
                            machine_definition: DefinitionContainer, 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 = round(
                        global_stack[0].getProperty("material_diameter",
                                                    "value"))
                else:
                    approximate_material_diameter = 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)
            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)
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._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)

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

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

            # Property changed: trigger reslice
            # To do this we use the global container stack propertyChanged.
            # Reslicing is necessary for setting changes in this plugin, because the changes
            # are applied only once per "fresh" gcode
            global_container_stack = Application.getInstance().getGlobalContainerStack()
            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):
        setting_data = self.getSettingDataString()
        if type(setting_data) == str:
            setting_data = json.loads(setting_data, object_pairs_hook = collections.OrderedDict)
        return setting_data

    def getSettingDataString(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()
Example #24
0
    def read(self, file_name):
        if file_name.split(".")[-1] != "ini":
            return None

        multi_extrusion = Application.getInstance().getGlobalContainerStack(
        ).getProperty("machine_extruder_count", "value") > 1
        if multi_extrusion:
            Logger.log(
                "e",
                "Unable to import legacy profile %s. Multi extrusion is not supported",
                file_name)
            raise Exception(
                "Unable to import legacy profile. Multi extrusion is not supported"
            )

        Logger.log("i",
                   "Importing legacy profile from file " + file_name + ".")
        profile = InstanceContainer(
            "Imported Legacy Profile")  # Create an empty profile.

        parser = configparser.ConfigParser(interpolation=None)
        try:
            with open(file_name) as f:
                parser.readfp(f)  #Parse the INI file.
        except Exception as e:
            Logger.log("e", "Unable to open legacy profile %s: %s", file_name,
                       str(e))
            return None

        #Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile".
        #Since importing multiple machine profiles is out of scope, just import the first section we find.
        section = ""
        for found_section in parser.sections():
            if found_section.startswith("profile"):
                section = found_section
                break
        if not section:  #No section starting with "profile" was found. Probably not a proper INI file.
            return None

        try:
            with open(
                    os.path.join(
                        PluginRegistry.getInstance().getPluginPath(
                            "LegacyProfileReader"), "DictionaryOfDoom.json"),
                    "r", -1, "utf-8") as f:
                dict_of_doom = json.load(f)  #Parse the Dictionary of Doom.
        except IOError as e:
            Logger.log("e",
                       "Could not open DictionaryOfDoom.json for reading: %s",
                       str(e))
            return None
        except Exception as e:
            Logger.log("e", "Could not parse DictionaryOfDoom.json: %s",
                       str(e))
            return None

        defaults = self.prepareDefaults(dict_of_doom)
        legacy_settings = self.prepareLocals(
            parser, section,
            defaults)  #Gets the settings from the legacy profile.

        #Check the target version in the Dictionary of Doom with this application version.
        if "target_version" not in dict_of_doom:
            Logger.log(
                "e",
                "Dictionary of Doom has no target version. Is it the correct JSON file?"
            )
            return None
        if InstanceContainer.Version != dict_of_doom["target_version"]:
            Logger.log(
                "e",
                "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!",
                dict_of_doom["target_version"], str(InstanceContainer.Version))
            return None

        if "translation" not in dict_of_doom:
            Logger.log(
                "e",
                "Dictionary of Doom has no translation. Is it the correct JSON file?"
            )
            return None
        current_printer_definition = Application.getInstance(
        ).getGlobalContainerStack().getBottom()
        profile.setDefinition(current_printer_definition)
        for new_setting in dict_of_doom[
                "translation"]:  #Evaluate all new settings that would get a value from the translations.
            old_setting_expression = dict_of_doom["translation"][new_setting]
            compiled = compile(old_setting_expression, new_setting, "eval")
            try:
                new_value = eval(
                    compiled, {"math": math}, legacy_settings
                )  #Pass the legacy settings as local variables to allow access to in the evaluation.
                value_using_defaults = eval(
                    compiled, {"math": math}, defaults
                )  #Evaluate again using only the default values to try to see if they are default.
            except Exception:  #Probably some setting name that was missing or something else that went wrong in the ini file.
                Logger.log(
                    "w", "Setting " + new_setting +
                    " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile."
                )
                continue
            definitions = current_printer_definition.findDefinitions(
                key=new_setting)
            if definitions:
                if new_value != value_using_defaults and definitions[
                        0].default_value != new_value:  # Not equal to the default in the new Cura OR the default in the legacy Cura.
                    profile.setProperty(
                        new_setting, "value",
                        new_value)  # Store the setting in the profile!

        if len(profile.getAllKeys()) == 0:
            Logger.log(
                "i",
                "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile."
            )
        profile.setDirty(True)
        profile.addMetaDataEntry("type", "quality_changes")
        profile.addMetaDataEntry("quality", "normal")
        return profile
Example #25
0
    def read(self, file_name):
        if file_name.split(".")[-1] != "ini":
            return None
        global_container_stack = Application.getInstance().getGlobalContainerStack()
        if not global_container_stack:
            return None

        multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
        if multi_extrusion:
            Logger.log("e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name)
            raise Exception("Unable to import legacy profile. Multi extrusion is not supported")

        Logger.log("i", "Importing legacy profile from file " + file_name + ".")
        profile = InstanceContainer("Imported Legacy Profile")  # Create an empty profile.

        parser = configparser.ConfigParser(interpolation = None)
        try:
            with open(file_name) as f:
                parser.readfp(f)  # Parse the INI file.
        except Exception as e:
            Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e))
            return None

        # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile".
        # Since importing multiple machine profiles is out of scope, just import the first section we find.
        section = ""
        for found_section in parser.sections():
            if found_section.startswith("profile"):
                section = found_section
                break
        if not section:  # No section starting with "profile" was found. Probably not a proper INI file.
            return None

        try:
            with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", -1, "utf-8") as f:
                dict_of_doom = json.load(f)  # Parse the Dictionary of Doom.
        except IOError as e:
            Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e))
            return None
        except Exception as e:
            Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e))
            return None

        defaults = self.prepareDefaults(dict_of_doom)
        legacy_settings = self.prepareLocals(parser, section, defaults) #Gets the settings from the legacy profile.

        #Check the target version in the Dictionary of Doom with this application version.
        if "target_version" not in dict_of_doom:
            Logger.log("e", "Dictionary of Doom has no target version. Is it the correct JSON file?")
            return None
        if InstanceContainer.Version != dict_of_doom["target_version"]:
            Logger.log("e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version))
            return None

        if "translation" not in dict_of_doom:
            Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
            return None
        current_printer_definition = global_container_stack.getBottom()
        profile.setDefinition(current_printer_definition)
        for new_setting in dict_of_doom["translation"]:  # Evaluate all new settings that would get a value from the translations.
            old_setting_expression = dict_of_doom["translation"][new_setting]
            compiled = compile(old_setting_expression, new_setting, "eval")
            try:
                new_value = eval(compiled, {"math": math}, legacy_settings)  # Pass the legacy settings as local variables to allow access to in the evaluation.
                value_using_defaults = eval(compiled, {"math": math}, defaults)  #Evaluate again using only the default values to try to see if they are default.
            except Exception:  # Probably some setting name that was missing or something else that went wrong in the ini file.
                Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.")
                continue
            definitions = current_printer_definition.findDefinitions(key = new_setting)
            if definitions:
                if new_value != value_using_defaults and definitions[0].default_value != new_value:  # Not equal to the default in the new Cura OR the default in the legacy Cura.
                    profile.setProperty(new_setting, "value", new_value)  # Store the setting in the profile!

        if len(profile.getAllKeys()) == 0:
            Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")
        profile.setDirty(True)
        profile.addMetaDataEntry("type", "quality_changes")
        profile.addMetaDataEntry("quality_type", "normal")
        return profile
Example #26
0
    def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id, new_global_quality_changes = None, create_new_ids = True):
        new_extruder_id = extruder_id

        application = CuraApplication.getInstance()

        extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
        if not extruder_definitions:
            Logger.log("w", "Could not find definition containers for extruder %s", new_extruder_id)
            return

        extruder_definition = extruder_definitions[0]
        unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id) if create_new_ids else machine.getName() + " " + new_extruder_id

        extruder_stack = ExtruderStack.ExtruderStack(unique_name)
        extruder_stack.setName(extruder_definition.getName())
        extruder_stack.setDefinition(extruder_definition)
        extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))

        # create a new definition_changes container for the extruder stack
        definition_changes_id = self.uniqueName(extruder_stack.getId() + "_settings") if create_new_ids else extruder_stack.getId() + "_settings"
        definition_changes_name = definition_changes_id
        definition_changes = InstanceContainer(definition_changes_id, parent = application)
        definition_changes.setName(definition_changes_name)
        definition_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
        definition_changes.addMetaDataEntry("type", "definition_changes")
        definition_changes.addMetaDataEntry("definition", extruder_definition.getId())

        # move definition_changes settings if exist
        for setting_key in definition_changes.getAllKeys():
            if machine.definition.getProperty(setting_key, "settable_per_extruder"):
                setting_value = machine.definitionChanges.getProperty(setting_key, "value")
                if setting_value is not None:
                    # move it to the extruder stack's definition_changes
                    setting_definition = machine.getSettingDefinition(setting_key)
                    new_instance = SettingInstance(setting_definition, definition_changes)
                    new_instance.setProperty("value", setting_value)
                    new_instance.resetState()  # Ensure that the state is not seen as a user state.
                    definition_changes.addInstance(new_instance)
                    definition_changes.setDirty(True)

                    machine.definitionChanges.removeInstance(setting_key, postpone_emit = True)

        self.addContainer(definition_changes)
        extruder_stack.setDefinitionChanges(definition_changes)

        # create empty user changes container otherwise
        user_container_id = self.uniqueName(extruder_stack.getId() + "_user") if create_new_ids else extruder_stack.getId() + "_user"
        user_container_name = user_container_id
        user_container = InstanceContainer(user_container_id, parent = application)
        user_container.setName(user_container_name)
        user_container.addMetaDataEntry("type", "user")
        user_container.addMetaDataEntry("machine", machine.getId())
        user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
        user_container.setDefinition(machine.definition.getId())
        user_container.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))

        if machine.userChanges:
            # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes
            # container to the extruder stack.
            for user_setting_key in machine.userChanges.getAllKeys():
                settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder")
                if settable_per_extruder:
                    setting_value = machine.getProperty(user_setting_key, "value")

                    setting_definition = machine.getSettingDefinition(user_setting_key)
                    new_instance = SettingInstance(setting_definition, definition_changes)
                    new_instance.setProperty("value", setting_value)
                    new_instance.resetState()  # Ensure that the state is not seen as a user state.
                    user_container.addInstance(new_instance)
                    user_container.setDirty(True)

                    machine.userChanges.removeInstance(user_setting_key, postpone_emit = True)

        self.addContainer(user_container)
        extruder_stack.setUserChanges(user_container)

        empty_variant = application.empty_variant_container
        empty_material = application.empty_material_container
        empty_quality = application.empty_quality_container

        if machine.variant.getId() not in ("empty", "empty_variant"):
            variant = machine.variant
        else:
            variant = empty_variant
        extruder_stack.variant = variant

        if machine.material.getId() not in ("empty", "empty_material"):
            material = machine.material
        else:
            material = empty_material
        extruder_stack.material = material

        if machine.quality.getId() not in ("empty", "empty_quality"):
            quality = machine.quality
        else:
            quality = empty_quality
        extruder_stack.quality = quality

        machine_quality_changes = machine.qualityChanges
        if new_global_quality_changes is not None:
            machine_quality_changes = new_global_quality_changes

        if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"):
            extruder_quality_changes_container = self.findInstanceContainers(name = machine_quality_changes.getName(), extruder = extruder_id)
            if extruder_quality_changes_container:
                extruder_quality_changes_container = extruder_quality_changes_container[0]

                quality_changes_id = extruder_quality_changes_container.getId()
                extruder_stack.qualityChanges = self.findInstanceContainers(id = quality_changes_id)[0]
            else:
                # Some extruder quality_changes containers can be created at runtime as files in the qualities
                # folder. Those files won't be loaded in the registry immediately. So we also need to search
                # the folder to see if the quality_changes exists.
                extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName())
                if extruder_quality_changes_container:
                    quality_changes_id = extruder_quality_changes_container.getId()
                    extruder_quality_changes_container.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
                    extruder_stack.qualityChanges = self.findInstanceContainers(id = quality_changes_id)[0]
                else:
                    # if we still cannot find a quality changes container for the extruder, create a new one
                    container_name = machine_quality_changes.getName()
                    container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name)
                    extruder_quality_changes_container = InstanceContainer(container_id, parent = application)
                    extruder_quality_changes_container.setName(container_name)
                    extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes")
                    extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
                    extruder_quality_changes_container.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
                    extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
                    extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId())

                    self.addContainer(extruder_quality_changes_container)
                    extruder_stack.qualityChanges = extruder_quality_changes_container

            if not extruder_quality_changes_container:
                Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]",
                           machine_quality_changes.getName(), extruder_stack.getId())
            else:
                # move all per-extruder settings to the extruder's quality changes
                for qc_setting_key in machine_quality_changes.getAllKeys():
                    settable_per_extruder = machine.getProperty(qc_setting_key, "settable_per_extruder")
                    if settable_per_extruder:
                        setting_value = machine_quality_changes.getProperty(qc_setting_key, "value")

                        setting_definition = machine.getSettingDefinition(qc_setting_key)
                        new_instance = SettingInstance(setting_definition, definition_changes)
                        new_instance.setProperty("value", setting_value)
                        new_instance.resetState()  # Ensure that the state is not seen as a user state.
                        extruder_quality_changes_container.addInstance(new_instance)
                        extruder_quality_changes_container.setDirty(True)

                        machine_quality_changes.removeInstance(qc_setting_key, postpone_emit=True)
        else:
            extruder_stack.qualityChanges = self.findInstanceContainers(id = "empty_quality_changes")[0]

        self.addContainer(extruder_stack)

        # Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have
        # per-extruder settings in the container for the machine instead of the extruder.
        if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"):
            quality_changes_machine_definition_id = machine_quality_changes.getDefinition().getId()
        else:
            whole_machine_definition = machine.definition
            machine_entry = machine.definition.getMetaDataEntry("machine")
            if machine_entry is not None:
                container_registry = ContainerRegistry.getInstance()
                whole_machine_definition = container_registry.findDefinitionContainers(id = machine_entry)[0]

            quality_changes_machine_definition_id = "fdmprinter"
            if whole_machine_definition.getMetaDataEntry("has_machine_quality"):
                quality_changes_machine_definition_id = machine.definition.getMetaDataEntry("quality_definition",
                                                                                            whole_machine_definition.getId())
        qcs = self.findInstanceContainers(type = "quality_changes", definition = quality_changes_machine_definition_id)
        qc_groups = {}  # map of qc names -> qc containers
        for qc in qcs:
            qc_name = qc.getName()
            if qc_name not in qc_groups:
                qc_groups[qc_name] = []
            qc_groups[qc_name].append(qc)
            # try to find from the quality changes cura directory too
            quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName())
            if quality_changes_container:
                qc_groups[qc_name].append(quality_changes_container)

        for qc_name, qc_list in qc_groups.items():
            qc_dict = {"global": None, "extruders": []}
            for qc in qc_list:
                extruder_position = qc.getMetaDataEntry("position")
                if extruder_position is not None:
                    qc_dict["extruders"].append(qc)
                else:
                    qc_dict["global"] = qc
            if qc_dict["global"] is not None and len(qc_dict["extruders"]) == 1:
                # move per-extruder settings
                for qc_setting_key in qc_dict["global"].getAllKeys():
                    settable_per_extruder = machine.getProperty(qc_setting_key, "settable_per_extruder")
                    if settable_per_extruder:
                        setting_value = qc_dict["global"].getProperty(qc_setting_key, "value")

                        setting_definition = machine.getSettingDefinition(qc_setting_key)
                        new_instance = SettingInstance(setting_definition, definition_changes)
                        new_instance.setProperty("value", setting_value)
                        new_instance.resetState()  # Ensure that the state is not seen as a user state.
                        qc_dict["extruders"][0].addInstance(new_instance)
                        qc_dict["extruders"][0].setDirty(True)

                        qc_dict["global"].removeInstance(qc_setting_key, postpone_emit=True)

        # Set next stack at the end
        extruder_stack.setNextStack(machine)

        return extruder_stack
Example #27
0
    def importProfile(self, file_name):
        Logger.log("d", "Attempting to import profile %s", file_name)
        if not file_name:
            return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "Invalid path")}

        plugin_registry = PluginRegistry.getInstance()
        extension = file_name.split(".")[-1]

        global_stack = Application.getInstance().getGlobalContainerStack()
        if not global_stack:
            return

        machine_extruders = []
        for position in sorted(global_stack.extruders):
            machine_extruders.append(global_stack.extruders[position])

        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            if meta_data["profile_reader"][0]["extension"] != extension:
                continue
            profile_reader = plugin_registry.getPluginObject(plugin_id)
            try:
                profile_or_list = profile_reader.read(file_name)  # Try to open the file with the profile reader.
            except NoProfileException:
                return { "status": "ok", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "No custom profile to import in file <filename>{0}</filename>", file_name)}
            except Exception as e:
                # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log("e", "Failed to import profile from %s: %s while using profile reader. Got exception %s", file_name, profile_reader.getPluginId(), str(e))
                return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "\n" + str(e))}

            if profile_or_list:
                # Ensure it is always a list of profiles
                if not isinstance(profile_or_list, list):
                    profile_or_list = [profile_or_list]

                # First check if this profile is suitable for this machine
                global_profile = None
                extruder_profiles = []
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                else:
                    for profile in profile_or_list:
                        if not profile.getMetaDataEntry("position"):
                            global_profile = profile
                        else:
                            extruder_profiles.append(profile)
                extruder_profiles = sorted(extruder_profiles, key = lambda x: int(x.getMetaDataEntry("position")))
                profile_or_list = [global_profile] + extruder_profiles

                if not global_profile:
                    Logger.log("e", "Incorrect profile [%s]. Could not find global profile", file_name)
                    return { "status": "error",
                             "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)}
                profile_definition = global_profile.getMetaDataEntry("definition")

                # Make sure we have a profile_definition in the file:
                if profile_definition is None:
                    break
                machine_definition = self.findDefinitionContainers(id = profile_definition)
                if not machine_definition:
                    Logger.log("e", "Incorrect profile [%s]. Unknown machine type [%s]", file_name, profile_definition)
                    return {"status": "error",
                            "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)
                            }
                machine_definition = machine_definition[0]

                # Get the expected machine definition.
                # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
                profile_definition = getMachineDefinitionIDForQualitySearch(machine_definition)
                expected_machine_definition = getMachineDefinitionIDForQualitySearch(global_stack.definition)

                # And check if the profile_definition matches either one (showing error if not):
                if profile_definition != expected_machine_definition:
                    Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name, profile_definition, expected_machine_definition)
                    return { "status": "error",
                             "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "The machine defined in profile <filename>{0}</filename> ({1}) doesn't match with your current machine ({2}), could not import it.", file_name, profile_definition, expected_machine_definition)}

                # Fix the global quality profile's definition field in case it's not correct
                global_profile.setMetaDataEntry("definition", expected_machine_definition)
                quality_name = global_profile.getName()
                quality_type = global_profile.getMetaDataEntry("quality_type")

                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                new_name = self.uniqueName(name_seed)

                # Ensure it is always a list of profiles
                if type(profile_or_list) is not list:
                    profile_or_list = [profile_or_list]

                # Make sure that there are also extruder stacks' quality_changes, not just one for the global stack
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                    extruder_profiles = []
                    for idx, extruder in enumerate(global_stack.extruders.values()):
                        profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
                        profile = InstanceContainer(profile_id)
                        profile.setName(quality_name)
                        profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
                        profile.addMetaDataEntry("type", "quality_changes")
                        profile.addMetaDataEntry("definition", expected_machine_definition)
                        profile.addMetaDataEntry("quality_type", quality_type)
                        profile.addMetaDataEntry("position", "0")
                        profile.setDirty(True)
                        if idx == 0:
                            # move all per-extruder settings to the first extruder's quality_changes
                            for qc_setting_key in global_profile.getAllKeys():
                                settable_per_extruder = global_stack.getProperty(qc_setting_key,
                                                                                           "settable_per_extruder")
                                if settable_per_extruder:
                                    setting_value = global_profile.getProperty(qc_setting_key, "value")

                                    setting_definition = global_stack.getSettingDefinition(qc_setting_key)
                                    new_instance = SettingInstance(setting_definition, profile)
                                    new_instance.setProperty("value", setting_value)
                                    new_instance.resetState()  # Ensure that the state is not seen as a user state.
                                    profile.addInstance(new_instance)
                                    profile.setDirty(True)

                                    global_profile.removeInstance(qc_setting_key, postpone_emit=True)
                        extruder_profiles.append(profile)

                    for profile in extruder_profiles:
                        profile_or_list.append(profile)

                # Import all profiles
                for profile_index, profile in enumerate(profile_or_list):
                    if profile_index == 0:
                        # This is assumed to be the global profile
                        profile_id = (global_stack.getBottom().getId() + "_" + name_seed).lower().replace(" ", "_")

                    elif profile_index < len(machine_extruders) + 1:
                        # This is assumed to be an extruder profile
                        extruder_id = machine_extruders[profile_index - 1].definition.getId()
                        extruder_position = str(profile_index - 1)
                        if not profile.getMetaDataEntry("position"):
                            profile.addMetaDataEntry("position", extruder_position)
                        else:
                            profile.setMetaDataEntry("position", extruder_position)
                        profile_id = (extruder_id + "_" + name_seed).lower().replace(" ", "_")

                    else: #More extruders in the imported file than in the machine.
                        continue #Delete the additional profiles.

                    result = self._configureProfile(profile, profile_id, new_name, expected_machine_definition)
                    if result is not None:
                        return {"status": "error", "message": catalog.i18nc(
                            "@info:status Don't translate the XML tags <filename> or <message>!",
                            "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                            file_name, result)}

                return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}

            # This message is throw when the profile reader doesn't find any profile in the file
            return {"status": "error", "message": catalog.i18nc("@info:status", "File {0} does not contain any valid profile.", file_name)}

        # If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)}
Example #28
0
    def importProfile(self, file_name):
        Logger.log("d", "Attempting to import profile %s", file_name)
        if not file_name:
            return {
                "status":
                "error",
                "message":
                catalog.i18nc(
                    "@info:status Don't translate the XML tags <filename> or <message>!",
                    "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                    file_name, "Invalid path")
            }

        plugin_registry = PluginRegistry.getInstance()
        extension = file_name.split(".")[-1]

        global_stack = Application.getInstance().getGlobalContainerStack()
        if not global_stack:
            return

        machine_extruders = []
        for position in sorted(global_stack.extruders):
            machine_extruders.append(global_stack.extruders[position])

        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            if meta_data["profile_reader"][0]["extension"] != extension:
                continue
            profile_reader = plugin_registry.getPluginObject(plugin_id)
            try:
                profile_or_list = profile_reader.read(
                    file_name)  # Try to open the file with the profile reader.
            except NoProfileException:
                return {
                    "status":
                    "ok",
                    "message":
                    catalog.i18nc(
                        "@info:status Don't translate the XML tags <filename> or <message>!",
                        "No custom profile to import in file <filename>{0}</filename>",
                        file_name)
                }
            except Exception as e:
                # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log(
                    "e",
                    "Failed to import profile from %s: %s while using profile reader. Got exception %s",
                    file_name, profile_reader.getPluginId(), str(e))
                return {
                    "status":
                    "error",
                    "message":
                    catalog.i18nc(
                        "@info:status Don't translate the XML tags <filename> or <message>!",
                        "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                        file_name, "\n" + str(e))
                }

            if profile_or_list:
                # Ensure it is always a list of profiles
                if not isinstance(profile_or_list, list):
                    profile_or_list = [profile_or_list]

                # First check if this profile is suitable for this machine
                global_profile = None
                extruder_profiles = []
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                else:
                    for profile in profile_or_list:
                        if not profile.getMetaDataEntry("position"):
                            global_profile = profile
                        else:
                            extruder_profiles.append(profile)
                extruder_profiles = sorted(
                    extruder_profiles,
                    key=lambda x: int(x.getMetaDataEntry("position")))
                profile_or_list = [global_profile] + extruder_profiles

                if not global_profile:
                    Logger.log(
                        "e",
                        "Incorrect profile [%s]. Could not find global profile",
                        file_name)
                    return {
                        "status":
                        "error",
                        "message":
                        catalog.i18nc(
                            "@info:status Don't translate the XML tags <filename> or <message>!",
                            "This profile <filename>{0}</filename> contains incorrect data, could not import it.",
                            file_name)
                    }
                profile_definition = global_profile.getMetaDataEntry(
                    "definition")

                # Make sure we have a profile_definition in the file:
                if profile_definition is None:
                    break
                machine_definition = self.findDefinitionContainers(
                    id=profile_definition)
                if not machine_definition:
                    Logger.log(
                        "e",
                        "Incorrect profile [%s]. Unknown machine type [%s]",
                        file_name, profile_definition)
                    return {
                        "status":
                        "error",
                        "message":
                        catalog.i18nc(
                            "@info:status Don't translate the XML tags <filename> or <message>!",
                            "This profile <filename>{0}</filename> contains incorrect data, could not import it.",
                            file_name)
                    }
                machine_definition = machine_definition[0]

                # Get the expected machine definition.
                # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
                profile_definition = getMachineDefinitionIDForQualitySearch(
                    machine_definition)
                expected_machine_definition = getMachineDefinitionIDForQualitySearch(
                    global_stack.definition)

                # And check if the profile_definition matches either one (showing error if not):
                if profile_definition != expected_machine_definition:
                    Logger.log(
                        "e",
                        "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile",
                        file_name, profile_definition,
                        expected_machine_definition)
                    return {
                        "status":
                        "error",
                        "message":
                        catalog.i18nc(
                            "@info:status Don't translate the XML tags <filename> or <message>!",
                            "The machine defined in profile <filename>{0}</filename> ({1}) doesn't match with your current machine ({2}), could not import it.",
                            file_name, profile_definition,
                            expected_machine_definition)
                    }

                # Fix the global quality profile's definition field in case it's not correct
                global_profile.setMetaDataEntry("definition",
                                                expected_machine_definition)
                quality_name = global_profile.getName()
                quality_type = global_profile.getMetaDataEntry("quality_type")

                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                new_name = self.uniqueName(name_seed)

                # Ensure it is always a list of profiles
                if type(profile_or_list) is not list:
                    profile_or_list = [profile_or_list]

                # Make sure that there are also extruder stacks' quality_changes, not just one for the global stack
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                    extruder_profiles = []
                    for idx, extruder in enumerate(
                            global_stack.extruders.values()):
                        profile_id = ContainerRegistry.getInstance(
                        ).uniqueName(global_stack.getId() + "_extruder_" +
                                     str(idx + 1))
                        profile = InstanceContainer(profile_id)
                        profile.setName(quality_name)
                        profile.addMetaDataEntry(
                            "setting_version", CuraApplication.SettingVersion)
                        profile.addMetaDataEntry("type", "quality_changes")
                        profile.addMetaDataEntry("definition",
                                                 expected_machine_definition)
                        profile.addMetaDataEntry("quality_type", quality_type)
                        profile.addMetaDataEntry("position", "0")
                        profile.setDirty(True)
                        if idx == 0:
                            # move all per-extruder settings to the first extruder's quality_changes
                            for qc_setting_key in global_profile.getAllKeys():
                                settable_per_extruder = global_stack.getProperty(
                                    qc_setting_key, "settable_per_extruder")
                                if settable_per_extruder:
                                    setting_value = global_profile.getProperty(
                                        qc_setting_key, "value")

                                    setting_definition = global_stack.getSettingDefinition(
                                        qc_setting_key)
                                    new_instance = SettingInstance(
                                        setting_definition, profile)
                                    new_instance.setProperty(
                                        "value", setting_value)
                                    new_instance.resetState(
                                    )  # Ensure that the state is not seen as a user state.
                                    profile.addInstance(new_instance)
                                    profile.setDirty(True)

                                    global_profile.removeInstance(
                                        qc_setting_key, postpone_emit=True)
                        extruder_profiles.append(profile)

                    for profile in extruder_profiles:
                        profile_or_list.append(profile)

                # Import all profiles
                for profile_index, profile in enumerate(profile_or_list):
                    if profile_index == 0:
                        # This is assumed to be the global profile
                        profile_id = (global_stack.getBottom().getId() + "_" +
                                      name_seed).lower().replace(" ", "_")

                    elif profile_index < len(machine_extruders) + 1:
                        # This is assumed to be an extruder profile
                        extruder_id = machine_extruders[profile_index -
                                                        1].definition.getId()
                        extruder_position = str(profile_index - 1)
                        if not profile.getMetaDataEntry("position"):
                            profile.addMetaDataEntry("position",
                                                     extruder_position)
                        else:
                            profile.setMetaDataEntry("position",
                                                     extruder_position)
                        profile_id = (extruder_id + "_" +
                                      name_seed).lower().replace(" ", "_")

                    else:  #More extruders in the imported file than in the machine.
                        continue  #Delete the additional profiles.

                    result = self._configureProfile(
                        profile, profile_id, new_name,
                        expected_machine_definition)
                    if result is not None:
                        return {
                            "status":
                            "error",
                            "message":
                            catalog.i18nc(
                                "@info:status Don't translate the XML tags <filename> or <message>!",
                                "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                                file_name, result)
                        }

                return {
                    "status":
                    "ok",
                    "message":
                    catalog.i18nc("@info:status",
                                  "Successfully imported profile {0}",
                                  profile_or_list[0].getName())
                }

            # This message is throw when the profile reader doesn't find any profile in the file
            return {
                "status":
                "error",
                "message":
                catalog.i18nc("@info:status",
                              "File {0} does not contain any valid profile.",
                              file_name)
            }

        # If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {
            "status":
            "error",
            "message":
            catalog.i18nc(
                "@info:status",
                "Profile {0} has an unknown file type or is corrupted.",
                file_name)
        }
Example #29
0
def getInstanceContainer(container_type) -> InstanceContainer:
    container = InstanceContainer(container_id = "InstanceContainer")
    container.addMetaDataEntry("type", container_type)
    return container
Example #30
0
    def _configureProfile(self, profile: InstanceContainer, id_seed: str,
                          new_name: str) -> Optional[str]:
        profile.setDirty(True)  # Ensure the profiles are correctly saved

        new_id = self.createUniqueName(
            "quality_changes", "", id_seed,
            catalog.i18nc("@label", "Custom profile"))
        profile._id = new_id
        profile.setName(new_name)

        if "type" in profile.getMetaData():
            profile.setMetaDataEntry("type", "quality_changes")
        else:
            profile.addMetaDataEntry("type", "quality_changes")

        quality_type = profile.getMetaDataEntry("quality_type")
        if not quality_type:
            return catalog.i18nc("@info:status",
                                 "Profile is missing a quality type.")

        quality_type_criteria = {"quality_type": quality_type}
        if self._machineHasOwnQualities():
            profile.setDefinition(self._activeQualityDefinition().getId())
            if self._machineHasOwnMaterials():
                active_material_id = self._activeMaterialId()
                if active_material_id and active_material_id != "empty":  # only update if there is an active material
                    profile.addMetaDataEntry("material", active_material_id)
                    quality_type_criteria["material"] = active_material_id

            quality_type_criteria["definition"] = profile.getDefinition(
            ).getId()

        else:
            profile.setDefinition(fdmprinter)
            quality_type_criteria["definition"] = "fdmprinter"

        machine_definition = Application.getInstance().getGlobalContainerStack(
        ).getBottom()
        del quality_type_criteria["definition"]

        # materials = None

        if "material" in quality_type_criteria:
            # materials = ContainerRegistry.getInstance().findInstanceContainers(id = quality_type_criteria["material"])
            del quality_type_criteria["material"]

        # Do not filter quality containers here with materials because we are trying to import a profile, so it should
        # NOT be restricted by the active materials on the current machine.
        materials = None

        # Check to make sure the imported profile actually makes sense in context of the current configuration.
        # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as
        # successfully imported but then fail to show up.
        from cura.QualityManager import QualityManager
        qualities = QualityManager.getInstance(
        )._getFilteredContainersForStack(machine_definition, materials,
                                         **quality_type_criteria)
        if not qualities:
            return catalog.i18nc(
                "@info:status",
                "Could not find a quality type {0} for the current configuration.",
                quality_type)

        ContainerRegistry.getInstance().addContainer(profile)

        return None
Example #31
0
def getInstanceContainer(container_type) -> InstanceContainer:
    container = InstanceContainer(container_id = "InstanceContainer")
    container.addMetaDataEntry("type", container_type)
    return container
Example #32
0
    def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id):
        new_extruder_id = extruder_id

        extruder_definitions = self.findDefinitionContainers(
            id=new_extruder_id)
        if not extruder_definitions:
            Logger.log("w",
                       "Could not find definition containers for extruder %s",
                       new_extruder_id)
            return

        extruder_definition = extruder_definitions[0]
        unique_name = self.uniqueName(machine.getName() + " " +
                                      new_extruder_id)

        extruder_stack = ExtruderStack.ExtruderStack(unique_name)
        extruder_stack.setName(extruder_definition.getName())
        extruder_stack.setDefinition(extruder_definition)
        extruder_stack.addMetaDataEntry(
            "position", extruder_definition.getMetaDataEntry("position"))
        extruder_stack.setNextStack(machine)

        # create empty user changes container otherwise
        user_container = InstanceContainer(extruder_stack.id + "_user")
        user_container.addMetaDataEntry("type", "user")
        user_container.addMetaDataEntry("machine", extruder_stack.getId())
        from cura.CuraApplication import CuraApplication
        user_container.addMetaDataEntry("setting_version",
                                        CuraApplication.SettingVersion)
        user_container.setDefinition(machine.definition)

        if machine.userChanges:
            # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes
            # container to the extruder stack.
            for user_setting_key in machine.userChanges.getAllKeys():
                settable_per_extruder = machine.getProperty(
                    user_setting_key, "settable_per_extruder")
                if settable_per_extruder:
                    user_container.addInstance(
                        machine.userChanges.getInstance(user_setting_key))
                    machine.userChanges.removeInstance(user_setting_key,
                                                       postpone_emit=True)

        extruder_stack.setUserChanges(user_container)
        self.addContainer(user_container)

        variant_id = "default"
        if machine.variant.getId() not in ("empty", "empty_variant"):
            variant_id = machine.variant.getId()
        else:
            variant_id = "empty_variant"
        extruder_stack.setVariantById(variant_id)

        material_id = "default"
        if machine.material.getId() not in ("empty", "empty_material"):
            material_id = machine.material.getId()
        else:
            material_id = "empty_material"
        extruder_stack.setMaterialById(material_id)

        quality_id = "default"
        if machine.quality.getId() not in ("empty", "empty_quality"):
            quality_id = machine.quality.getId()
        else:
            quality_id = "empty_quality"
        extruder_stack.setQualityById(quality_id)

        if machine.qualityChanges.getId() not in ("empty",
                                                  "empty_quality_changes"):
            extruder_quality_changes_container = self.findInstanceContainers(
                name=machine.qualityChanges.getName(), extruder=extruder_id)
            if extruder_quality_changes_container:
                extruder_quality_changes_container = extruder_quality_changes_container[
                    0]
                quality_changes_id = extruder_quality_changes_container.getId()
                extruder_stack.setQualityChangesById(quality_changes_id)
            else:
                # Some extruder quality_changes containers can be created at runtime as files in the qualities
                # folder. Those files won't be loaded in the registry immediately. So we also need to search
                # the folder to see if the quality_changes exists.
                extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(
                    machine.qualityChanges.getName())
                if extruder_quality_changes_container:
                    quality_changes_id = extruder_quality_changes_container.getId(
                    )
                    extruder_stack.setQualityChangesById(quality_changes_id)

            if not extruder_quality_changes_container:
                Logger.log(
                    "w",
                    "Could not find quality_changes named [%s] for extruder [%s]",
                    machine.qualityChanges.getName(), extruder_stack.getId())
        else:
            extruder_stack.setQualityChangesById("empty_quality_changes")

        self.addContainer(extruder_stack)

        return extruder_stack
Example #33
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)
Example #34
0
File: Script.py Project: CPS-3/Cura
class Script:
    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"])
                try:
                    self._definition.deserialize(json.dumps(setting_data))
                    ContainerRegistry.getInstance().addContainer(self._definition)
                except ContainerFormatError:
                    self._definition = None
                    return
        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)

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

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

            # Property changed: trigger reslice
            # To do this we use the global container stack propertyChanged.
            # Reslicing is necessary for setting changes in this plugin, because the changes
            # are applied only once per "fresh" gcode
            global_container_stack = Application.getInstance().getGlobalContainerStack()
            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):
        setting_data = self.getSettingDataString()
        if type(setting_data) == str:
            setting_data = json.loads(setting_data, object_pairs_hook = collections.OrderedDict)
        return setting_data

    def getSettingDataString(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

    ##  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 = "", **kwargs):
        #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):
        raise NotImplementedError()