mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-05 06:44:20 +08:00

CURA-4188 The newly created extruder stacks don't have user containers, so the user changes cannot be saved. This fix makes sure that in the upgrade, user containers will be created.
296 lines
13 KiB
Python
296 lines
13 KiB
Python
# Copyright (c) 2017 Ultimaker B.V.
|
|
# Cura is released under the terms of the AGPLv3 or higher.
|
|
|
|
import configparser #To parse the files we need to upgrade and write the new files.
|
|
import io #To serialise configparser output to a string.
|
|
import os
|
|
from urllib.parse import quote_plus
|
|
|
|
from UM.Resources import Resources
|
|
from UM.VersionUpgrade import VersionUpgrade
|
|
|
|
from cura.CuraApplication import CuraApplication
|
|
|
|
_removed_settings = { #Settings that were removed in 2.5.
|
|
"start_layers_at_same_position",
|
|
"sub_div_rad_mult"
|
|
}
|
|
|
|
_split_settings = { #These settings should be copied to all settings it was split into.
|
|
"support_interface_line_distance": {"support_roof_line_distance", "support_bottom_line_distance"}
|
|
}
|
|
|
|
## A collection of functions that convert the configuration of the user in Cura
|
|
# 2.5 to a configuration for Cura 2.6.
|
|
#
|
|
# All of these methods are essentially stateless.
|
|
class VersionUpgrade25to26(VersionUpgrade):
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._current_fdm_printer_count = 2
|
|
|
|
## Gets the version number from a CFG file in Uranium's 2.5 format.
|
|
#
|
|
# Since the format may change, this is implemented for the 2.5 format only
|
|
# and needs to be included in the version upgrade system rather than
|
|
# globally in Uranium.
|
|
#
|
|
# \param serialised The serialised form of a CFG file.
|
|
# \return The version number stored in the CFG file.
|
|
# \raises ValueError The format of the version number in the file is
|
|
# incorrect.
|
|
# \raises KeyError The format of the file is incorrect.
|
|
def getCfgVersion(self, serialised):
|
|
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
|
|
|
|
## Upgrades the preferences file from version 2.5 to 2.6.
|
|
#
|
|
# \param serialised The serialised form of a preferences file.
|
|
# \param filename The name of the file to upgrade.
|
|
def upgradePreferences(self, serialised, filename):
|
|
parser = configparser.ConfigParser(interpolation = None)
|
|
parser.read_string(serialised)
|
|
|
|
#Remove settings from the visible_settings.
|
|
if parser.has_section("general") and "visible_settings" in parser["general"]:
|
|
visible_settings = parser["general"]["visible_settings"].split(";")
|
|
new_visible_settings = []
|
|
for setting in visible_settings:
|
|
if setting in _removed_settings:
|
|
continue #Skip.
|
|
if setting in _split_settings:
|
|
for replaced_setting in _split_settings[setting]:
|
|
new_visible_settings.append(replaced_setting)
|
|
continue #Don't add the original.
|
|
new_visible_settings.append(setting) #No special handling, so just add the original visible setting back.
|
|
parser["general"]["visible_settings"] = ";".join(new_visible_settings)
|
|
|
|
#Change the version number in the file.
|
|
if "general" not in parser:
|
|
parser["general"] = {}
|
|
parser.set("general", "version", "4")
|
|
|
|
if "metadata" not in parser:
|
|
parser["metadata"] = {}
|
|
parser.set("metadata", "setting_version", "1")
|
|
|
|
#Re-serialise the file.
|
|
output = io.StringIO()
|
|
parser.write(output)
|
|
return [filename], [output.getvalue()]
|
|
|
|
## Upgrades an instance container from version 2.5 to 2.6.
|
|
#
|
|
# \param serialised The serialised form of a quality profile.
|
|
# \param filename The name of the file to upgrade.
|
|
def upgradeInstanceContainer(self, serialised, filename):
|
|
parser = configparser.ConfigParser(interpolation = None)
|
|
parser.read_string(serialised)
|
|
|
|
#Remove settings from the [values] section.
|
|
if parser.has_section("values"):
|
|
for removed_setting in (_removed_settings & parser["values"].keys()): #Both in keys that need to be removed and in keys present in the file.
|
|
del parser["values"][removed_setting]
|
|
for replaced_setting in (_split_settings.keys() & parser["values"].keys()):
|
|
for replacement in _split_settings[replaced_setting]:
|
|
parser["values"][replacement] = parser["values"][replaced_setting] #Copy to replacement before removing the original!
|
|
del replaced_setting
|
|
|
|
for each_section in ("general", "metadata"):
|
|
if not parser.has_section(each_section):
|
|
parser.add_section(each_section)
|
|
|
|
# Update version numbers
|
|
parser["general"]["version"] = "2"
|
|
parser["metadata"]["setting_version"] = "1"
|
|
|
|
#Re-serialise the file.
|
|
output = io.StringIO()
|
|
parser.write(output)
|
|
return [filename], [output.getvalue()]
|
|
|
|
## Upgrades a machine stack from version 2.5 to 2.6
|
|
#
|
|
# \param serialised The serialised form of a quality profile.
|
|
# \param filename The name of the file to upgrade.
|
|
def upgradeMachineStack(self, serialised, filename):
|
|
parser = configparser.ConfigParser(interpolation=None)
|
|
parser.read_string(serialised)
|
|
|
|
# NOTE: This is for Custom FDM printers
|
|
# In 2.5, Custom FDM printers don't support multiple extruders, but since 2.6 they do.
|
|
machine_id = parser["general"]["id"]
|
|
quality_container_id = parser["containers"]["2"]
|
|
material_container_id = parser["containers"]["3"]
|
|
definition_container_id = parser["containers"]["6"]
|
|
|
|
if definition_container_id == "custom" and not self._checkCustomFdmPrinterHasExtruderStack(machine_id):
|
|
# go through all extruders and make sure that this custom FDM printer has 8 extruder stacks.
|
|
self._acquireNextUniqueCustomFdmPrinterExtruderStackIdIndex()
|
|
for position in range(8):
|
|
self._createCustomFdmPrinterExtruderStack(machine_id, position, quality_container_id, material_container_id)
|
|
|
|
# Update version numbers
|
|
parser["general"]["version"] = "3"
|
|
parser["metadata"]["setting_version"] = "1"
|
|
|
|
# Re-serialise the file.
|
|
output = io.StringIO()
|
|
parser.write(output)
|
|
|
|
return [filename], [output.getvalue()]
|
|
|
|
## Acquires the next unique extruder stack index number for the Custom FDM Printer.
|
|
def _acquireNextUniqueCustomFdmPrinterExtruderStackIdIndex(self):
|
|
extruder_stack_dir = Resources.getPath(CuraApplication.ResourceTypes.ExtruderStack)
|
|
file_name_list = os.listdir(extruder_stack_dir)
|
|
file_name_list = [os.path.basename(file_name) for file_name in file_name_list]
|
|
while True:
|
|
self._current_fdm_printer_count += 1
|
|
stack_id_exists = False
|
|
for position in range(8):
|
|
stack_id = "custom_extruder_%s" % (position + 1)
|
|
if self._current_fdm_printer_count > 1:
|
|
stack_id += " #%s" % self._current_fdm_printer_count
|
|
|
|
if stack_id in file_name_list:
|
|
stack_id_exists = True
|
|
break
|
|
if not stack_id_exists:
|
|
break
|
|
|
|
return self._current_fdm_printer_count
|
|
|
|
def _checkCustomFdmPrinterHasExtruderStack(self, machine_id):
|
|
# go through all extruders and make sure that this custom FDM printer has extruder stacks.
|
|
extruder_stack_dir = Resources.getPath(CuraApplication.ResourceTypes.ExtruderStack)
|
|
has_extruders = False
|
|
for item in os.listdir(extruder_stack_dir):
|
|
file_path = os.path.join(extruder_stack_dir, item)
|
|
if not os.path.isfile(file_path):
|
|
continue
|
|
|
|
parser = configparser.ConfigParser()
|
|
try:
|
|
parser.read([file_path])
|
|
except:
|
|
# skip, it is not a valid stack file
|
|
continue
|
|
|
|
if "metadata" not in parser:
|
|
continue
|
|
if "machine" not in parser["metadata"]:
|
|
continue
|
|
|
|
if machine_id != parser["metadata"]["machine"]:
|
|
continue
|
|
has_extruders = True
|
|
break
|
|
|
|
return has_extruders
|
|
|
|
def _createCustomFdmPrinterExtruderStack(self, machine_id: str, position: int, quality_id: str, material_id: str):
|
|
stack_id = "custom_extruder_%s" % (position + 1)
|
|
if self._current_fdm_printer_count > 1:
|
|
stack_id += " #%s" % self._current_fdm_printer_count
|
|
|
|
definition_id = "custom_extruder_%s" % (position + 1)
|
|
|
|
# create a definition changes container for this stack
|
|
definition_changes_parser = self._getCustomFdmPrinterDefinitionChanges(stack_id)
|
|
definition_changes_id = definition_changes_parser["general"]["name"]
|
|
# create a user settings container
|
|
user_settings_parser = self._getCustomFdmPrinterUserSettings(stack_id)
|
|
user_settings_id = user_settings_parser["general"]["name"]
|
|
|
|
parser = configparser.ConfigParser()
|
|
parser.add_section("general")
|
|
parser["general"]["version"] = str(2)
|
|
parser["general"]["name"] = "Extruder %s" % (position + 1)
|
|
parser["general"]["id"] = stack_id
|
|
|
|
parser.add_section("metadata")
|
|
parser["metadata"]["type"] = "extruder_train"
|
|
parser["metadata"]["machine"] = machine_id
|
|
parser["metadata"]["position"] = str(position)
|
|
|
|
parser.add_section("containers")
|
|
parser["containers"]["0"] = user_settings_id
|
|
parser["containers"]["1"] = "empty_quality_changes"
|
|
parser["containers"]["2"] = quality_id
|
|
parser["containers"]["3"] = material_id
|
|
parser["containers"]["4"] = "empty_variant"
|
|
parser["containers"]["5"] = definition_changes_id
|
|
parser["containers"]["6"] = definition_id
|
|
|
|
definition_changes_output = io.StringIO()
|
|
definition_changes_parser.write(definition_changes_output)
|
|
definition_changes_filename = quote_plus(definition_changes_id) + ".inst.cfg"
|
|
|
|
user_settings_output = io.StringIO()
|
|
user_settings_parser.write(user_settings_output)
|
|
user_settings_filename = quote_plus(user_settings_id) + ".inst.cfg"
|
|
|
|
extruder_output = io.StringIO()
|
|
parser.write(extruder_output)
|
|
extruder_filename = quote_plus(stack_id) + ".extruder.cfg"
|
|
|
|
extruder_stack_dir = Resources.getPath(CuraApplication.ResourceTypes.ExtruderStack)
|
|
definition_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.DefinitionChangesContainer)
|
|
user_settings_dir = Resources.getPath(CuraApplication.ResourceTypes.UserInstanceContainer)
|
|
|
|
with open(os.path.join(definition_changes_dir, definition_changes_filename), "w") as f:
|
|
f.write(definition_changes_output.getvalue())
|
|
with open(os.path.join(user_settings_dir, user_settings_filename), "w") as f:
|
|
f.write(user_settings_output.getvalue())
|
|
with open(os.path.join(extruder_stack_dir, extruder_filename), "w") as f:
|
|
f.write(extruder_output.getvalue())
|
|
|
|
## Creates a definition changes container which doesn't contain anything for the Custom FDM Printers.
|
|
# The container ID will be automatically generated according to the given stack name.
|
|
def _getCustomFdmPrinterDefinitionChanges(self, stack_id: str):
|
|
# In 2.5, there is no definition_changes container for the Custom FDM printer, so it should be safe to use the
|
|
# default name unless some one names the printer as something like "Custom FDM Printer_settings".
|
|
definition_changes_id = stack_id + "_settings"
|
|
|
|
parser = configparser.ConfigParser()
|
|
parser.add_section("general")
|
|
parser["general"]["version"] = str(2)
|
|
parser["general"]["name"] = definition_changes_id
|
|
parser["general"]["definition"] = "custom"
|
|
|
|
parser.add_section("metadata")
|
|
parser["metadata"]["type"] = "definition_changes"
|
|
parser["metadata"]["setting_version"] = str(1)
|
|
|
|
parser.add_section("values")
|
|
|
|
return parser
|
|
|
|
## Creates a user settings container which doesn't contain anything for the Custom FDM Printers.
|
|
# The container ID will be automatically generated according to the given stack name.
|
|
def _getCustomFdmPrinterUserSettings(self, stack_id: str):
|
|
# For the extruder stacks created in the upgrade, also create user_settings containers so the user changes
|
|
# will be saved.
|
|
user_settings_id = stack_id + "_user"
|
|
|
|
parser = configparser.ConfigParser()
|
|
parser.add_section("general")
|
|
parser["general"]["version"] = str(2)
|
|
parser["general"]["name"] = user_settings_id
|
|
parser["general"]["definition"] = "custom"
|
|
|
|
parser.add_section("metadata")
|
|
parser["metadata"]["extruder"] = stack_id
|
|
parser["metadata"]["type"] = "user"
|
|
parser["metadata"]["setting_version"] = str(1)
|
|
|
|
parser.add_section("values")
|
|
|
|
return parser
|