123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- import os
- import os.path
- import re
- import configparser
- from typing import Optional
- from PyQt5.QtWidgets import QMessageBox
- from UM.Decorators import override
- from UM.Settings.ContainerRegistry import ContainerRegistry
- from UM.Settings.ContainerStack import ContainerStack
- from UM.Settings.InstanceContainer import InstanceContainer
- from UM.Settings.SettingInstance import SettingInstance
- from UM.Application import Application
- from UM.Logger import Logger
- from UM.Message import Message
- from UM.Platform import Platform
- from UM.PluginRegistry import PluginRegistry
- from UM.Util import parseBool
- from UM.Resources import Resources
- from . import ExtruderStack
- from . import GlobalStack
- from .ContainerManager import ContainerManager
- from .ExtruderManager import ExtruderManager
- from cura.CuraApplication import CuraApplication
- from UM.i18n import i18nCatalog
- catalog = i18nCatalog("cura")
- class CuraContainerRegistry(ContainerRegistry):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
-
-
- self.containerAdded.connect(self._onContainerAdded)
-
-
-
-
-
-
- @override(ContainerRegistry)
- def addContainer(self, container):
-
- if type(container) == ContainerStack:
- container = self._convertContainerStack(container)
- if isinstance(container, InstanceContainer) and type(container) != type(self.getEmptyInstanceContainer()):
-
- required_setting_version = CuraApplication.SettingVersion
- actual_setting_version = int(container.getMetaDataEntry("setting_version", default = 0))
- if required_setting_version != actual_setting_version:
- Logger.log("w", "Instance container {container_id} is outdated. Its setting version is {actual_setting_version} but it should be {required_setting_version}.".format(container_id = container.getId(), actual_setting_version = actual_setting_version, required_setting_version = required_setting_version))
- return
- super().addContainer(container)
-
-
-
-
-
-
- def createUniqueName(self, container_type, current_name, new_name, fallback_name):
- new_name = new_name.strip()
- num_check = re.compile("(.*?)\s*#\d+$").match(new_name)
- if num_check:
- new_name = num_check.group(1)
- if new_name == "":
- new_name = fallback_name
- unique_name = new_name
- i = 1
-
- while self._containerExists(container_type, unique_name) and unique_name != current_name:
- i += 1
- unique_name = "%s #%d" % (new_name, i)
- return unique_name
-
-
-
-
- def _containerExists(self, container_type, container_name):
- container_class = ContainerStack if container_type == "machine" else InstanceContainer
- return self.findContainersMetadata(id = container_name, type = container_type, ignore_case = True) or \
- self.findContainersMetadata(container_type = container_class, name = container_name, type = container_type)
-
-
-
-
-
- def exportProfile(self, instance_ids, file_name, file_type):
-
-
- split = file_type.rfind(" (*.")
- if split < 0:
- Logger.log("e", "Invalid file format identifier %s", file_type)
- return
- description = file_type[:split]
- extension = file_type[split + 4:-1]
- if not file_name.endswith("." + extension):
- file_name += "." + extension
-
- if not Platform.isWindows():
- if os.path.exists(file_name):
- result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
- catalog.i18nc("@label Don't translate the XML tag <filename>!", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_name))
- if result == QMessageBox.No:
- return
- found_containers = []
- extruder_positions = []
- for instance_id in instance_ids:
- containers = ContainerRegistry.getInstance().findInstanceContainers(id = instance_id)
- if containers:
- found_containers.append(containers[0])
-
- extruder_id = containers[0].getMetaDataEntry("extruder", "")
- if extruder_id == "":
-
- extruder_positions.append(-1)
- else:
- extruder_containers = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = extruder_id)
- if extruder_containers:
- extruder_positions.append(int(extruder_containers[0].get("position", 0)))
- else:
- extruder_positions.append(0)
-
- found_containers = [containers for (positions, containers) in sorted(zip(extruder_positions, found_containers))]
- profile_writer = self._findProfileWriter(extension, description)
- try:
- success = profile_writer.write(file_name, found_containers)
- except Exception as e:
- Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
- m = Message(catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>", file_name, str(e)),
- lifetime = 0,
- title = catalog.i18nc("@info:title", "Error"))
- m.show()
- return
- if not success:
- Logger.log("w", "Failed to export profile to %s: Writer plugin reported failure.", file_name)
- m = Message(catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.", file_name),
- lifetime = 0,
- title = catalog.i18nc("@info:title", "Error"))
- m.show()
- return
- m = Message(catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Exported profile to <filename>{0}</filename>", file_name),
- title = catalog.i18nc("@info:title", "Export succeeded"))
- m.show()
-
-
-
-
- def _findProfileWriter(self, extension, description):
- plugin_registry = PluginRegistry.getInstance()
- for plugin_id, meta_data in self._getIOPlugins("profile_writer"):
- for supported_type in meta_data["profile_writer"]:
- supported_extension = supported_type.get("extension", None)
- if supported_extension == extension:
- supported_description = supported_type.get("description", None)
- if supported_description == description:
- return plugin_registry.getPluginObject(plugin_id)
- return None
-
-
-
-
-
- def importProfile(self, file_name):
- Logger.log("d", "Attempting to import profile %s", file_name)
- if not file_name:
- return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "Invalid path")}
- plugin_registry = PluginRegistry.getInstance()
- extension = file_name.split(".")[-1]
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if not global_container_stack:
- return
- machine_extruders = list(ExtruderManager.getInstance().getMachineExtruders(global_container_stack.getId()))
- machine_extruders.sort(key = lambda k: k.getMetaDataEntry("position"))
- for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
- if meta_data["profile_reader"][0]["extension"] != extension:
- continue
- profile_reader = plugin_registry.getPluginObject(plugin_id)
- try:
- profile_or_list = profile_reader.read(file_name)
- except Exception as e:
-
- Logger.log("e", "Failed to import profile from %s: %s while using profile reader. Got exception %s", file_name,profile_reader.getPluginId(), str(e))
- return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))}
- if profile_or_list:
- name_seed = os.path.splitext(os.path.basename(file_name))[0]
- new_name = self.uniqueName(name_seed)
-
- if type(profile_or_list) is not list:
- profile_or_list = [profile_or_list]
-
- for profile_index, profile in enumerate(profile_or_list):
- if profile_index == 0:
-
- profile_id = (global_container_stack.getBottom().getId() + "_" + name_seed).lower().replace(" ", "_")
- elif profile_index < len(machine_extruders) + 1:
-
- extruder_id = Application.getInstance().getMachineManager().getQualityDefinitionId(machine_extruders[profile_index - 1].getBottom())
- if not profile.getMetaDataEntry("extruder"):
- profile.addMetaDataEntry("extruder", extruder_id)
- else:
- profile.setMetaDataEntry("extruder", extruder_id)
- profile_id = (extruder_id + "_" + name_seed).lower().replace(" ", "_")
- else:
- continue
- result = self._configureProfile(profile, profile_id, new_name)
- if result is not None:
- return {"status": "error", "message": catalog.i18nc(
- "@info:status Don't translate the XML tags <filename> or <message>!",
- "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
- file_name, result)}
- return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}
-
- return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)}
- @override(ContainerRegistry)
- def load(self):
- super().load()
- self._registerSingleExtrusionMachinesExtruderStacks()
- self._connectUpgradedExtruderStacksToMachines()
-
-
-
-
-
-
-
- def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]:
- profile.setDirty(True)
- new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile"))
- profile._id = new_id
- profile.setName(new_name)
- if "type" in profile.getMetaData():
- profile.setMetaDataEntry("type", "quality_changes")
- else:
- profile.addMetaDataEntry("type", "quality_changes")
- quality_type = profile.getMetaDataEntry("quality_type")
- if not quality_type:
- return catalog.i18nc("@info:status", "Profile is missing a quality type.")
- quality_type_criteria = {"quality_type": quality_type}
- if self._machineHasOwnQualities():
- profile.setDefinition(self._activeQualityDefinition().getId())
- if self._machineHasOwnMaterials():
- active_material_id = self._activeMaterialId()
- if active_material_id and active_material_id != "empty":
- profile.addMetaDataEntry("material", active_material_id)
- quality_type_criteria["material"] = active_material_id
- quality_type_criteria["definition"] = profile.getDefinition().getId()
- else:
- profile.setDefinition("fdmprinter")
- quality_type_criteria["definition"] = "fdmprinter"
- machine_definition = Application.getInstance().getGlobalContainerStack().getBottom()
- del quality_type_criteria["definition"]
-
- if "material" in quality_type_criteria:
-
- del quality_type_criteria["material"]
-
-
- materials = None
-
-
-
- from cura.QualityManager import QualityManager
- qualities = QualityManager.getInstance()._getFilteredContainersForStack(machine_definition, materials, **quality_type_criteria)
- if not qualities:
- return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type)
- ContainerRegistry.getInstance().addContainer(profile)
- return None
-
-
- def _getIOPlugins(self, io_type):
- plugin_registry = PluginRegistry.getInstance()
- active_plugin_ids = plugin_registry.getActivePlugins()
- result = []
- for plugin_id in active_plugin_ids:
- meta_data = plugin_registry.getMetaData(plugin_id)
- if io_type in meta_data:
- result.append( (plugin_id, meta_data) )
- return result
-
-
- def _activeQualityDefinition(self):
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if global_container_stack:
- definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(global_container_stack.getBottom())
- definition = self.findDefinitionContainers(id = definition_id)[0]
- if definition:
- return definition
- return None
-
-
- def _machineHasOwnMaterials(self):
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if global_container_stack:
- return global_container_stack.getMetaDataEntry("has_materials", False)
- return False
-
-
- def _activeMaterialId(self):
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if global_container_stack and global_container_stack.material:
- return global_container_stack.material.getId()
- return ""
-
-
- def _machineHasOwnQualities(self):
- global_container_stack = Application.getInstance().getGlobalContainerStack()
- if global_container_stack:
- return parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", False))
- return False
-
- def _convertContainerStack(self, container):
- assert type(container) == ContainerStack
- container_type = container.getMetaDataEntry("type")
- if container_type not in ("extruder_train", "machine"):
-
- return container
- Logger.log("d", "Converting ContainerStack {stack} to {type}", stack = container.getId(), type = container_type)
- new_stack = None
- if container_type == "extruder_train":
- new_stack = ExtruderStack.ExtruderStack(container.getId())
- else:
- new_stack = GlobalStack.GlobalStack(container.getId())
- container_contents = container.serialize()
- new_stack.deserialize(container_contents)
-
- if os.path.isfile(container.getPath()):
- os.remove(container.getPath())
- return new_stack
- def _registerSingleExtrusionMachinesExtruderStacks(self):
- machines = self.findContainerStacks(type = "machine", machine_extruder_trains = {"0": "fdmextruder"})
- for machine in machines:
- extruder_stacks = self.findContainerStacks(type = "extruder_train", machine = machine.getId())
- if not extruder_stacks:
- self.addExtruderStackForSingleExtrusionMachine(machine, "fdmextruder")
- def _onContainerAdded(self, container):
-
-
-
- if not isinstance(container, ContainerStack) or container.getMetaDataEntry("type") != "machine":
- return
- machine_extruder_trains = container.getMetaDataEntry("machine_extruder_trains")
- if machine_extruder_trains is not None and machine_extruder_trains != {"0": "fdmextruder"}:
- return
- extruder_stacks = self.findContainerStacks(type = "extruder_train", machine = container.getId())
- if not extruder_stacks:
- self.addExtruderStackForSingleExtrusionMachine(container, "fdmextruder")
- def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id):
- new_extruder_id = extruder_id
- extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
- if not extruder_definitions:
- Logger.log("w", "Could not find definition containers for extruder %s", new_extruder_id)
- return
- extruder_definition = extruder_definitions[0]
- unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id)
- extruder_stack = ExtruderStack.ExtruderStack(unique_name)
- extruder_stack.setName(extruder_definition.getName())
- extruder_stack.setDefinition(extruder_definition)
- extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
- from cura.CuraApplication import CuraApplication
-
- definition_changes_id = self.uniqueName(extruder_stack.getId() + "_settings")
- definition_changes_name = definition_changes_id
- definition_changes = InstanceContainer(definition_changes_id)
- definition_changes.setName(definition_changes_name)
- definition_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
- definition_changes.addMetaDataEntry("type", "definition_changes")
- definition_changes.addMetaDataEntry("definition", extruder_definition.getId())
-
- for setting_key in definition_changes.getAllKeys():
- if machine.definition.getProperty(setting_key, "settable_per_extruder"):
- setting_value = machine.definitionChanges.getProperty(setting_key, "value")
- if setting_value is not None:
-
- setting_definition = machine.getSettingDefinition(setting_key)
- new_instance = SettingInstance(setting_definition, definition_changes)
- new_instance.setProperty("value", setting_value)
- new_instance.resetState()
- definition_changes.addInstance(new_instance)
- definition_changes.setDirty(True)
- machine.definitionChanges.removeInstance(setting_key, postpone_emit = True)
- self.addContainer(definition_changes)
- extruder_stack.setDefinitionChanges(definition_changes)
-
- user_container_id = self.uniqueName(extruder_stack.getId() + "_user")
- user_container_name = user_container_id
- user_container = InstanceContainer(user_container_id)
- user_container.setName(user_container_name)
- user_container.addMetaDataEntry("type", "user")
- user_container.addMetaDataEntry("machine", extruder_stack.getId())
- user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
- user_container.setDefinition(machine.definition.getId())
- if machine.userChanges:
-
-
- for user_setting_key in machine.userChanges.getAllKeys():
- settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder")
- if settable_per_extruder:
- setting_value = machine.getProperty(user_setting_key, "value")
- setting_definition = machine.getSettingDefinition(user_setting_key)
- new_instance = SettingInstance(setting_definition, definition_changes)
- new_instance.setProperty("value", setting_value)
- new_instance.resetState()
- user_container.addInstance(new_instance)
- user_container.setDirty(True)
- machine.userChanges.removeInstance(user_setting_key, postpone_emit = True)
- self.addContainer(user_container)
- extruder_stack.setUserChanges(user_container)
- variant_id = "default"
- if machine.variant.getId() not in ("empty", "empty_variant"):
- variant_id = machine.variant.getId()
- else:
- variant_id = "empty_variant"
- extruder_stack.setVariantById(variant_id)
- material_id = "default"
- if machine.material.getId() not in ("empty", "empty_material"):
- material_id = machine.material.getId()
- else:
- material_id = "empty_material"
- extruder_stack.setMaterialById(material_id)
- quality_id = "default"
- if machine.quality.getId() not in ("empty", "empty_quality"):
- quality_id = machine.quality.getId()
- else:
- quality_id = "empty_quality"
- extruder_stack.setQualityById(quality_id)
- if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
- extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id)
- if extruder_quality_changes_container:
- extruder_quality_changes_container = extruder_quality_changes_container[0]
- quality_changes_id = extruder_quality_changes_container.getId()
- extruder_stack.setQualityChangesById(quality_changes_id)
- else:
-
-
-
- extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName())
- if extruder_quality_changes_container:
- quality_changes_id = extruder_quality_changes_container.getId()
- extruder_stack.setQualityChangesById(quality_changes_id)
- if not extruder_quality_changes_container:
- Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]",
- machine.qualityChanges.getName(), extruder_stack.getId())
- else:
- extruder_stack.setQualityChangesById("empty_quality_changes")
- self.addContainer(extruder_stack)
-
- extruder_stack.setNextStack(machine)
- return extruder_stack
- def _findQualityChangesContainerInCuraFolder(self, name):
- quality_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.QualityInstanceContainer)
- instance_container = None
- for item in os.listdir(quality_changes_dir):
- file_path = os.path.join(quality_changes_dir, item)
- if not os.path.isfile(file_path):
- continue
- parser = configparser.ConfigParser()
- try:
- parser.read([file_path])
- except:
-
- continue
- if not parser.has_option("general", "name"):
- continue
- if parser["general"]["name"] == name:
-
- container_id = os.path.basename(file_path).replace(".inst.cfg", "")
- instance_container = InstanceContainer(container_id)
- with open(file_path, "r") as f:
- serialized = f.read()
- instance_container.deserialize(serialized, file_path)
- self.addContainer(instance_container)
- break
- return instance_container
-
-
-
-
-
- def _connectUpgradedExtruderStacksToMachines(self):
- extruder_stacks = self.findContainers(container_type = ExtruderStack.ExtruderStack)
- for extruder_stack in extruder_stacks:
- if extruder_stack.getNextStack():
-
- continue
- machines = ContainerRegistry.getInstance().findContainerStacks(id = extruder_stack.getMetaDataEntry("machine", ""))
- if machines:
- extruder_stack.setNextStack(machines[0])
- else:
- Logger.log("w", "Could not find machine {machine} for extruder {extruder}", machine = extruder_stack.getMetaDataEntry("machine"), extruder = extruder_stack.getId())
|