Profile.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. # Copyright (c) 2016 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. import configparser #To read config files.
  4. import copy #To split config files into multiple config files.
  5. import io #To write config files to strings as if they were files.
  6. import UM.VersionUpgrade
  7. ## The materials in Cura 2.2.
  8. #
  9. # This is required to know how to split old profiles if the old machine didn't
  10. # have material-specific profiles but the new machine has. This cannot be read
  11. # from the current source directory since the current source directory may be
  12. # a later version than Cura 2.2, so it must be stored in the upgrade plug-in.
  13. _new_materials = {"generic_abs", "generic_cpe", "generic_pla", "generic_pva"}
  14. ## Creates a new profile instance by parsing a serialised profile in version 1
  15. # of the file format.
  16. #
  17. # \param serialised The serialised form of a profile in version 1.
  18. # \param filename The supposed filename of the profile, without extension.
  19. # \return A profile instance, or None if the file format is incorrect.
  20. def importFrom(serialised, filename):
  21. try:
  22. return Profile(serialised, filename)
  23. except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
  24. return None
  25. ## A representation of a profile used as intermediary form for conversion from
  26. # one format to the other.
  27. class Profile:
  28. ## Reads version 1 of the file format, storing it in memory.
  29. #
  30. # \param serialised A string with the contents of a profile.
  31. # \param filename The supposed filename of the profile, without extension.
  32. def __init__(self, serialised, filename):
  33. self._filename = filename
  34. parser = configparser.ConfigParser(interpolation = None)
  35. parser.read_string(serialised)
  36. # Check correctness.
  37. if not parser.has_section("general"):
  38. raise UM.VersionUpgrade.FormatException("No \"general\" section.")
  39. if not parser.has_option("general", "version"):
  40. raise UM.VersionUpgrade.FormatException("No \"version\" in the \"general\" section.")
  41. if int(parser.get("general", "version")) != 1: # Hard-coded profile version here. If this number changes the entire function needs to change.
  42. raise UM.VersionUpgrade.InvalidVersionException("The version of this profile is wrong. It must be 1.")
  43. # Parse the general section.
  44. self._name = parser.get("general", "name")
  45. self._type = parser.get("general", "type", fallback = None)
  46. if "weight" in parser["general"]:
  47. self._weight = int(parser.get("general", "weight"))
  48. else:
  49. self._weight = None
  50. self._machine_type_id = parser.get("general", "machine_type", fallback = None)
  51. self._machine_variant_name = parser.get("general", "machine_variant", fallback = None)
  52. self._machine_instance_name = parser.get("general", "machine_instance", fallback = None)
  53. if "material" in parser["general"]:
  54. self._material_name = parser.get("general", "material")
  55. elif self._type == "material":
  56. self._material_name = parser.get("general", "name", fallback = None)
  57. else:
  58. self._material_name = None
  59. # Parse the settings.
  60. self._settings = {}
  61. if parser.has_section("settings"):
  62. for key, value in parser["settings"].items():
  63. self._settings[key] = value
  64. # Parse the defaults and the disabled defaults.
  65. self._changed_settings_defaults = {}
  66. if parser.has_section("defaults"):
  67. for key, value in parser["defaults"].items():
  68. self._changed_settings_defaults[key] = value
  69. self._disabled_settings_defaults = []
  70. if parser.has_section("disabled_defaults"):
  71. disabled_defaults_string = parser.get("disabled_defaults", "values")
  72. self._disabled_settings_defaults = [item for item in disabled_defaults_string.split(",") if item != ""] # Split by comma.
  73. ## Serialises this profile as file format version 2.
  74. #
  75. # \return A tuple containing the new filename and a serialised form of
  76. # this profile, serialised in version 2 of the file format.
  77. def export(self):
  78. import VersionUpgrade21to22 # Import here to prevent circular dependencies.
  79. if self._name == "Current settings":
  80. self._filename += "_current_settings" #This resolves a duplicate ID arising from how Cura 2.1 stores its current settings.
  81. config = configparser.ConfigParser(interpolation = None)
  82. config.add_section("general")
  83. config.set("general", "version", "2") #Hard-coded profile version 2.
  84. config.set("general", "name", self._name)
  85. if self._machine_type_id:
  86. translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id)
  87. config.set("general", "definition", translated_machine)
  88. else:
  89. config.set("general", "definition", "fdmprinter")
  90. config.add_section("metadata")
  91. if self._type:
  92. config.set("metadata", "type", self._type)
  93. else:
  94. config.set("metadata", "type", "quality")
  95. if self._weight:
  96. config.set("metadata", "weight", self._weight)
  97. if self._machine_variant_name:
  98. if self._machine_type_id:
  99. config.set("metadata", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id))
  100. else:
  101. config.set("metadata", "variant", self._machine_variant_name)
  102. if self._settings:
  103. VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings)
  104. config.add_section("values")
  105. for key, value in self._settings.items():
  106. config.set("values", key, str(value))
  107. if self._changed_settings_defaults:
  108. VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._changed_settings_defaults)
  109. config.add_section("defaults")
  110. for key, value in self._changed_settings_defaults.items():
  111. config.set("defaults", key, str(value))
  112. if self._disabled_settings_defaults:
  113. disabled_settings_defaults = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting)
  114. for setting in self._disabled_settings_defaults]
  115. config.add_section("disabled_defaults")
  116. disabled_defaults_string = str(disabled_settings_defaults[0]) #Must be at least 1 item, otherwise we wouldn't enter this if statement.
  117. for item in disabled_settings_defaults[1:]:
  118. disabled_defaults_string += "," + str(item)
  119. #Material metadata may cause the file to split, so do it last to minimise processing time (do more with the copy).
  120. filenames = []
  121. configs = []
  122. if self._material_name and self._type != "material":
  123. config.set("metadata", "material", self._material_name)
  124. filenames.append(self._filename)
  125. configs.append(config)
  126. elif self._type != "material" and self._machine_type_id in VersionUpgrade21to22.machines_with_machine_quality():
  127. #Split this profile into multiple profiles, one for each material.
  128. for material_id in _new_materials:
  129. filenames.append("{profile}_{material}".format(profile = self._filename, material = material_id))
  130. config_copy = copy.copy(config)
  131. config_copy.set("metadata", "material", material_id)
  132. configs.append(config_copy)
  133. outputs = []
  134. for config in configs:
  135. output = io.StringIO()
  136. config.write(output)
  137. outputs.append(output.getvalue())
  138. return filenames, outputs