Lipu Fei 16764f4750 Create user containers for newly created custom FDM printer extruders
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.
2017-08-22 17:37:25 +02:00

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