Example #1
0
    def _findQualityChangesContainerInCuraFolder(self, name):
        quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityInstanceContainer)

        instance_container = None

        for item in os.listdir(quality_changes_dir):
            file_path = os.path.join(quality_changes_dir, item)
            if not os.path.isfile(file_path):
                continue

            parser = configparser.ConfigParser()
            try:
                parser.read([file_path])
            except:
                # skip, it is not a valid stack file
                continue

            if not parser.has_option("general", "name"):
                continue

            if parser["general"]["name"] == name:
                # load the container
                container_id = os.path.basename(file_path).replace(".inst.cfg", "")

                instance_container = InstanceContainer(container_id)
                with open(file_path, "r") as f:
                    serialized = f.read()
                instance_container.deserialize(serialized, file_path)
                self.addContainer(instance_container)
                break

        return instance_container
Example #2
0
def definition_changes_container() -> InstanceContainer:
    definition_changes_container = InstanceContainer(container_id = "Test Definition Changes")
    definition_changes_container.setMetaDataEntry("type", "definition_changes")
    # Add current setting version to the instance container
    from cura.CuraApplication import CuraApplication
    definition_changes_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
    return definition_changes_container
Example #3
0
    def findAllQualitiesForMachineMaterial(self, machine_definition: "DefinitionContainerInterface", material_container: InstanceContainer) -> List[InstanceContainer]:
        criteria = {"type": "quality"}
        result = self._getFilteredContainersForStack(machine_definition, [material_container.getMetaData()], **criteria)
        if not result:
            basic_materials = self._getBasicMaterialMetadatas(material_container.getMetaData())
            if basic_materials:
                result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria)

        return result
    def _updateMetaData(self, container: InstanceContainer) -> None:
        index = self.find("id", container.id)

        if self._section_property:
            self.setProperty(index, "section", container.getMetaDataEntry(self._section_property, ""))

        self.setProperty(index, "metadata", container.getMetaData())
        self.setProperty(index, "name", container.getName())
        self.setProperty(index, "id", container.getId())
Example #5
0
def readQualityProfileFromString(profile_string):
    # Create an empty profile - the id and name will be changed by the ContainerRegistry
    profile = InstanceContainer("")
    try:
        profile.deserialize(profile_string)
    except Exception as e:  # Not a valid g-code file.
        Logger.log("e", "Unable to serialise the profile: %s", str(e))
        return None
    return profile
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 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 #7
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 #8
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 #9
0
 def _loadProfile(self, serialized, profile_id):
     # Create an empty profile.
     profile = InstanceContainer(profile_id)
     profile.setMetaDataEntry("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 #10
0
def test_findInstanceContainers(container_registry, data):
    for container in data["containers"]: # Fill the registry with mock containers.
        container = container.copy()
        container_id = container["id"]
        del container["id"]
        instance_container = InstanceContainer(container_id)
        for key, value in container.items(): # Copy data into metadata.
            instance_container.getMetaData()[key] = value
        container_registry.addContainer(instance_container)

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

    _verifyMetaDataMatches(results, data["result"])
Example #11
0
    def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
        flat_container = InstanceContainer(instance_container2.getName())
        flat_container.setDefinition(instance_container2.getDefinition())
        flat_container.setMetaData(instance_container2.getMetaData())

        for key in instance_container2.getAllKeys():
            flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value"))

        for key in instance_container1.getAllKeys():
            flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value"))
        return flat_container
Example #12
0
    def _onContainerChanged(self, container: InstanceContainer) -> None:
        container_type = container.getMetaDataEntry("type")
        if container_type not in ("quality", "quality_changes"):
            return

        # update the cache table
        self._update_timer.start()
Example #13
0
    def __init__(self):
        super().__init__()
        self._settings = None
        self._stack = None

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


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

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

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

        ContainerRegistry.getInstance().addContainer(self._stack)
Example #15
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 #16
0
    def _getBasicMaterials(self, material_container: InstanceContainer):
        base_material = material_container.getMetaDataEntry("material")
        material_container_definition = material_container.getDefinition()
        if material_container_definition and material_container_definition.getMetaDataEntry("has_machine_quality"):
            definition_id = material_container.getDefinition().getMetaDataEntry("quality_definition", material_container.getDefinition().getId())
        else:
            definition_id = "fdmprinter"
        if base_material:
            # There is a basic material specified
            criteria = { "type": "material", "name": base_material, "definition": definition_id }
            containers = ContainerRegistry.getInstance().findInstanceContainers(**criteria)
            containers = [basic_material for basic_material in containers if
                               basic_material.getMetaDataEntry("variant") == material_container.getMetaDataEntry(
                                   "variant")]
            return containers

        return []
Example #17
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 #18
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 #19
0
    def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
        flat_container = InstanceContainer(instance_container2.getName())

        # The metadata includes id, name and definition
        flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData()))

        if instance_container1.getDefinition():
            flat_container.setDefinition(instance_container1.getDefinition().getId())

        for key in instance_container2.getAllKeys():
            flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value"))

        for key in instance_container1.getAllKeys():
            flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value"))

        return flat_container
Example #20
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 #21
0
    def renameQualityContainer(self, container_id, new_name):
        containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id, type = "quality")
        if containers:
            new_name = self._createUniqueName("quality", containers[0].getName(), new_name,
                                              catalog.i18nc("@label", "Custom profile"))

            if containers[0].getName() == new_name:
                # Nothing to do.
                return

            # As we also want the id of the container to be changed (so that profile name is the name of the file
            # on disk. We need to create a new instance and remove it (so the old file of the container is removed)
            # If we don't do that, we might get duplicates & other weird issues.
            new_container = InstanceContainer("")
            new_container.deserialize(containers[0].serialize())

            # Actually set the name
            new_container.setName(new_name)
            new_container._id = new_name  # Todo: Fix proper id change function for this.

            # Add the "new" container.
            UM.Settings.ContainerRegistry.getInstance().addContainer(new_container)

            # Ensure that the renamed profile is saved -before- we remove the old profile.
            Application.getInstance().saveSettings()

            # Actually set & remove new / old quality.
            self.setActiveQuality(new_name)
            self.removeQualityContainer(containers[0].getId())
Example #22
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 and name will be changed by the ContainerRegistry
        profile = InstanceContainer("")
        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

        profile.addMetaDataEntry("type", "quality")

        return profile
Example #23
0
    def _findQualityChangesContainerInCuraFolder(self, name):
        quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityChangesInstanceContainer)

        instance_container = None

        for item in os.listdir(quality_changes_dir):
            file_path = os.path.join(quality_changes_dir, item)
            if not os.path.isfile(file_path):
                continue

            parser = configparser.ConfigParser(interpolation=None)
            try:
                parser.read([file_path])
            except:
                # skip, it is not a valid stack file
                continue

            if not parser.has_option("general", "name"):
                continue

            if parser["general"]["name"] == name:
                # load the container
                container_id = os.path.basename(file_path).replace(".inst.cfg", "")
                if self.findInstanceContainers(id = container_id):
                    # this container is already in the registry, skip it
                    continue

                instance_container = InstanceContainer(container_id)
                with open(file_path, "r", encoding = "utf-8") as f:
                    serialized = f.read()
                try:
                    instance_container.deserialize(serialized, file_path)
                except ContainerFormatError:
                    Logger.logException("e", "Unable to deserialize InstanceContainer %s", file_path)
                    continue
                self.addContainer(instance_container)
                break

        return instance_container
Example #24
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 #25
0
    def _performMerge(self, merge_into: InstanceContainer, merge: InstanceContainer, clear_settings: bool = True) -> None:
        if merge == merge_into:
            return

        for key in merge.getAllKeys():
            merge_into.setProperty(key, "value", merge.getProperty(key, "value"))

        if clear_settings:
            merge.clear()
Example #26
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 #27
0
    def duplicateContainer(self, container_id):
        if not self._active_container_stack:
            return ""
        containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
        if containers:
            new_name = self._createUniqueName("quality", "", containers[0].getName(), catalog.i18nc("@label", "Custom profile"))

            new_container = InstanceContainer("")

            ## Copy all values
            new_container.deserialize(containers[0].serialize())

            new_container.setReadOnly(False)
            new_container.setName(new_name)
            new_container._id = new_name
            UM.Settings.ContainerRegistry.getInstance().addContainer(new_container)
            return new_name

        return ""
Example #28
0
    def _serialiseSettings(self, settings):
        prefix = ";SETTING_" + str(GCodeWriter.version) + " "  # The prefix to put before each line.
        prefix_length = len(prefix)

        all_settings = InstanceContainer("G-code-imported-profile") #Create a new 'profile' with ALL settings so that the slice can be precisely reproduced.
        all_settings.setDefinition(settings.getBottom())
        for key in settings.getAllKeys():
            all_settings.setProperty(key, "value", settings.getProperty(key, "value")) #Just copy everything over to the setting instance.
        serialised = all_settings.serialize()

        # Escape characters that have a special meaning in g-code comments.
        pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))
        # Perform the replacement with a regular expression.
        serialised = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialised)

        # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
        result = ""

        # Lines have 80 characters, so the payload of each line is 80 - prefix.
        for pos in range(0, len(serialised), 80 - prefix_length):
            result += prefix + serialised[pos : pos + 80 - prefix_length] + "\n"
        serialised = result

        return serialised
