VersionUpgrade.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import configparser #To get version numbers from config files.
  4. import io
  5. import os
  6. import os.path
  7. from typing import Dict, List, Optional, Tuple
  8. from UM.Resources import Resources
  9. from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin.
  10. import UM.VersionUpgrade
  11. class VersionUpgrade22to24(VersionUpgrade):
  12. def upgradeMachineInstance(self, serialised: str, filename: str) -> Optional[Tuple[List[str], List[str]]]:
  13. # All of this is needed to upgrade custom variant machines from old Cura to 2.4 where
  14. # `definition_changes` instance container has been introduced. Variant files which
  15. # look like the the handy work of the old machine settings plugin are converted directly
  16. # on disk.
  17. config = configparser.ConfigParser(interpolation = None)
  18. config.read_string(serialised) # Read the input string as config file.
  19. if config.get("metadata", "type") == "definition_changes":
  20. # This is not a container stack, don't upgrade it here
  21. return None
  22. config.set("general", "version", "3")
  23. container_list = [] # type: List[str]
  24. if config.has_section("containers"):
  25. for index, container_id in config.items("containers"):
  26. container_list.append(container_id)
  27. elif config.has_option("general", "containers"):
  28. containers = config.get("general", "containers")
  29. container_list = containers.split(",")
  30. user_variants = self.__getUserVariants()
  31. name_path_dict = {}
  32. for variant in user_variants:
  33. name_path_dict[variant["name"]] = variant["path"]
  34. user_variant_names = set(container_list).intersection(name_path_dict.keys())
  35. if len(user_variant_names):
  36. # One of the user defined variants appears in the list of containers in the stack.
  37. for variant_name in user_variant_names: # really there should just be one variant to convert.
  38. config_name = self.__convertVariant(name_path_dict[variant_name])
  39. # Change the name of variant and insert empty_variant into the stack.
  40. new_container_list = []
  41. for item in container_list:
  42. if not item: # the last item may be an empty string
  43. continue
  44. if item == variant_name:
  45. new_container_list.append("empty_variant")
  46. new_container_list.append(config_name)
  47. else:
  48. new_container_list.append(item)
  49. container_list = new_container_list
  50. if not config.has_section("containers"):
  51. config.add_section("containers")
  52. config.remove_option("general", "containers")
  53. for idx in range(len(container_list)):
  54. config.set("containers", str(idx), container_list[idx])
  55. output = io.StringIO()
  56. config.write(output)
  57. return [filename], [output.getvalue()]
  58. def __convertVariant(self, variant_path: str) -> str:
  59. # Copy the variant to the machine_instances/*_settings.inst.cfg
  60. variant_config = configparser.ConfigParser(interpolation = None)
  61. with open(variant_path, "r", encoding = "utf-8") as fhandle:
  62. variant_config.read_file(fhandle)
  63. config_name = "Unknown Variant"
  64. if variant_config.has_section("general") and variant_config.has_option("general", "name"):
  65. config_name = variant_config.get("general", "name")
  66. if config_name.endswith("_variant"):
  67. config_name = config_name[:-len("_variant")] + "_settings"
  68. variant_config.set("general", "name", config_name)
  69. if not variant_config.has_section("metadata"):
  70. variant_config.add_section("metadata")
  71. variant_config.set("metadata", "type", "definition_changes")
  72. resource_path = Resources.getDataStoragePath()
  73. machine_instances_dir = os.path.join(resource_path, "machine_instances")
  74. if variant_path.endswith("_variant.inst.cfg"):
  75. variant_path = variant_path[:-len("_variant.inst.cfg")] + "_settings.inst.cfg"
  76. with open(os.path.join(machine_instances_dir, os.path.basename(variant_path)), "w", encoding = "utf-8") as fp:
  77. variant_config.write(fp)
  78. return config_name
  79. def __getUserVariants(self) -> List[Dict[str, str]]:
  80. resource_path = Resources.getDataStoragePath()
  81. variants_dir = os.path.join(resource_path, "variants")
  82. result = []
  83. for entry in os.scandir(variants_dir):
  84. if entry.name.endswith(".inst.cfg") and entry.is_file():
  85. config = configparser.ConfigParser(interpolation = None)
  86. with open(entry.path, "r", encoding = "utf-8") as fhandle:
  87. config.read_file(fhandle)
  88. if config.has_section("general") and config.has_option("general", "name"):
  89. result.append( { "path": entry.path, "name": config.get("general", "name") } )
  90. return result
  91. def upgradeExtruderTrain(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
  92. config = configparser.ConfigParser(interpolation = None)
  93. config.read_string(serialised) # Read the input string as config file.
  94. config.set("general", "version", "3") # Just bump the version number. That is all we need for now.
  95. output = io.StringIO()
  96. config.write(output)
  97. return [filename], [output.getvalue()]
  98. def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
  99. config = configparser.ConfigParser(interpolation = None)
  100. config.read_string(serialised)
  101. if not config.has_section("general"):
  102. raise UM.VersionUpgrade.FormatException("No \"general\" section.")
  103. # Make z_seam_x and z_seam_y options visible. In a clean 2.4 they are visible by default.
  104. if config.has_option("general", "visible_settings"):
  105. visible_settings = config.get("general", "visible_settings")
  106. visible_set = set(visible_settings.split(";"))
  107. visible_set.add("z_seam_x")
  108. visible_set.add("z_seam_y")
  109. config.set("general", "visible_settings", ";".join(visible_set))
  110. config.set("general", "version", value="4")
  111. output = io.StringIO()
  112. config.write(output)
  113. return [filename], [output.getvalue()]
  114. def upgradeQuality(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]:
  115. config = configparser.ConfigParser(interpolation = None)
  116. config.read_string(serialised) # Read the input string as config file.
  117. config.set("metadata", "type", "quality_changes") # Update metadata/type to quality_changes
  118. config.set("general", "version", "2") # Just bump the version number. That is all we need for now.
  119. output = io.StringIO()
  120. config.write(output)
  121. return [filename], [output.getvalue()]
  122. def getCfgVersion(self, serialised: str) -> int:
  123. parser = configparser.ConfigParser(interpolation = None)
  124. parser.read_string(serialised)
  125. format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
  126. setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
  127. return format_version * 1000000 + setting_version