Export project settings into BambuLab 3MF

CURA-12101
The printer parses the machine_start_gcode to allow selecting the filaments mapping at start time, without it the user has to set the filaments in fixed order. This is probably a security to ensure the proper filament is loaded at start.
This commit is contained in:
Erwan MATHIEU 2025-04-29 11:28:56 +02:00
parent 52be6f3d2d
commit d2e625edb3
2 changed files with 32 additions and 14 deletions

View File

@ -3,7 +3,7 @@
from dataclasses import asdict
from typing import cast, Dict, TYPE_CHECKING
from typing import cast, Dict, TYPE_CHECKING, Any
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.SettingFunction import SettingFunction
@ -54,6 +54,15 @@ class Settings:
return self.application.getSidebarCustomMenuItems()
def getAllGlobalSettings(self) -> Dict[str, Any]:
global_stack = cast(GlobalStack, self.application.getGlobalContainerStack())
all_settings = {}
for setting in global_stack.getAllKeys():
all_settings[setting] = self._retrieveValue(global_stack, setting)
return all_settings
def getSliceMetadata(self) -> Dict[str, Dict[str, Dict[str, str]]]:
"""Get all changed settings and all settings. For each extruder and the global stack"""
print_information = self.application.getPrintInformation()
@ -71,24 +80,16 @@ class Settings:
"quality": asdict(machine_manager.activeQualityDisplayNameMap()),
}
def _retrieveValue(container: InstanceContainer, setting_: str):
value_ = container.getProperty(setting_, "value")
for _ in range(0, 1024): # Prevent possibly endless loop by not using a limit.
if not isinstance(value_, SettingFunction):
return value_ # Success!
value_ = value_(container)
return 0 # Fallback value after breaking possibly endless loop.
global_stack = cast(GlobalStack, self.application.getGlobalContainerStack())
# Add global user or quality changes
global_flattened_changes = InstanceContainer.createMergedInstanceContainer(global_stack.userChanges, global_stack.qualityChanges)
for setting in global_flattened_changes.getAllKeys():
settings["global"]["changes"][setting] = _retrieveValue(global_flattened_changes, setting)
settings["global"]["changes"][setting] = self._retrieveValue(global_flattened_changes, setting)
# Get global all settings values without user or quality changes
for setting in global_stack.getAllKeys():
settings["global"]["all_settings"][setting] = _retrieveValue(global_stack, setting)
settings["global"]["all_settings"][setting] = self._retrieveValue(global_stack, setting)
for i, extruder in enumerate(global_stack.extruderList):
# Add extruder fields to settings dictionary
@ -100,10 +101,19 @@ class Settings:
# Add extruder user or quality changes
extruder_flattened_changes = InstanceContainer.createMergedInstanceContainer(extruder.userChanges, extruder.qualityChanges)
for setting in extruder_flattened_changes.getAllKeys():
settings[f"extruder_{i}"]["changes"][setting] = _retrieveValue(extruder_flattened_changes, setting)
settings[f"extruder_{i}"]["changes"][setting] = self._retrieveValue(extruder_flattened_changes, setting)
# Get extruder all settings values without user or quality changes
for setting in extruder.getAllKeys():
settings[f"extruder_{i}"]["all_settings"][setting] = _retrieveValue(extruder, setting)
settings[f"extruder_{i}"]["all_settings"][setting] = self._retrieveValue(extruder, setting)
return settings
@staticmethod
def _retrieveValue(container: InstanceContainer, setting_: str):
value_ = container.getProperty(setting_, "value")
for _ in range(0, 1024): # Prevent possibly endless loop by not using a limit.
if not isinstance(value_, SettingFunction):
return value_ # Success!
value_ = value_(container)
return 0 # Fallback value after breaking possibly endless loop.

View File

@ -31,6 +31,7 @@ GCODE_MD5_PATH = f"{GCODE_PATH}.md5"
MODEL_SETTINGS_PATH = f"{METADATA_PATH}/model_settings.config"
PLATE_DESC_PATH = f"{METADATA_PATH}/plate_1.json"
SLICE_INFO_PATH = f"{METADATA_PATH}/slice_info.config"
PROJECT_SETTINGS_PATH = f"{METADATA_PATH}/project_settings.config"
class BambuLabVariant(ThreeMFVariant):
"""BambuLab specific implementation of the 3MF format."""
@ -48,7 +49,7 @@ class BambuLabVariant(ThreeMFVariant):
# Add relations elements for thumbnails
ET.SubElement(relations_element, "Relationship",
Target="/" + THUMBNAIL_PATH_MULTIPLATE, Id="rel-2",
Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
pe="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
ET.SubElement(relations_element, "Relationship",
Target="/" + THUMBNAIL_PATH_MULTIPLATE, Id="rel-4",
@ -74,6 +75,7 @@ class BambuLabVariant(ThreeMFVariant):
self._storeModelSettings(archive)
self._storePlateDesc(archive)
self._storeSliceInfo(archive)
self._storeProjectSettings(archive)
def _storeGCode(self, archive: zipfile.ZipFile, metadata_relations_element: ET.Element):
"""Store GCode data in the archive."""
@ -166,3 +168,9 @@ class BambuLabVariant(ThreeMFVariant):
used_g=str(used_g))
self._writer._storeElementTree(archive, SLICE_INFO_PATH, config)
def _storeProjectSettings(self, archive: zipfile.ZipFile):
api = CuraApplication.getInstance().getCuraAPI()
file = zipfile.ZipInfo(PROJECT_SETTINGS_PATH)
json_string = json.dumps(api.interface.settings.getAllGlobalSettings(), separators=(", ", ": "), indent=4)
archive.writestr(file, json_string.encode("UTF-8"))