# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import configparser #To get version numbers from config files. import io import os import os.path from typing import Dict, List, Optional, Tuple from UM.Resources import Resources from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin. import UM.VersionUpgrade class VersionUpgrade22to24(VersionUpgrade): def upgradeMachineInstance(self, serialised: str, filename: str) -> Optional[Tuple[List[str], List[str]]]: # All of this is needed to upgrade custom variant machines from old Cura to 2.4 where # `definition_changes` instance container has been introduced. Variant files which # look like the the handy work of the old machine settings plugin are converted directly # on disk. config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) # Read the input string as config file. if config.get("metadata", "type") == "definition_changes": # This is not a container stack, don't upgrade it here return None config.set("general", "version", "3") container_list = [] # type: List[str] if config.has_section("containers"): for index, container_id in config.items("containers"): container_list.append(container_id) elif config.has_option("general", "containers"): containers = config.get("general", "containers") container_list = containers.split(",") user_variants = self.__getUserVariants() name_path_dict = {} for variant in user_variants: name_path_dict[variant["name"]] = variant["path"] user_variant_names = set(container_list).intersection(name_path_dict.keys()) if len(user_variant_names): # One of the user defined variants appears in the list of containers in the stack. for variant_name in user_variant_names: # really there should just be one variant to convert. config_name = self.__convertVariant(name_path_dict[variant_name]) # Change the name of variant and insert empty_variant into the stack. new_container_list = [] for item in container_list: if not item: # the last item may be an empty string continue if item == variant_name: new_container_list.append("empty_variant") new_container_list.append(config_name) else: new_container_list.append(item) container_list = new_container_list if not config.has_section("containers"): config.add_section("containers") config.remove_option("general", "containers") for idx in range(len(container_list)): config.set("containers", str(idx), container_list[idx]) output = io.StringIO() config.write(output) return [filename], [output.getvalue()] def __convertVariant(self, variant_path: str) -> str: # Copy the variant to the machine_instances/*_settings.inst.cfg variant_config = configparser.ConfigParser(interpolation = None) with open(variant_path, "r", encoding = "utf-8") as fhandle: variant_config.read_file(fhandle) config_name = "Unknown Variant" if variant_config.has_section("general") and variant_config.has_option("general", "name"): config_name = variant_config.get("general", "name") if config_name.endswith("_variant"): config_name = config_name[:-len("_variant")] + "_settings" variant_config.set("general", "name", config_name) if not variant_config.has_section("metadata"): variant_config.add_section("metadata") variant_config.set("metadata", "type", "definition_changes") resource_path = Resources.getDataStoragePath() machine_instances_dir = os.path.join(resource_path, "machine_instances") if variant_path.endswith("_variant.inst.cfg"): variant_path = variant_path[:-len("_variant.inst.cfg")] + "_settings.inst.cfg" with open(os.path.join(machine_instances_dir, os.path.basename(variant_path)), "w", encoding = "utf-8") as fp: variant_config.write(fp) return config_name def __getUserVariants(self) -> List[Dict[str, str]]: resource_path = Resources.getDataStoragePath() variants_dir = os.path.join(resource_path, "variants") result = [] for entry in os.scandir(variants_dir): if entry.name.endswith(".inst.cfg") and entry.is_file(): config = configparser.ConfigParser(interpolation = None) with open(entry.path, "r", encoding = "utf-8") as fhandle: config.read_file(fhandle) if config.has_section("general") and config.has_option("general", "name"): result.append( { "path": entry.path, "name": config.get("general", "name") } ) return result def upgradeExtruderTrain(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) # Read the input string as config file. config.set("general", "version", "3") # Just bump the version number. That is all we need for now. output = io.StringIO() config.write(output) return [filename], [output.getvalue()] def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) if not config.has_section("general"): raise UM.VersionUpgrade.FormatException("No \"general\" section.") # Make z_seam_x and z_seam_y options visible. In a clean 2.4 they are visible by default. if config.has_option("general", "visible_settings"): visible_settings = config.get("general", "visible_settings") visible_set = set(visible_settings.split(";")) visible_set.add("z_seam_x") visible_set.add("z_seam_y") config.set("general", "visible_settings", ";".join(visible_set)) config.set("general", "version", value="4") output = io.StringIO() config.write(output) return [filename], [output.getvalue()] def upgradeQuality(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) # Read the input string as config file. config.set("metadata", "type", "quality_changes") # Update metadata/type to quality_changes config.set("general", "version", "2") # Just bump the version number. That is all we need for now. output = io.StringIO() config.write(output) return [filename], [output.getvalue()] def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version