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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                    for profile in extruder_profiles:
                        profile_or_list.append(profile)

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

                    elif profile_index < len(machine_extruders) + 1:
                        # This is assumed to be an extruder profile
                        extruder_id = machine_extruders[profile_index -
                                                        1].definition.getId()
                        extruder_position = str(profile_index - 1)
                        if not profile.getMetaDataEntry("position"):
                            profile.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:
                        return {
                            "status":
                            "error",
                            "message":
                            catalog.i18nc(
                                "@info:status Don't translate the XML tags <filename> or <message>!",
                                "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                                file_name, result)
                        }

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

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

        # If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {
            "status":
            "error",
            "message":
            catalog.i18nc(
                "@info:status",
                "Profile {0} has an unknown file type or is corrupted.",
                file_name)
        }
    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
Exemple #3
0
def getInstanceContainer(container_type) -> InstanceContainer:
    container = InstanceContainer(container_id="InstanceContainer")
    container.addMetaDataEntry("type", container_type)
    return container
Exemple #4
0
    def importProfile(self, file_name: str) -> Dict[str, str]:
        """Imports a profile from a file

        :param file_name: The full path and filename of the profile to import.
        :return: Dict with a 'status' key containing the string 'ok' or 'error',
            and a 'message' key containing a message for the user.
        """

        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", default = "0")))
                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)}
Exemple #5
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
    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.setMetaDataEntry("type", "profile")
        # don't know what quality_type it is based on, so use "normal" by default
        profile.setMetaDataEntry("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]
Exemple #7
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
Exemple #8
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
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)
    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
Exemple #11
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
def intent_container():
    container = InstanceContainer(container_id="intent container")
    container.setMetaDataEntry("type", "intent")

    return container
Exemple #13
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
Exemple #14
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
Exemple #15
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.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
Exemple #16
0
def global_variant():
    container = InstanceContainer(container_id="global_variant")
    container.setMetaDataEntry("type", "variant")

    return container
Exemple #17
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)
Exemple #18
0
def material_instance_container():
    container = InstanceContainer(container_id="material container")
    container.setMetaDataEntry("type", "material")

    return container
Exemple #19
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
Exemple #20
0
def quality_changes_container():
    container = InstanceContainer(container_id="quality changes container")
    container.setMetaDataEntry("type", "quality_changes")

    return container
Exemple #21
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]
Exemple #22
0
    def read(self, file_name):
        """Reads a legacy Cura profile from a file and returns it.

        :param file_name: The file to read the legacy Cura profile from.
        :return: The legacy Cura profile that was in the file, if any. If the
            file could not be read or didn't contain a valid profile,  None is returned.
        """

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

        input_parser = configparser.ConfigParser(interpolation = None)
        try:
            input_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 input_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(input_parser, section, defaults) #Gets the settings from the legacy profile.

        # Serialised format into version 4.5. Do NOT upgrade this, let the version upgrader handle it.
        output_parser = configparser.ConfigParser(interpolation = None)
        output_parser.add_section("general")
        output_parser.add_section("metadata")
        output_parser.add_section("values")

        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()
        output_parser["general"]["definition"] = 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.
                    output_parser["values"][new_setting] = str(new_value) # Store the setting in the profile!

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

        output_parser["general"]["version"] = "4"
        output_parser["general"]["name"] = profile_id
        output_parser["metadata"]["type"] = "quality_changes"
        output_parser["metadata"]["quality_type"] = "normal" # Don't know what quality_type it is based on, so use "normal" by default.
        output_parser["metadata"]["position"] = "0" # We only support single extrusion.
        output_parser["metadata"]["setting_version"] = "5" # What the dictionary of doom is made for.

        # Serialise in order to perform the version upgrade.
        stream = io.StringIO()
        output_parser.write(stream)
        data = stream.getvalue()

        profile = InstanceContainer(profile_id)
        profile.deserialize(data, file_name) # Also performs the version upgrade.
        profile.setDirty(True)

        #We need to return one extruder stack and one global stack.
        global_container_id = container_registry.uniqueName("Global Imported Legacy Profile")
        # We duplicate the extruder profile into the global stack.
        # This may introduce some settings that are global in the extruder stack and some settings that are per-extruder in the global stack.
        # We don't care about that. The engine will ignore them anyway.
        global_profile = profile.duplicate(new_id = global_container_id, new_name = profile_id) #Needs to have the same name as the extruder profile.
        del global_profile.getMetaData()["position"] # Has no position because it's global.
        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]