Example #29
0
def quality_changes_container():
    container = InstanceContainer(container_id="quality changes container")
    container.setMetaDataEntry("type", "quality_changes")

    return container
Example #30
0
    def _createQualityChanges(
            self, quality_type: str, new_name: str, machine: "GlobalStack",
            extruder_stack: Optional["ExtruderStack"]) -> "InstanceContainer":
        base_id = machine.definition.getId(
        ) if extruder_stack is None else extruder_stack.getId()
        new_id = base_id + "_" + new_name
        new_id = new_id.lower().replace(" ", "_")
        new_id = self._container_registry.uniqueName(new_id)

        # Create a new quality_changes container for the quality.
        quality_changes = InstanceContainer(new_id)
        quality_changes.setName(new_name)
        quality_changes.setMetaDataEntry("type", "quality_changes")
        quality_changes.setMetaDataEntry("quality_type", quality_type)

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

        # If the machine specifies qualities should be filtered, ensure we match the current criteria.
        machine_definition_id = getMachineDefinitionIDForQualitySearch(
            machine.definition)
        quality_changes.setDefinition(machine_definition_id)

        quality_changes.setMetaDataEntry("setting_version",
                                         self._application.SettingVersion)
        return quality_changes
def test_valueChanges(container_registry):
    setting_property_provider = SettingPropertyProvider()
    # First create the basics; A container stack that will hold 2 instance containers and a single definition container
    container_stack = ContainerStack("test!")
    instance_container = InstanceContainer("data")
    second_instance_container = InstanceContainer("data2")
    definition_container = DefinitionContainer("foo")

    # Create a setting definition for our one and only setting!
    setting_definition = SettingDefinition("test_setting")
    setting_definition._deserialize_dict({
        "value": 10,
        "label": "blorp",
        "type": "float",
        "description": "nah",
        "maximum_value": 23,
        "maximum_value_warning": 21
    })
    definition_container.addDefinition(setting_definition)

    # Make a single setting instance for our one and only setting.
    setting_instance = SettingInstance(setting_definition, instance_container)
    setting_instance.setProperty("value", 20)
    setting_instance.setProperty("enabled", False)
    instance_container.addInstance(setting_instance)

    # Make a setting instance that lives in our second instance_container
    setting_instance2 = SettingInstance(setting_definition,
                                        second_instance_container)
    second_instance_container.addInstance(setting_instance2)

    # Now that both containers are done, actually add them.
    container_stack.addContainer(definition_container)
    container_stack.addContainer(second_instance_container)
    container_stack.addContainer(instance_container)

    setting_property_provider.setContainerStack(container_stack)
    setting_property_provider.setKey("test_setting")

    assert setting_property_provider.getPropertyValue("value", 0) == 20

    setting_property_provider.setWatchedProperties(
        ["enabled", "value", "validationState"])
    assert setting_property_provider.properties.value("enabled") == "False"
    assert setting_property_provider.properties.value("value") == "20"

    # Validator state is a special property that gets added based on the type and the value
    assert setting_property_provider.properties.value(
        "validationState") == str(ValidatorState.Valid)

    # Set the property value to something that will trigger a warning
    setting_property_provider.setPropertyValue("value", 22)
    assert setting_property_provider.properties.value(
        "validationState") == str(ValidatorState.MaximumWarning)
    assert setting_property_provider.getPropertyValue("value", 0) == 22
    # The setting doesn't exist in our second instance container, so this should be None
    assert setting_property_provider.getPropertyValue("value", 1) is None

    # Set the property value to something that will trigger an error
    setting_property_provider.setPropertyValue("value", 25)
    # The Qtimer that we use to prevent flooding doesn't work in tests, so tickle the change manually.
    setting_property_provider._update()

    assert setting_property_provider.properties.value(
        "validationState") == str(ValidatorState.MaximumError)

    # We now ask for the second instance container to be targeted
    setting_property_provider.setStoreIndex(1)
    assert setting_property_provider.storeIndex == 1

    setting_property_provider.setPropertyValue("value", 2)
    setting_property_provider._update()
    # So now we should see a change in that instance container
    assert setting_property_provider.getPropertyValue("value", 1) == 2
    # But not if we ask the provider, because the container above it still has a 25 as value!
    assert setting_property_provider.getPropertyValue("value", 0) == 25

    assert setting_property_provider.stackLevels == [0, 1, 2]

    # We're asking for an index that doesn't exist.
    assert setting_property_provider.getPropertyValue("value", 2000) is None

    # The value is used, so the property must be true
    assert setting_property_provider.isValueUsed

    # Try to remove the setting from the container
    setting_property_provider.removeFromContainer(0)
    assert setting_property_provider.getPropertyValue("value", 0) is None

    # Ensure that a weird index doesn't break
    setting_property_provider.removeFromContainer(90001)
Example #32
0
def global_variant():
    container = InstanceContainer(container_id="global_variant")
    container.setMetaDataEntry("type", "variant")

    return container
    def _serialiseSettings(self, stack):
        container_registry = self._application.getContainerRegistry()

        prefix = self._setting_keyword + str(
            GCodeWriter.version) + " "  # The prefix to put before each line.
        prefix_length = len(prefix)

        quality_type = stack.quality.getMetaDataEntry("quality_type")
        container_with_profile = stack.qualityChanges
        machine_definition_id_for_quality = ContainerTree.getInstance(
        ).machines[stack.definition.getId()].quality_definition
        if container_with_profile.getId() == "empty_quality_changes":
            # If the global quality changes is empty, create a new one
            quality_name = container_registry.uniqueName(
                stack.quality.getName())
            quality_id = container_registry.uniqueName(
                (stack.definition.getId() + "_" +
                 quality_name).lower().replace(" ", "_"))
            container_with_profile = InstanceContainer(quality_id)
            container_with_profile.setName(quality_name)
            container_with_profile.setMetaDataEntry("type", "quality_changes")
            container_with_profile.setMetaDataEntry("quality_type",
                                                    quality_type)
            if stack.getMetaDataEntry(
                    "position"
            ) is not None:  # For extruder stacks, the quality changes should include an intent category.
                container_with_profile.setMetaDataEntry(
                    "intent_category",
                    stack.intent.getMetaDataEntry("intent_category",
                                                  "default"))
            container_with_profile.setDefinition(
                machine_definition_id_for_quality)

        flat_global_container = self._createFlattenedContainerInstance(
            stack.userChanges, container_with_profile)
        # If the quality changes is not set, we need to set type manually
        if flat_global_container.getMetaDataEntry("type", None) is None:
            flat_global_container.setMetaDataEntry("type", "quality_changes")

        # Ensure that quality_type is set. (Can happen if we have empty quality changes).
        if flat_global_container.getMetaDataEntry("quality_type",
                                                  None) is None:
            flat_global_container.setMetaDataEntry(
                "quality_type",
                stack.quality.getMetaDataEntry("quality_type", "normal"))

        # Get the machine definition ID for quality profiles
        flat_global_container.setMetaDataEntry(
            "definition", machine_definition_id_for_quality)

        serialized = flat_global_container.serialize()
        data = {"global_quality": serialized}

        all_setting_keys = flat_global_container.getAllKeys()
        for extruder in sorted(
                stack.extruders.values(),
                key=lambda k: int(k.getMetaDataEntry("position"))):
            extruder_quality = extruder.qualityChanges
            if extruder_quality.getId() == "empty_quality_changes":
                # Same story, if quality changes is empty, create a new one
                quality_name = container_registry.uniqueName(
                    stack.quality.getName())
                quality_id = container_registry.uniqueName(
                    (stack.definition.getId() + "_" +
                     quality_name).lower().replace(" ", "_"))
                extruder_quality = InstanceContainer(quality_id)
                extruder_quality.setName(quality_name)
                extruder_quality.setMetaDataEntry("type", "quality_changes")
                extruder_quality.setMetaDataEntry("quality_type", quality_type)
                extruder_quality.setDefinition(
                    machine_definition_id_for_quality)

            flat_extruder_quality = self._createFlattenedContainerInstance(
                extruder.userChanges, extruder_quality)
            # If the quality changes is not set, we need to set type manually
            if flat_extruder_quality.getMetaDataEntry("type", None) is None:
                flat_extruder_quality.setMetaDataEntry("type",
                                                       "quality_changes")

            # Ensure that extruder is set. (Can happen if we have empty quality changes).
            if flat_extruder_quality.getMetaDataEntry("position",
                                                      None) is None:
                flat_extruder_quality.setMetaDataEntry(
                    "position", extruder.getMetaDataEntry("position"))

            # Ensure that quality_type is set. (Can happen if we have empty quality changes).
            if flat_extruder_quality.getMetaDataEntry("quality_type",
                                                      None) is None:
                flat_extruder_quality.setMetaDataEntry(
                    "quality_type",
                    extruder.quality.getMetaDataEntry("quality_type",
                                                      "normal"))

            # Change the default definition
            flat_extruder_quality.setMetaDataEntry(
                "definition", machine_definition_id_for_quality)

            extruder_serialized = flat_extruder_quality.serialize()
            data.setdefault("extruder_quality", []).append(extruder_serialized)

            all_setting_keys.update(flat_extruder_quality.getAllKeys())

        # Check if there is any profiles
        if not all_setting_keys:
            Logger.log(
                "i",
                "No custom settings found, not writing settings to g-code.")
            return ""

        json_string = json.dumps(data)

        # Escape characters that have a special meaning in g-code comments.
        pattern = re.compile("|".join(GCodeWriter.escape_characters.keys()))

        # Perform the replacement with a regular expression.
        escaped_string = pattern.sub(
            lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))],
            json_string)

        # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix.
        result = ""

        # Lines have 80 characters, so the payload of each line is 80 - prefix.
        for pos in range(0, len(escaped_string), 80 - prefix_length):
            result += prefix + escaped_string[pos:pos + 80 -
                                              prefix_length] + "\n"
        return result
Example #34
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

    ##  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 #35
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.setMetaDataEntry("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 = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
        if global_stack is None:
            return None
        definition_id = ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_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_group_dict = ContainerTree.getInstance().getCurrentQualityGroups()
        # "not_supported" profiles can be imported.
        if quality_type != empty_quality_container.getMetaDataEntry("quality_type") and 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 #36
0
    def preRead(self, file_name):
        self._3mf_mesh_reader = Application.getInstance().getMeshFileHandler(
        ).getReaderForFile(file_name)
        if self._3mf_mesh_reader and self._3mf_mesh_reader.preRead(
                file_name) == WorkspaceReader.PreReadResult.accepted:
            pass
        else:
            Logger.log(
                "w",
                "Could not find reader that was able to read the scene data for 3MF workspace"
            )
            return WorkspaceReader.PreReadResult.failed

        machine_name = ""
        machine_type = ""
        variant_type_name = i18n_catalog.i18nc("@label", "Nozzle")

        num_extruders = 0
        # Check if there are any conflicts, so we can ask the user.
        archive = zipfile.ZipFile(file_name, "r")
        cura_file_names = [
            name for name in archive.namelist() if name.startswith("Cura/")
        ]
        container_stack_files = [
            name for name in cura_file_names
            if name.endswith(self._container_stack_suffix)
        ]
        self._resolve_strategies = {
            "machine": None,
            "quality_changes": None,
            "material": None
        }
        machine_conflict = False
        quality_changes_conflict = False
        for container_stack_file in container_stack_files:
            container_id = self._stripFileToId(container_stack_file)
            serialized = archive.open(container_stack_file).read().decode(
                "utf-8")
            if machine_name == "":
                machine_name = self._getMachineNameFromSerializedStack(
                    serialized)
            stacks = self._container_registry.findContainerStacks(
                id=container_id)
            if stacks:
                # Check if there are any changes at all in any of the container stacks.
                id_list = self._getContainerIdListFromSerialized(serialized)
                for index, container_id in enumerate(id_list):
                    if stacks[0].getContainer(index).getId() != container_id:
                        machine_conflict = True
            Job.yieldThread()

        definition_container_files = [
            name for name in cura_file_names
            if name.endswith(self._definition_container_suffix)
        ]
        for definition_container_file in definition_container_files:
            container_id = self._stripFileToId(definition_container_file)
            definitions = self._container_registry.findDefinitionContainers(
                id=container_id)

            if not definitions:
                definition_container = DefinitionContainer(container_id)
                definition_container.deserialize(
                    archive.open(definition_container_file).read().decode(
                        "utf-8"))

            else:
                definition_container = definitions[0]

            if definition_container.getMetaDataEntry("type") != "extruder":
                machine_type = definition_container.getName()
                variant_type_name = definition_container.getMetaDataEntry(
                    "variants_name", variant_type_name)
            else:
                num_extruders += 1
            Job.yieldThread()

        if num_extruders == 0:
            num_extruders = 1  # No extruder stacks found, which means there is one extruder

        extruders = num_extruders * [""]

        material_labels = []
        material_conflict = False
        xml_material_profile = self._getXmlProfileClass()
        if self._material_container_suffix is None:
            self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(
                xml_material_profile).preferredSuffix
        if xml_material_profile:
            material_container_files = [
                name for name in cura_file_names
                if name.endswith(self._material_container_suffix)
            ]
            for material_container_file in material_container_files:
                container_id = self._stripFileToId(material_container_file)
                materials = self._container_registry.findInstanceContainers(
                    id=container_id)
                material_labels.append(
                    self._getMaterialLabelFromSerialized(
                        archive.open(material_container_file).read().decode(
                            "utf-8")))
                if materials and not materials[0].isReadOnly(
                ):  # Only non readonly materials can be in conflict
                    material_conflict = True
                Job.yieldThread()
        # Check if any quality_changes instance container is in conflict.
        instance_container_files = [
            name for name in cura_file_names
            if name.endswith(self._instance_container_suffix)
        ]
        quality_name = ""
        quality_type = ""
        num_settings_overriden_by_quality_changes = 0  # How many settings are changed by the quality changes
        num_user_settings = 0
        for instance_container_file in instance_container_files:
            container_id = self._stripFileToId(instance_container_file)
            instance_container = InstanceContainer(container_id)

            # Deserialize InstanceContainer by converting read data from bytes to string
            instance_container.deserialize(
                archive.open(instance_container_file).read().decode("utf-8"))
            container_type = instance_container.getMetaDataEntry("type")
            if container_type == "quality_changes":
                quality_name = instance_container.getName()
                num_settings_overriden_by_quality_changes += len(
                    instance_container._instances)
                # Check if quality changes already exists.
                quality_changes = self._container_registry.findInstanceContainers(
                    id=container_id)
                if quality_changes:
                    # Check if there really is a conflict by comparing the values
                    if quality_changes[0] != instance_container:
                        quality_changes_conflict = True
            elif container_type == "quality":
                # If the quality name is not set (either by quality or changes, set it now)
                # Quality changes should always override this (as they are "on top")
                if quality_name == "":
                    quality_name = instance_container.getName()
                quality_type = instance_container.getName()
            elif container_type == "user":
                num_user_settings += len(instance_container._instances)

            Job.yieldThread()
        num_visible_settings = 0
        try:
            temp_preferences = Preferences()
            temp_preferences.readFromFile(
                io.TextIOWrapper(archive.open("Cura/preferences.cfg"))
            )  # We need to wrap it, else the archive parser breaks.

            visible_settings_string = temp_preferences.getValue(
                "general/visible_settings")
            if visible_settings_string is not None:
                num_visible_settings = len(visible_settings_string.split(";"))
            active_mode = temp_preferences.getValue("cura/active_mode")
            if not active_mode:
                active_mode = Preferences.getInstance().getValue(
                    "cura/active_mode")
        except KeyError:
            # If there is no preferences file, it's not a workspace, so notify user of failure.
            Logger.log("w", "File %s is not a valid workspace.", file_name)
            return WorkspaceReader.PreReadResult.failed

        # Show the dialog, informing the user what is about to happen.
        self._dialog.setMachineConflict(machine_conflict)
        self._dialog.setQualityChangesConflict(quality_changes_conflict)
        self._dialog.setMaterialConflict(material_conflict)
        self._dialog.setNumVisibleSettings(num_visible_settings)
        self._dialog.setQualityName(quality_name)
        self._dialog.setQualityType(quality_type)
        self._dialog.setNumSettingsOverridenByQualityChanges(
            num_settings_overriden_by_quality_changes)
        self._dialog.setNumUserSettings(num_user_settings)
        self._dialog.setActiveMode(active_mode)
        self._dialog.setMachineName(machine_name)
        self._dialog.setMaterialLabels(material_labels)
        self._dialog.setMachineType(machine_type)
        self._dialog.setExtruders(extruders)
        self._dialog.setVariantType(variant_type_name)
        self._dialog.setHasObjectsOnPlate(
            Application.getInstance().platformActivity)
        self._dialog.show()

        # Block until the dialog is closed.
        self._dialog.waitForClose()

        if self._dialog.getResult() == {}:
            return WorkspaceReader.PreReadResult.cancelled

        self._resolve_strategies = self._dialog.getResult()

        return WorkspaceReader.PreReadResult.accepted
def test_roundtrip_instance(tmpdir, process_count, loaded_container_registry):
    instance_container = InstanceContainer("test_container")
    instance_container.setName("Test Instance Container")
    instance_container.setDefinition("inherits")
    instance_container.setMetaDataEntry("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("inherits")
        with unittest.mock.patch(
                "UM.Settings.ContainerRegistry.ContainerRegistry.getInstance",
                unittest.mock.MagicMock(
                    return_value=loaded_container_registry)):
            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 #38
0
    def read(self, file_name):
        archive = zipfile.ZipFile(file_name, "r")

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

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

        # Copy a number of settings from the temp preferences to the global
        global_preferences = Preferences.getInstance()

        visible_settings = temp_preferences.getValue(
            "general/visible_settings")
        if visible_settings is None:
            Logger.log(
                "w",
                "Workspace did not contain visible settings. Leaving visibility unchanged"
            )
        else:
            global_preferences.setValue("general/visible_settings",
                                        visible_settings)

        categories_expanded = temp_preferences.getValue(
            "cura/categories_expanded")
        if categories_expanded is None:
            Logger.log(
                "w",
                "Workspace did not contain expanded categories. Leaving them unchanged"
            )
        else:
            global_preferences.setValue("cura/categories_expanded",
                                        categories_expanded)

        Application.getInstance().expandedCategoriesChanged.emit(
        )  # Notify the GUI of the change

        self._id_mapping = {}

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

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

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

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

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

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

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

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

                # Check if a stack by this ID already exists;
                container_stacks = self._container_registry.findContainerStacks(
                    id=container_id)
                if container_stacks:
                    stack = container_stacks[0]
                    if self._resolve_strategies["machine"] == "override":
                        # TODO: HACK
                        # There is a machine, check if it has authenticationd data. If so, keep that data.
                        network_authentication_id = container_stacks[
                            0].getMetaDataEntry("network_authentication_id")
                        network_authentication_key = container_stacks[
                            0].getMetaDataEntry("network_authentication_key")
                        container_stacks[0].deserialize(
                            archive.open(container_stack_file).read().decode(
                                "utf-8"))
                        if network_authentication_id:
                            container_stacks[0].addMetaDataEntry(
                                "network_authentication_id",
                                network_authentication_id)
                        if network_authentication_key:
                            container_stacks[0].addMetaDataEntry(
                                "network_authentication_key",
                                network_authentication_key)
                    elif self._resolve_strategies["machine"] == "new":
                        new_id = self.getNewId(container_id)
                        stack = ContainerStack(new_id)
                        stack.deserialize(
                            archive.open(container_stack_file).read().decode(
                                "utf-8"))

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

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

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

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

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

            return None

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

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

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

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

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

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

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

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

        Logger.log(
            "d",
            "Workspace loading is notifying rest of the code of changes...")

        # Notify everything/one that is to notify about changes.
        global_stack.containersChanged.emit(global_stack.getTop())

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

        # Actually change the active machine.
        Application.getInstance().setGlobalContainerStack(global_stack)

        # Load all the nodes / meshdata of the workspace
        nodes = self._3mf_mesh_reader.read(file_name)
        if nodes is None:
            nodes = []
        return nodes
Example #39
0
class Script:
    def __init__(self) -> None:
        super().__init__()
        self._stack = None  # type: Optional[ContainerStack]
        self._definition = None  # type: Optional[DefinitionContainerInterface]
        self._instance = None  # type: Optional[InstanceContainer]

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

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

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

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

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

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

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

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

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

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

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

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

    #   Convenience function to produce a line of g-code.
    #
    #   You can put in an original g-code line and it will 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 will be put in this order "G", "M", "T", "S", "F", "X", "Y", "Z", "E"
    #   followed by any other parameters
    #   \param line The original g-code line that must be modified. If not provided, an entirely new g-code line will be produced.
    #   \return A line of g-code with the desired parameters filled in.
    def putValue(self, line: str = "", **kwargs) -> str:
        # Strip the comment.
        comment = ""
        if ";" in line:
            comment = line[line.find(";"):]
            line = line[:line.find(";")]  # Strip the comment.

        # Parse the original g-code line and add them to kwargs.
        for part in line.split(" "):
            if part == "":
                continue
            parameter = part[0]
            if parameter not in kwargs:
                value = part[1:]
                kwargs[parameter] = value

        # Start writing the new g-code line.
        line_parts = list()
        # First add these parameters in order
        for parameter in ["G", "M", "T", "S", "F", "X", "Y", "Z", "E"]:
            if parameter in kwargs:
                line_parts.append(parameter + str(kwargs.pop(parameter)))
        # Then add the rest of the parameters
        for parameter, value in kwargs.items():
            line_parts.append(parameter + str(value))

        # If there was a comment, put it back in.
        if comment != "":
            line_parts.append(comment)

        # Construct the new line
        return " ".join(line_parts)

    #   This is called when the script is executed.
    #   It gets a list of g-code strings and needs to return a (modified) list.
    def execute(self, data: List[str]) -> List[str]:
        raise NotImplementedError()
Example #40
0
def intent_container():
    container = InstanceContainer(container_id="intent container")
    container.setMetaDataEntry("type", "intent")

    return container
Example #41
0
def material_instance_container():
    container = InstanceContainer(container_id="material container")
    container.setMetaDataEntry("type", "material")

    return container
Example #42
0
    def preRead(self, file_name):
        self._3mf_mesh_reader = Application.getInstance().getMeshFileHandler().getReaderForFile(file_name)
        if self._3mf_mesh_reader and self._3mf_mesh_reader.preRead(file_name) == WorkspaceReader.PreReadResult.accepted:
            pass
        else:
            Logger.log("w", "Could not find reader that was able to read the scene data for 3MF workspace")
            return WorkspaceReader.PreReadResult.failed

        # Check if there are any conflicts, so we can ask the user.
        archive = zipfile.ZipFile(file_name, "r")
        cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
        container_stack_files = [name for name in cura_file_names if name.endswith(self._container_stack_suffix)]
        self._resolve_strategies = {"machine": None, "quality_changes": None, "material": None}
        machine_conflict = False
        quality_changes_conflict = False
        for container_stack_file in container_stack_files:
            container_id = self._stripFileToId(container_stack_file)
            stacks = self._container_registry.findContainerStacks(id=container_id)
            if stacks:
                # Check if there are any changes at all in any of the container stacks.
                id_list = self._getContainerIdListFromSerialized(archive.open(container_stack_file).read().decode("utf-8"))
                for index, container_id in enumerate(id_list):
                    if stacks[0].getContainer(index).getId() != container_id:
                        machine_conflict = True
                        break

        material_conflict = False
        xml_material_profile = self._getXmlProfileClass()
        if self._material_container_suffix is None:
            self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).preferredSuffix
        if xml_material_profile:
            material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
            for material_container_file in material_container_files:
                container_id = self._stripFileToId(material_container_file)
                materials = self._container_registry.findInstanceContainers(id=container_id)
                if materials and not materials[0].isReadOnly():  # Only non readonly materials can be in conflict
                    material_conflict = True

        # Check if any quality_changes instance container is in conflict.
        instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
        for instance_container_file in instance_container_files:
            container_id = self._stripFileToId(instance_container_file)
            instance_container = InstanceContainer(container_id)

            # Deserialize InstanceContainer by converting read data from bytes to string
            instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
            container_type = instance_container.getMetaDataEntry("type")
            if container_type == "quality_changes":
                # Check if quality changes already exists.
                quality_changes = self._container_registry.findInstanceContainers(id = container_id)
                if quality_changes:
                    # Check if there really is a conflict by comparing the values
                    if quality_changes[0] != instance_container:
                        quality_changes_conflict = True
                        break
        try:
            archive.open("Cura/preferences.cfg")
        except KeyError:
            # If there is no preferences file, it's not a workspace, so notify user of failure.
            Logger.log("w", "File %s is not a valid workspace.", file_name)
            return WorkspaceReader.PreReadResult.failed

        if machine_conflict or quality_changes_conflict or material_conflict:
            # There is a conflict; User should choose to either update the existing data, add everything as new data or abort
            self._dialog.setMachineConflict(machine_conflict)
            self._dialog.setQualityChangesConflict(quality_changes_conflict)
            self._dialog.setMaterialConflict(material_conflict)
            self._dialog.show()

            # Block until the dialog is closed.
            self._dialog.waitForClose()

            if self._dialog.getResult() == {}:
                return WorkspaceReader.PreReadResult.cancelled

            self._resolve_strategies = self._dialog.getResult()

        return WorkspaceReader.PreReadResult.accepted
Example #43
0
    def importProfile(self, file_name: str) -> Dict[str, str]:
        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>!", "Failed to import profile from <filename>{0}</filename>: {1}", file_name, "Invalid path")}

        global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
        if not global_stack:
            return {"status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Can't import profile from <filename>{0}</filename> before a printer is added.", file_name)}
        container_tree = ContainerTree.getInstance()

        machine_extruders = global_stack.extruderList

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

        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            if meta_data["profile_reader"][0]["extension"] != extension:
                continue
            profile_reader = cast(ProfileReader, 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>!", "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>!", "Failed to import profile from <filename>{0}</filename>:", file_name) + "\n<message>" + str(e) + "</message>"}

            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>!", "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_definitions = self.findContainers(id = profile_definition)
                if not machine_definitions:
                    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>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)
                            }
                machine_definition = machine_definitions[0]

                # Get the expected machine definition.
                # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
                has_machine_quality = parseBool(machine_definition.getMetaDataEntry("has_machine_quality", "false"))
                profile_definition = machine_definition.getMetaDataEntry("quality_definition", machine_definition.getId()) if has_machine_quality else "fdmprinter"
                expected_machine_definition = container_tree.machines[global_stack.definition.getId()].quality_definition

                # And check if the profile_definition matches either one (showing error if not):
                if profile_definition != expected_machine_definition:
                    Logger.log("d", "Profile {file_name} is for machine {profile_definition}, but the current active machine is {expected_machine_definition}. Changing profile's definition.".format(file_name = file_name, profile_definition = profile_definition, expected_machine_definition = expected_machine_definition))
                    global_profile.setMetaDataEntry("definition", expected_machine_definition)
                    for extruder_profile in extruder_profiles:
                        extruder_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.extruderList):
                        profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
                        profile = InstanceContainer(profile_id)
                        profile.setName(quality_name)
                        profile.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
                        profile.setMetaDataEntry("type", "quality_changes")
                        profile.setMetaDataEntry("definition", expected_machine_definition)
                        profile.setMetaDataEntry("quality_type", quality_type)
                        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)
                                    if setting_definition is not None:
                                        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
                profile_ids_added = []  # type: List[str]
                for profile_index, profile in enumerate(profile_or_list):
                    if profile_index == 0:
                        # This is assumed to be the global profile
                        profile_id = (cast(ContainerInterface, 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.setMetaDataEntry("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:
                        # Remove any profiles that did got added.
                        for profile_id in profile_ids_added:
                            self.removeContainer(profile_id)

                        return {"status": "error", "message": catalog.i18nc(
                            "@info:status Don't translate the XML tag <filename>!",
                            "Failed to import profile from <filename>{0}</filename>:",
                            file_name) + " " + result}
                    profile_ids_added.append(profile.getId())
                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 #44
0
    def read(self, file_name):
        archive = zipfile.ZipFile(file_name, "r")

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

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

        # Copy a number of settings from the temp preferences to the global
        global_preferences = Preferences.getInstance()

        visible_settings = temp_preferences.getValue("general/visible_settings")
        if visible_settings is None:
            Logger.log("w", "Workspace did not contain visible settings. Leaving visibility unchanged")
        else:
            global_preferences.setValue("general/visible_settings", visible_settings)

        categories_expanded = temp_preferences.getValue("cura/categories_expanded")
        if categories_expanded is None:
            Logger.log("w", "Workspace did not contain expanded categories. Leaving them unchanged")
        else:
            global_preferences.setValue("cura/categories_expanded", categories_expanded)

        Application.getInstance().expandedCategoriesChanged.emit()  # Notify the GUI of the change

        self._id_mapping = {}

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

        global_stack_file, extruder_stack_files = self._determineGlobalAndExtruderStackFiles(file_name, cura_file_names)

        global_stack = None
        extruder_stacks = []
        extruder_stacks_added = []
        container_stacks_added = []

        containers_added = []

        global_stack_id_original = self._stripFileToId(global_stack_file)
        global_stack_id_new = global_stack_id_original
        global_stack_need_rename = False

        extruder_stack_id_map = {}  # new and old ExtruderStack IDs map
        if self._resolve_strategies["machine"] == "new":
            # We need a new id if the id already exists
            if self._container_registry.findContainerStacks(id = global_stack_id_original):
                global_stack_id_new = self.getNewId(global_stack_id_original)
                global_stack_need_rename = True

            for each_extruder_stack_file in extruder_stack_files:
                old_container_id = self._stripFileToId(each_extruder_stack_file)
                new_container_id = old_container_id
                if self._container_registry.findContainerStacks(id = old_container_id):
                    # get a new name for this extruder
                    new_container_id = self.getNewId(old_container_id)

                extruder_stack_id_map[old_container_id] = new_container_id

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

        Logger.log("d", "Workspace loading is checking materials...")
        material_containers = []
        # Get all the material files and check if they exist. If not, add them.
        xml_material_profile = self._getXmlProfileClass()
        if self._material_container_suffix is None:
            self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).suffixes[0]
        if xml_material_profile:
            material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
            for material_container_file in material_container_files:
                container_id = self._stripFileToId(material_container_file)
                materials = self._container_registry.findInstanceContainers(id = container_id)

                if not materials:
                    material_container = xml_material_profile(container_id)
                    material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
                    containers_to_add.append(material_container)
                else:
                    material_container = materials[0]
                    if not material_container.isReadOnly():  # Only create new materials if they are not read only.
                        if self._resolve_strategies["material"] == "override":
                            material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
                        elif self._resolve_strategies["material"] == "new":
                            # Note that we *must* deserialize it with a new ID, as multiple containers will be
                            # auto created & added.
                            material_container = xml_material_profile(self.getNewId(container_id))
                            material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"))
                            containers_to_add.append(material_container)

                material_containers.append(material_container)
                Job.yieldThread()

        Logger.log("d", "Workspace loading is checking instance containers...")
        # Get quality_changes and user profiles saved in the workspace
        instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
        user_instance_containers = []
        quality_and_definition_changes_instance_containers = []
        for instance_container_file in instance_container_files:
            container_id = self._stripFileToId(instance_container_file)
            serialized = archive.open(instance_container_file).read().decode("utf-8")

            # HACK! we ignore "quality" and "variant" instance containers!
            parser = configparser.ConfigParser()
            parser.read_string(serialized)
            if not parser.has_option("metadata", "type"):
                Logger.log("w", "Cannot find metadata/type in %s, ignoring it", instance_container_file)
                continue
            if parser.get("metadata", "type") in self._ignored_instance_container_types:
                continue

            instance_container = InstanceContainer(container_id)

            # Deserialize InstanceContainer by converting read data from bytes to string
            instance_container.deserialize(serialized)
            container_type = instance_container.getMetaDataEntry("type")
            Job.yieldThread()

            #
            # IMPORTANT:
            # If an instance container (or maybe other type of container) exists, and user chooses "Create New",
            # we need to rename this container and all references to it, and changing those references are VERY
            # HARD.
            #
            if container_type in self._ignored_instance_container_types:
                # Ignore certain instance container types
                Logger.log("w", "Ignoring instance container [%s] with type [%s]", container_id, container_type)
                continue
            elif container_type == "user":
                # Check if quality changes already exists.
                user_containers = self._container_registry.findInstanceContainers(id = container_id)
                if not user_containers:
                    containers_to_add.append(instance_container)
                else:
                    if self._resolve_strategies["machine"] == "override" or self._resolve_strategies["machine"] is None:
                        instance_container = user_containers[0]
                        instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
                        instance_container.setDirty(True)
                    elif self._resolve_strategies["machine"] == "new":
                        # The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
                        old_extruder_id = instance_container.getMetaDataEntry("extruder", None)
                        if old_extruder_id:
                            new_extruder_id = extruder_stack_id_map[old_extruder_id]
                            new_id = new_extruder_id + "_current_settings"
                            instance_container._id = new_id
                            instance_container.setName(new_id)
                            instance_container.setMetaDataEntry("extruder", new_extruder_id)
                            containers_to_add.append(instance_container)

                        machine_id = instance_container.getMetaDataEntry("machine", None)
                        if machine_id:
                            new_machine_id = self.getNewId(machine_id)
                            new_id = new_machine_id + "_current_settings"
                            instance_container._id = new_id
                            instance_container.setName(new_id)
                            instance_container.setMetaDataEntry("machine", new_machine_id)
                            containers_to_add.append(instance_container)
                user_instance_containers.append(instance_container)
            elif container_type in ("quality_changes", "definition_changes"):
                # Check if quality changes already exists.
                changes_containers = self._container_registry.findInstanceContainers(id = container_id)
                if not changes_containers:
                    # no existing containers with the same ID, so we can safely add the new one
                    containers_to_add.append(instance_container)
                else:
                    # we have found existing container with the same ID, so we need to resolve according to the
                    # selected strategy.
                    if self._resolve_strategies[container_type] == "override":
                        instance_container = changes_containers[0]
                        instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
                        instance_container.setDirty(True)

                    elif self._resolve_strategies[container_type] == "new":
                        # TODO: how should we handle the case "new" for quality_changes and definition_changes?

                        instance_container.setName(self._container_registry.uniqueName(instance_container.getName()))
                        new_changes_container_id = self.getNewId(instance_container.getId())
                        instance_container._id = new_changes_container_id

                        # TODO: we don't know the following is correct or not, need to verify
                        #       AND REFACTOR!!!
                        if self._resolve_strategies["machine"] == "new":
                            # The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
                            old_extruder_id = instance_container.getMetaDataEntry("extruder", None)
                            if old_extruder_id:
                                new_extruder_id = extruder_stack_id_map[old_extruder_id]
                                instance_container.setMetaDataEntry("extruder", new_extruder_id)

                            machine_id = instance_container.getMetaDataEntry("machine", None)
                            if machine_id:
                                new_machine_id = self.getNewId(machine_id)
                                instance_container.setMetaDataEntry("machine", new_machine_id)

                        containers_to_add.append(instance_container)

                    elif self._resolve_strategies[container_type] is None:
                        # The ID already exists, but nothing in the values changed, so do nothing.
                        pass
                quality_and_definition_changes_instance_containers.append(instance_container)
            else:
                existing_container = self._container_registry.findInstanceContainers(id = container_id)
                if not existing_container:
                    containers_to_add.append(instance_container)
            if global_stack_need_rename:
                if instance_container.getMetaDataEntry("machine"):
                    instance_container.setMetaDataEntry("machine", global_stack_id_new)

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

        # Get the stack(s) saved in the workspace.
        Logger.log("d", "Workspace loading is checking stacks containers...")

        # --
        # load global stack file
        try:
            if self._resolve_strategies["machine"] == "override":
                container_stacks = self._container_registry.findContainerStacks(id = global_stack_id_original)
                stack = container_stacks[0]

                # HACK
                # There is a machine, check if it has authentication data. If so, keep that data.
                network_authentication_id = container_stacks[0].getMetaDataEntry("network_authentication_id")
                network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key")
                container_stacks[0].deserialize(archive.open(global_stack_file).read().decode("utf-8"))
                if network_authentication_id:
                    container_stacks[0].addMetaDataEntry("network_authentication_id", network_authentication_id)
                if network_authentication_key:
                    container_stacks[0].addMetaDataEntry("network_authentication_key", network_authentication_key)

            elif self._resolve_strategies["machine"] == "new":
                # create a new global stack
                stack = GlobalStack(global_stack_id_new)
                # Deserialize stack by converting read data from bytes to string
                stack.deserialize(archive.open(global_stack_file).read().decode("utf-8"))

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

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

                # Only machines need a new name, stacks may be non-unique
                stack.setName(global_stack_id_new)

                container_stacks_added.append(stack)
                self._container_registry.addContainer(stack)
                containers_added.append(stack)
            else:
                Logger.log("e", "Resolve strategy of %s for machine is not supported",
                           self._resolve_strategies["machine"])

            global_stack = stack
            Job.yieldThread()
        except:
            Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
            # Something went really wrong. Try to remove any data that we added.
            for container in containers_added:
                self._container_registry.removeContainer(container.getId())
            return

        # --
        # load extruder stack files
        try:
            for extruder_stack_file in extruder_stack_files:
                container_id = self._stripFileToId(extruder_stack_file)
                extruder_file_content = archive.open(extruder_stack_file, "r").read().decode("utf-8")

                if self._resolve_strategies["machine"] == "override":
                    # deserialize new extruder stack over the current ones
                    stack = self._overrideExtruderStack(global_stack, extruder_file_content)

                elif self._resolve_strategies["machine"] == "new":
                    new_id = extruder_stack_id_map[container_id]
                    stack = ExtruderStack(new_id)

                    # HACK: the global stack can have a new name, so we need to make sure that this extruder stack
                    #       references to the new name instead of the old one. Normally, this can be done after
                    #       deserialize() by setting the metadata, but in the case of ExtruderStack, deserialize()
                    #       also does addExtruder() to its machine stack, so we have to make sure that it's pointing
                    #       to the right machine BEFORE deserialization.
                    extruder_config = configparser.ConfigParser()
                    extruder_config.read_string(extruder_file_content)
                    extruder_config.set("metadata", "machine", global_stack_id_new)
                    tmp_string_io = io.StringIO()
                    extruder_config.write(tmp_string_io)
                    extruder_file_content = tmp_string_io.getvalue()

                    stack.deserialize(extruder_file_content)

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

                    self._container_registry.addContainer(stack)
                    extruder_stacks_added.append(stack)
                    containers_added.append(stack)
                else:
                    Logger.log("w", "Unknown resolve strategy: %s", self._resolve_strategies["machine"])

                extruder_stacks.append(stack)
        except:
            Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
            # Something went really wrong. Try to remove any data that we added. 
            for container in containers_added:
                self._container_registry.removeContainer(container.getId())
            return

        #
        # Replacing the old containers if resolve is "new".
        # When resolve is "new", some containers will get renamed, so all the other containers that reference to those
        # MUST get updated too.
        #
        if self._resolve_strategies["machine"] == "new":
            # A new machine was made, but it was serialized with the wrong user container. Fix that now.
            for container in user_instance_containers:
                # replacing the container ID for user instance containers for the extruders
                extruder_id = container.getMetaDataEntry("extruder", None)
                if extruder_id:
                    for extruder in extruder_stacks:
                        if extruder.getId() == extruder_id:
                            extruder.userChanges = container
                            continue

                # replacing the container ID for user instance containers for the machine
                machine_id = container.getMetaDataEntry("machine", None)
                if machine_id:
                    if global_stack.getId() == machine_id:
                        global_stack.userChanges = container
                        continue

        for changes_container_type in ("quality_changes", "definition_changes"):
            if self._resolve_strategies[changes_container_type] == "new":
                # Quality changes needs to get a new ID, added to registry and to the right stacks
                for each_changes_container in quality_and_definition_changes_instance_containers:
                    # NOTE: The renaming and giving new IDs are possibly redundant because they are done in the
                    #       instance container loading part.
                    new_id = each_changes_container.getId()

                    # Find the old (current) changes container in the global stack
                    if changes_container_type == "quality_changes":
                        old_container = global_stack.qualityChanges
                    elif changes_container_type == "definition_changes":
                        old_container = global_stack.definitionChanges

                    # sanity checks
                    # NOTE: The following cases SHOULD NOT happen!!!!
                    if not old_container:
                        Logger.log("e", "We try to get [%s] from the global stack [%s] but we got None instead!",
                                   changes_container_type, global_stack.getId())

                    # Replace the quality/definition changes container if it's in the GlobalStack
                    # NOTE: we can get an empty container here, but the IDs will not match,
                    # so this comparison is fine.
                    if self._id_mapping.get(old_container.getId()) == new_id:
                        if changes_container_type == "quality_changes":
                            global_stack.qualityChanges = each_changes_container
                        elif changes_container_type == "definition_changes":
                            global_stack.definitionChanges = each_changes_container
                        continue

                    # Replace the quality/definition changes container if it's in one of the ExtruderStacks
                    for each_extruder_stack in extruder_stacks:
                        changes_container = None
                        if changes_container_type == "quality_changes":
                            changes_container = each_extruder_stack.qualityChanges
                        elif changes_container_type == "definition_changes":
                            changes_container = each_extruder_stack.definitionChanges

                        # sanity checks
                        # NOTE: The following cases SHOULD NOT happen!!!!
                        if not changes_container:
                            Logger.log("e", "We try to get [%s] from the extruder stack [%s] but we got None instead!",
                                       changes_container_type, each_extruder_stack.getId())

                        # NOTE: we can get an empty container here, but the IDs will not match,
                        # so this comparison is fine.
                        if self._id_mapping.get(changes_container.getId()) == new_id:
                            if changes_container_type == "quality_changes":
                                each_extruder_stack.qualityChanges = each_changes_container
                            elif changes_container_type == "definition_changes":
                                each_extruder_stack.definitionChanges = each_changes_container

        if self._resolve_strategies["material"] == "new":
            for each_material in material_containers:
                old_material = global_stack.material

                # check if the old material container has been renamed to this material container ID
                # if the container hasn't been renamed, we do nothing.
                new_id = self._id_mapping.get(old_material.getId())
                if new_id is None or new_id != each_material.getId():
                    continue

                if old_material.getId() in self._id_mapping:
                    global_stack.material = each_material

                for each_extruder_stack in extruder_stacks:
                    old_material = each_extruder_stack.material

                    # check if the old material container has been renamed to this material container ID
                    # if the container hasn't been renamed, we do nothing.
                    new_id = self._id_mapping.get(old_material.getId())
                    if new_id is None or new_id != each_material.getId():
                        continue

                    if old_material.getId() in self._id_mapping:
                        each_extruder_stack.material = each_material

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

        Logger.log("d", "Workspace loading is notifying rest of the code of changes...")

        if self._resolve_strategies["machine"] == "new":
            for stack in extruder_stacks:
                stack.setNextStack(global_stack)
                stack.containersChanged.emit(stack.getTop())

        # Actually change the active machine.
        Application.getInstance().setGlobalContainerStack(global_stack)

        # Notify everything/one that is to notify about changes.
        global_stack.containersChanged.emit(global_stack.getTop())

        # Load all the nodes / meshdata of the workspace
        nodes = self._3mf_mesh_reader.read(file_name)
        if nodes is None:
            nodes = []
        return nodes
Example #45
0
    def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id, new_global_quality_changes = None, create_new_ids = True):
        new_extruder_id = extruder_id

        application = cura.CuraApplication.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.setMetaDataEntry("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.setMetaDataEntry("setting_version", application.SettingVersion)
        definition_changes.setMetaDataEntry("type", "definition_changes")
        definition_changes.setMetaDataEntry("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.setMetaDataEntry("type", "user")
        user_container.setMetaDataEntry("machine", machine.getId())
        user_container.setMetaDataEntry("setting_version", application.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.setMetaDataEntry("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.setMetaDataEntry("type", "quality_changes")
                    extruder_quality_changes_container.setMetaDataEntry("setting_version", application.SettingVersion)
                    extruder_quality_changes_container.setMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
                    extruder_quality_changes_container.setMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
                    extruder_quality_changes_container.setMetaDataEntry("intent_category", "default")  # Intent categories weren't a thing back then.
                    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 #46
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 #47
0
    def _configureProfile(
            self,
            profile: InstanceContainer,
            id_seed: str,
            new_name: str,
            base_profile: InstanceContainer = None) -> 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)

        # 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)

        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()
                profile_material_id = profile.getMetaDataEntry("material")
                containers = Application.getInstance().getContainerRegistry(
                ).findContainers(type="material", id=profile_material_id)
                if len(containers) > 0:
                    active_material_id = profile_material_id
                if active_material_id and active_material_id != "empty":  # only update if there is an active material
                    profile.setMetaDataEntry("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:
            if base_profile:
                base_profile.setMetaDataEntry("material", active_material_id)
                base_profile.setDirty(True)
                ContainerRegistry.getInstance().addContainer(base_profile)
            else:
                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 #48
0
def getInstanceContainer(container_type) -> InstanceContainer:
    container = InstanceContainer(container_id="InstanceContainer")
    container.addMetaDataEntry("type", container_type)
    return container
Example #49
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 #50
0
    def _createFlattenedContainerInstance(self, instance_container1,
                                          instance_container2):
        flat_container = InstanceContainer(instance_container2.getName())
        if instance_container1.getDefinition():
            flat_container.setDefinition(instance_container1.getDefinition())
        else:
            flat_container.setDefinition(instance_container2.getDefinition())
        flat_container.setMetaData(
            copy.deepcopy(instance_container2.getMetaData()))

        for key in instance_container2.getAllKeys():
            flat_container.setProperty(
                key, "value", instance_container2.getProperty(key, "value"))

        for key in instance_container1.getAllKeys():
            flat_container.setProperty(
                key, "value", instance_container1.getProperty(key, "value"))

        return flat_container
Example #51
0
    def updateQualityChanges(self) -> bool:
        """Update the current active quality changes container with the settings from the user container.

        This will go through the active global stack and all active extruder stacks and merge the changes from the user
        container into the quality_changes container. After that, the user container is cleared.

        :return: :type{bool} True if successful, False if not.
        """

        application = cura.CuraApplication.CuraApplication.getInstance()
        global_stack = application.getMachineManager().activeMachine
        if not global_stack:
            return False

        application.getMachineManager().blurSettings.emit()

        current_quality_changes_name = global_stack.qualityChanges.getName()
        current_quality_type = global_stack.quality.getMetaDataEntry(
            "quality_type")
        extruder_stacks = global_stack.extruderList
        container_registry = cura.CuraApplication.CuraApplication.getInstance(
        ).getContainerRegistry()
        machine_definition_id = ContainerTree.getInstance().machines[
            global_stack.definition.getId()].quality_definition
        for stack in [global_stack] + extruder_stacks:
            # Find the quality_changes container for this stack and merge the contents of the top container into it.
            quality_changes = stack.qualityChanges

            if quality_changes.getId() == "empty_quality_changes":
                quality_changes = InstanceContainer(
                    container_registry.uniqueName(
                        (stack.getId() + "_" +
                         current_quality_changes_name).lower().replace(
                             " ", "_")))
                quality_changes.setName(current_quality_changes_name)
                quality_changes.setMetaDataEntry("type", "quality_changes")
                quality_changes.setMetaDataEntry("quality_type",
                                                 current_quality_type)
                if stack.getMetaDataEntry(
                        "position") is not None:  # Extruder stacks.
                    quality_changes.setMetaDataEntry(
                        "position", stack.getMetaDataEntry("position"))
                    quality_changes.setMetaDataEntry(
                        "intent_category",
                        stack.quality.getMetaDataEntry("intent_category",
                                                       "default"))
                quality_changes.setMetaDataEntry("setting_version",
                                                 application.SettingVersion)
                quality_changes.setDefinition(machine_definition_id)
                container_registry.addContainer(quality_changes)
                stack.qualityChanges = quality_changes

            if not quality_changes or container_registry.isReadOnly(
                    quality_changes.getId()):
                Logger.log(
                    "e",
                    "Could not update quality of a nonexistent or read only quality profile in stack %s",
                    stack.getId())
                continue

            self._performMerge(quality_changes, stack.getTop())

        cura.CuraApplication.CuraApplication.getInstance().getMachineManager(
        ).activeQualityChangesGroupChanged.emit()

        return True
Example #52
0
    def read(self, file_name):
        if file_name.split(".")[-1] != "ini":
            return None
        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")
        return profile
Example #53
0
def test_deserialize_containers(container_stack, container_registry):
    container = InstanceContainer("a")
    container_registry.addContainer(container)

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

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

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

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

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

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

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

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

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

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

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

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

    container_stack = ContainerStack(str(uuid.uuid4()))
    container_b = InstanceContainer(
        "b")  # Add the missing container and try again.
    ContainerRegistry.getInstance().addContainer(container_b)
    container_stack.deserialize(serialised)
    assert container_stack.getContainers() == [container, container_b]
Example #54
0
    def preRead(self, file_name):
        self._3mf_mesh_reader = Application.getInstance().getMeshFileHandler(
        ).getReaderForFile(file_name)
        if self._3mf_mesh_reader and self._3mf_mesh_reader.preRead(
                file_name) == WorkspaceReader.PreReadResult.accepted:
            pass
        else:
            Logger.log(
                "w",
                "Could not find reader that was able to read the scene data for 3MF workspace"
            )
            return WorkspaceReader.PreReadResult.failed

        # Check if there are any conflicts, so we can ask the user.
        archive = zipfile.ZipFile(file_name, "r")
        cura_file_names = [
            name for name in archive.namelist() if name.startswith("Cura/")
        ]
        container_stack_files = [
            name for name in cura_file_names
            if name.endswith(self._container_stack_suffix)
        ]
        self._resolve_strategies = {
            "machine": None,
            "quality_changes": None,
            "material": None
        }
        machine_conflict = False
        quality_changes_conflict = False
        for container_stack_file in container_stack_files:
            container_id = self._stripFileToId(container_stack_file)
            stacks = self._container_registry.findContainerStacks(
                id=container_id)
            if stacks:
                # Check if there are any changes at all in any of the container stacks.
                id_list = self._getContainerIdListFromSerialized(
                    archive.open(container_stack_file).read().decode("utf-8"))
                for index, container_id in enumerate(id_list):
                    if stacks[0].getContainer(index).getId() != container_id:
                        machine_conflict = True
                        break
            Job.yieldThread()

        material_conflict = False
        xml_material_profile = self._getXmlProfileClass()
        if self._material_container_suffix is None:
            self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(
                xml_material_profile).preferredSuffix
        if xml_material_profile:
            material_container_files = [
                name for name in cura_file_names
                if name.endswith(self._material_container_suffix)
            ]
            for material_container_file in material_container_files:
                container_id = self._stripFileToId(material_container_file)
                materials = self._container_registry.findInstanceContainers(
                    id=container_id)
                if materials and not materials[0].isReadOnly(
                ):  # Only non readonly materials can be in conflict
                    material_conflict = True
                Job.yieldThread()

        # Check if any quality_changes instance container is in conflict.
        instance_container_files = [
            name for name in cura_file_names
            if name.endswith(self._instance_container_suffix)
        ]
        for instance_container_file in instance_container_files:
            container_id = self._stripFileToId(instance_container_file)
            instance_container = InstanceContainer(container_id)

            # Deserialize InstanceContainer by converting read data from bytes to string
            instance_container.deserialize(
                archive.open(instance_container_file).read().decode("utf-8"))
            container_type = instance_container.getMetaDataEntry("type")
            if container_type == "quality_changes":
                # Check if quality changes already exists.
                quality_changes = self._container_registry.findInstanceContainers(
                    id=container_id)
                if quality_changes:
                    # Check if there really is a conflict by comparing the values
                    if quality_changes[0] != instance_container:
                        quality_changes_conflict = True
                        break
            Job.yieldThread()
        try:
            archive.open("Cura/preferences.cfg")
        except KeyError:
            # If there is no preferences file, it's not a workspace, so notify user of failure.
            Logger.log("w", "File %s is not a valid workspace.", file_name)
            return WorkspaceReader.PreReadResult.failed

        if machine_conflict or quality_changes_conflict or material_conflict:
            # There is a conflict; User should choose to either update the existing data, add everything as new data or abort
            self._dialog.setMachineConflict(machine_conflict)
            self._dialog.setQualityChangesConflict(quality_changes_conflict)
            self._dialog.setMaterialConflict(material_conflict)
            self._dialog.show()

            # Block until the dialog is closed.
            self._dialog.waitForClose()

            if self._dialog.getResult() == {}:
                return WorkspaceReader.PreReadResult.cancelled

            self._resolve_strategies = self._dialog.getResult()

        return WorkspaceReader.PreReadResult.accepted
Example #55
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 #56
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))
        return quality_changes
Example #57
0
    def read(self, file_name):
        # Load all the nodes / meshdata of the workspace
        nodes = self._3mf_mesh_reader.read(file_name)
        if nodes is None:
            nodes = []

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

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

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

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

        self._id_mapping = {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            return None

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

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

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

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

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

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

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

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

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

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

        # Actually change the active machine.
        Application.getInstance().setGlobalContainerStack(global_stack)
        return nodes
Example #58
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 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(
                ContainerRegistry.getInstance().findDefinitionContainers(
                    id="fdmprinter")[0])
            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"]

        # 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 #59
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 #60
0
    def preRead(self, file_name, show_dialog=True, *args, **kwargs):
        self._3mf_mesh_reader = Application.getInstance().getMeshFileHandler().getReaderForFile(file_name)
        if self._3mf_mesh_reader and self._3mf_mesh_reader.preRead(file_name) == WorkspaceReader.PreReadResult.accepted:
            pass
        else:
            Logger.log("w", "Could not find reader that was able to read the scene data for 3MF workspace")
            return WorkspaceReader.PreReadResult.failed

        machine_name = ""
        machine_type = ""
        variant_type_name = i18n_catalog.i18nc("@label", "Nozzle")

        # Check if there are any conflicts, so we can ask the user.
        archive = zipfile.ZipFile(file_name, "r")
        cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]

        # A few lists of containers in this project files.
        # When loading the global stack file, it may be associated with those containers, which may or may not be
        # in Cura already, so we need to provide them as alternative search lists.
        definition_container_list = []
        instance_container_list = []
        material_container_list = []

        resolve_strategy_keys = ["machine", "material", "quality_changes"]
        self._resolve_strategies = {k: None for k in resolve_strategy_keys}
        containers_found_dict = {k: False for k in resolve_strategy_keys}

        #
        # Read definition containers
        #
        machine_definition_container_count = 0
        extruder_definition_container_count = 0
        definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
        for each_definition_container_file in definition_container_files:
            container_id = self._stripFileToId(each_definition_container_file)
            definitions = self._container_registry.findDefinitionContainers(id=container_id)

            if not definitions:
                definition_container = DefinitionContainer(container_id)
                definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8"))

            else:
                definition_container = definitions[0]
            definition_container_list.append(definition_container)

            definition_container_type = definition_container.getMetaDataEntry("type")
            if definition_container_type == "machine":
                machine_type = definition_container.getName()
                variant_type_name = definition_container.getMetaDataEntry("variants_name", variant_type_name)

                machine_definition_container_count += 1
            elif definition_container_type == "extruder":
                extruder_definition_container_count += 1
            else:
                Logger.log("w", "Unknown definition container type %s for %s",
                           definition_container_type, each_definition_container_file)
            Job.yieldThread()
        # sanity check
        if machine_definition_container_count != 1:
            msg = "Expecting one machine definition container but got %s" % machine_definition_container_count
            Logger.log("e", msg)
            raise RuntimeError(msg)

        material_labels = []
        material_conflict = False
        xml_material_profile = self._getXmlProfileClass()
        if self._material_container_suffix is None:
            self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).preferredSuffix
        if xml_material_profile:
            material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
            for material_container_file in material_container_files:
                container_id = self._stripFileToId(material_container_file)
                materials = self._container_registry.findInstanceContainers(id=container_id)
                material_labels.append(self._getMaterialLabelFromSerialized(archive.open(material_container_file).read().decode("utf-8")))
                if materials:
                    containers_found_dict["material"] = True
                    if not materials[0].isReadOnly():  # Only non readonly materials can be in conflict
                        material_conflict = True
                Job.yieldThread()

        # Check if any quality_changes instance container is in conflict.
        instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
        quality_name = ""
        quality_type = ""
        num_settings_overriden_by_quality_changes = 0 # How many settings are changed by the quality changes
        num_settings_overriden_by_definition_changes = 0 # How many settings are changed by the definition changes
        num_user_settings = 0
        quality_changes_conflict = False
        definition_changes_conflict = False

        for each_instance_container_file in instance_container_files:
            container_id = self._stripFileToId(each_instance_container_file)
            instance_container = InstanceContainer(container_id)

            # Deserialize InstanceContainer by converting read data from bytes to string
            instance_container.deserialize(archive.open(each_instance_container_file).read().decode("utf-8"))
            instance_container_list.append(instance_container)

            container_type = instance_container.getMetaDataEntry("type")
            if container_type == "quality_changes":
                quality_name = instance_container.getName()
                num_settings_overriden_by_quality_changes += len(instance_container._instances)
                # Check if quality changes already exists.
                quality_changes = self._container_registry.findInstanceContainers(id = container_id)
                if quality_changes:
                    containers_found_dict["quality_changes"] = True
                    # Check if there really is a conflict by comparing the values
                    if quality_changes[0] != instance_container:
                        quality_changes_conflict = True
            elif container_type == "definition_changes":
                definition_name = instance_container.getName()
                num_settings_overriden_by_definition_changes += len(instance_container._instances)
                definition_changes = self._container_registry.findDefinitionContainers(id = container_id)
                if definition_changes:
                    if definition_changes[0] != instance_container:
                        definition_changes_conflict = True
            elif container_type == "user":
                num_user_settings += len(instance_container._instances)
            elif container_type in self._ignored_instance_container_types:
                # Ignore certain instance container types
                Logger.log("w", "Ignoring instance container [%s] with type [%s]", container_id, container_type)
                continue

            Job.yieldThread()

        # Load ContainerStack files and ExtruderStack files
        global_stack_file, extruder_stack_files = self._determineGlobalAndExtruderStackFiles(
            file_name, cura_file_names)
        machine_conflict = False
        # Because there can be cases as follows:
        #  - the global stack exists but some/all of the extruder stacks DON'T exist
        #  - the global stack DOESN'T exist but some/all of the extruder stacks exist
        # To simplify this, only check if the global stack exists or not
        container_id = self._stripFileToId(global_stack_file)
        serialized = archive.open(global_stack_file).read().decode("utf-8")
        machine_name = self._getMachineNameFromSerializedStack(serialized)
        stacks = self._container_registry.findContainerStacks(id = container_id)
        if stacks:
            global_stack = stacks[0]
            containers_found_dict["machine"] = True
            # Check if there are any changes at all in any of the container stacks.
            id_list = self._getContainerIdListFromSerialized(serialized)
            for index, container_id in enumerate(id_list):
                # take into account the old empty container IDs
                container_id = self._old_empty_profile_id_dict.get(container_id, container_id)
                if global_stack.getContainer(index).getId() != container_id:
                    machine_conflict = True
                    break
        Job.yieldThread()

        # if the global stack is found, we check if there are conflicts in the extruder stacks
        if containers_found_dict["machine"] and not machine_conflict:
            for extruder_stack_file in extruder_stack_files:
                container_id = self._stripFileToId(extruder_stack_file)
                serialized = archive.open(extruder_stack_file).read().decode("utf-8")
                parser = configparser.ConfigParser()
                parser.read_string(serialized)

                # The check should be done for the extruder stack that's associated with the existing global stack,
                # and those extruder stacks may have different IDs.
                # So we check according to the positions

                position = str(parser["metadata"]["position"])
                if position not in global_stack.extruders:
                    # The extruder position defined in the project doesn't exist in this global stack.
                    # We can say that it is a machine conflict, but it is very hard to override the machine in this
                    # case because we need to override the existing extruders and add the non-existing extruders.
                    #
                    # HACK:
                    # To make this simple, we simply say that there is no machine conflict and create a new machine
                    # by default.
                    machine_conflict = False
                    break

                existing_extruder_stack = global_stack.extruders[position]
                # check if there are any changes at all in any of the container stacks.
                id_list = self._getContainerIdListFromSerialized(serialized)
                for index, container_id in enumerate(id_list):
                    # take into account the old empty container IDs
                    container_id = self._old_empty_profile_id_dict.get(container_id, container_id)
                    if existing_extruder_stack.getContainer(index).getId() != container_id:
                        machine_conflict = True
                        break

        num_visible_settings = 0
        try:
            temp_preferences = Preferences()
            temp_preferences.readFromFile(io.TextIOWrapper(archive.open("Cura/preferences.cfg")))  # We need to wrap it, else the archive parser breaks.

            visible_settings_string = temp_preferences.getValue("general/visible_settings")
            if visible_settings_string is not None:
                num_visible_settings = len(visible_settings_string.split(";"))
            active_mode = temp_preferences.getValue("cura/active_mode")
            if not active_mode:
                active_mode = Preferences.getInstance().getValue("cura/active_mode")
        except KeyError:
            # If there is no preferences file, it's not a workspace, so notify user of failure.
            Logger.log("w", "File %s is not a valid workspace.", file_name)
            return WorkspaceReader.PreReadResult.failed

        # In case we use preRead() to check if a file is a valid project file, we don't want to show a dialog.
        if not show_dialog:
            return WorkspaceReader.PreReadResult.accepted

        # prepare data for the dialog
        num_extruders = extruder_definition_container_count
        if num_extruders == 0:
            num_extruders = 1  # No extruder stacks found, which means there is one extruder

        extruders = num_extruders * [""]

        # Show the dialog, informing the user what is about to happen.
        self._dialog.setMachineConflict(machine_conflict)
        self._dialog.setQualityChangesConflict(quality_changes_conflict)
        self._dialog.setDefinitionChangesConflict(definition_changes_conflict)
        self._dialog.setMaterialConflict(material_conflict)
        self._dialog.setNumVisibleSettings(num_visible_settings)
        self._dialog.setQualityName(quality_name)
        self._dialog.setQualityType(quality_type)
        self._dialog.setNumSettingsOverridenByQualityChanges(num_settings_overriden_by_quality_changes)
        self._dialog.setNumUserSettings(num_user_settings)
        self._dialog.setActiveMode(active_mode)
        self._dialog.setMachineName(machine_name)
        self._dialog.setMaterialLabels(material_labels)
        self._dialog.setMachineType(machine_type)
        self._dialog.setExtruders(extruders)
        self._dialog.setVariantType(variant_type_name)
        self._dialog.setHasObjectsOnPlate(Application.getInstance().platformActivity)
        self._dialog.show()

        # Block until the dialog is closed.
        self._dialog.waitForClose()

        if self._dialog.getResult() == {}:
            return WorkspaceReader.PreReadResult.cancelled

        self._resolve_strategies = self._dialog.getResult()
        #
        # There can be 3 resolve strategies coming from the dialog:
        #  - new:       create a new container
        #  - override:  override the existing container
        #  - None:      There is no conflict, which means containers with the same IDs may or may not be there already.
        #               If there is an existing container, there is no conflict between the them, and default to "override"
        #               If there is no existing container, default to "new"
        #
        # Default values
        for key, strategy in self._resolve_strategies.items():
            if key not in containers_found_dict or strategy is not None:
                continue
            self._resolve_strategies[key] = "override" if containers_found_dict[key] else "new"

        return WorkspaceReader.PreReadResult.accepted