diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 44d4761321..dc0a65be1d 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -294,21 +294,25 @@ class CuraApplication(QtApplication): # Since they are empty, they should never be serialized and instead just programmatically created. # We need them to simplify the switching between materials. empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() + self.empty_container = empty_container empty_definition_changes_container = copy.deepcopy(empty_container) empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes") empty_definition_changes_container.addMetaDataEntry("type", "definition_changes") ContainerRegistry.getInstance().addContainer(empty_definition_changes_container) + self.empty_definition_changes_container = empty_definition_changes_container empty_variant_container = copy.deepcopy(empty_container) empty_variant_container.setMetaDataEntry("id", "empty_variant") empty_variant_container.addMetaDataEntry("type", "variant") ContainerRegistry.getInstance().addContainer(empty_variant_container) + self.empty_variant_container = empty_variant_container empty_material_container = copy.deepcopy(empty_container) empty_material_container.setMetaDataEntry("id", "empty_material") empty_material_container.addMetaDataEntry("type", "material") ContainerRegistry.getInstance().addContainer(empty_material_container) + self.empty_material_container = empty_material_container empty_quality_container = copy.deepcopy(empty_container) empty_quality_container.setMetaDataEntry("id", "empty_quality") @@ -317,12 +321,14 @@ class CuraApplication(QtApplication): empty_quality_container.addMetaDataEntry("type", "quality") empty_quality_container.addMetaDataEntry("supported", False) ContainerRegistry.getInstance().addContainer(empty_quality_container) + self.empty_quality_container = empty_quality_container empty_quality_changes_container = copy.deepcopy(empty_container) empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes") empty_quality_changes_container.addMetaDataEntry("type", "quality_changes") empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported") ContainerRegistry.getInstance().addContainer(empty_quality_changes_container) + self.empty_quality_changes_container = empty_quality_changes_container with ContainerRegistry.getInstance().lockFile(): ContainerRegistry.getInstance().loadAllMetadata() diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index a719a7df8c..ee8b9c8922 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -257,7 +257,7 @@ class QualityManager(QObject): return quality_changes_group_dict def getQualityGroups(self, machine: "GlobalStack") -> dict: - # TODO: How to make this simpler, including the fallbacks. + # TODO: How to make this simpler, including the fall backs. # Get machine definition ID machine_definition_id = self._default_machine_definition_id if parseBool(machine.getMetaDataEntry("has_machine_quality", False)): @@ -270,8 +270,13 @@ class QualityManager(QObject): raise RuntimeError("Cannot find node for machine def [%s] in Quality lookup table" % machine_definition_id) # iterate over all quality_types in the machine node + node_to_fetch_global = machine_node quality_group_dict = {} - for quality_type, quality_node in machine_node.quality_type_map.items(): + if not node_to_fetch_global.quality_type_map: + # Fallback mechanism: + # If there is no machine-specific quality, fallback to use the default fdmprinter's. + node_to_fetch_global = self._machine_variant_material_quality_type_to_quality_dict.get(self._default_machine_definition_id) + for quality_type, quality_node in node_to_fetch_global.quality_type_map.items(): quality_group = QualityGroup(quality_node.metadata["name"], quality_type) quality_group.node_for_global = quality_node diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 608a13c600..365658ee48 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -6,6 +6,7 @@ from UM.Logger import Logger from UM.Settings.Interfaces import DefinitionContainerInterface from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Util import parseBool from .GlobalStack import GlobalStack from .ExtruderStack import ExtruderStack @@ -22,7 +23,13 @@ class CuraStackBuilder: # \return The new global stack or None if an error occurred. @classmethod def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]: + from cura.CuraApplication import CuraApplication + application = CuraApplication.getInstance() + variant_manager = CuraApplication.getInstance()._variant_manager + material_manager = CuraApplication.getInstance()._material_manager + quality_manager = CuraApplication.getInstance()._quality_manager registry = ContainerRegistry.getInstance() + definitions = registry.findDefinitionContainers(id = definition_id) if not definitions: Logger.log("w", "Definition {definition} was not found!", definition = definition_id) @@ -30,7 +37,33 @@ class CuraStackBuilder: machine_definition = definitions[0] - generated_name = registry.createUniqueName("machine", "", name, machine_definition.name) + # get variant container for extruders + variant_container = application.empty_variant_container + # Only look for the preferred variant if this machine has variants + variant_name = None + if parseBool(machine_definition.getMetaDataEntry("has_variants", False)): + variant_name = machine_definition.getMetaDataEntry("preferred_variant_name") + if variant_name: + variant_node = variant_manager.getVariant(definition_id, variant_name) + # Sanity check. If you see this error, the related definition files should be fixed. + if variant_node is None: + raise RuntimeError("Cannot find variant with definition [%s] and variant name [%s]" % (definition_id, variant_name)) + variant_container = variant_node.getContainer() + + # get material container for extruders + material_container = application.empty_material_container + # Only look for the preferred material if this machine has materials + if parseBool(machine_definition.getMetaDataEntry("has_materials", False)): + material_diameter = machine_definition.getProperty("material_diameter", "value") + root_material_id = machine_definition.getMetaDataEntry("preferred_material") + material_node = material_manager.getMaterialNode(definition_id, variant_name, material_diameter, root_material_id) + # Sanity check. If you see this error, the related definition files should be fixed. + if not material_node: + raise RuntimeError("Cannot find material with definition [%s], variant_name [%s], and root_material_id [%s]" % + (definition_id, variant_name, root_material_id)) + material_container = material_node.getContainer() + + generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName()) # Make sure the new name does not collide with any definition or (quality) profile # createUniqueName() only looks at other stacks, but not at definitions or quality profiles # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true @@ -40,49 +73,45 @@ class CuraStackBuilder: new_global_stack = cls.createGlobalStack( new_stack_id = generated_name, definition = machine_definition, - quality = "default", - material = "default", - variant = "default", + variant_container = application.empty_variant_container, # TODO: fix for build plate + material_container = application.empty_material_container, + quality_container = application.empty_quality_container, ) - new_global_stack.setName(generated_name) - extruder_definition = registry.findDefinitionContainers(machine = machine_definition.getId()) + # Create ExtruderStacks + extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains") - if not extruder_definition: - # create extruder stack for single extrusion machines that have no separate extruder definition files - extruder_definition = registry.findDefinitionContainers(id = "fdmextruder")[0] - new_extruder_id = registry.uniqueName(machine_definition.getName() + " " + extruder_definition.id) + for position, extruder_definition_id in extruder_dict.items(): + # Sanity check: make sure that the positions in the extruder definitions are same as in the machine + # definition + extruder_definition = registry.findDefinitionContainers(id = extruder_definition_id)[0] + position_in_extruder_def = extruder_definition.getMetaDataEntry("position") + if position_in_extruder_def != position: + raise RuntimeError("Extruder position [%s] defined in extruder definition [%s] is not the same as in machine definition [%s] position [%s]" % + (position_in_extruder_def, extruder_definition_id, definition_id, position)) + + new_extruder_id = registry.uniqueName(extruder_definition_id) new_extruder = cls.createExtruderStack( new_extruder_id, - definition = extruder_definition, - machine_definition_id = machine_definition.getId(), - quality = "default", - material = "default", - variant = "default", - next_stack = new_global_stack + extruder_definition = extruder_definition, + machine_definition_id = definition_id, + position = position, + variant_container = variant_container, + material_container = material_container, + quality_container = application.empty_quality_container, ) + new_extruder.setNextStack(new_global_stack) new_global_stack.addExtruder(new_extruder) registry.addContainer(new_extruder) - else: - # create extruder stack for each found extruder definition - for extruder_definition in registry.findDefinitionContainers(machine = machine_definition.id): - position = extruder_definition.getMetaDataEntry("position", None) - if not position: - Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.id) - new_extruder_id = registry.uniqueName(extruder_definition.id) - new_extruder = cls.createExtruderStack( - new_extruder_id, - definition = extruder_definition, - machine_definition_id = machine_definition.getId(), - quality = "default", - material = "default", - variant = "default", - next_stack = new_global_stack - ) - new_global_stack.addExtruder(new_extruder) - registry.addContainer(new_extruder) + preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type") + quality_group_dict = quality_manager.getQualityGroups(new_global_stack) + quality_group = quality_group_dict.get(preferred_quality_type) + + new_global_stack.quality = quality_group.node_for_global.getContainer() + for position, extruder_stack in new_global_stack.extruders.items(): + extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer() # Register the global stack after the extruder stacks are created. This prevents the registry from adding another # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1). @@ -100,43 +129,27 @@ class CuraStackBuilder: # # \return A new Global stack instance with the specified parameters. @classmethod - def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition_id: str, **kwargs) -> ExtruderStack: - stack = ExtruderStack(new_stack_id) - stack.setName(definition.getName()) - stack.setDefinition(definition) - stack.addMetaDataEntry("position", definition.getMetaDataEntry("position")) - - if "next_stack" in kwargs: - # Add stacks before containers are added, since they may trigger a setting update. - stack.setNextStack(kwargs["next_stack"]) - - user_container = InstanceContainer(new_stack_id + "_user") - user_container.addMetaDataEntry("type", "user") - user_container.addMetaDataEntry("extruder", new_stack_id) + def createExtruderStack(cls, new_stack_id: str, extruder_definition: DefinitionContainerInterface, machine_definition_id: str, + position: int, + variant_container, material_container, quality_container) -> ExtruderStack: from cura.CuraApplication import CuraApplication - user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - user_container.setDefinition(machine_definition_id) + application = CuraApplication.getInstance() - stack.setUserChanges(user_container) + stack = ExtruderStack(new_stack_id) + stack.setName(extruder_definition.getName()) + stack.setDefinition(extruder_definition) - # Important! The order here matters, because that allows the stack to - # assume the material and variant have already been set. - if "definition_changes" in kwargs: - stack.setDefinitionChangesById(kwargs["definition_changes"]) - else: - stack.setDefinitionChanges(cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings")) + stack.addMetaDataEntry("position", position) - if "variant" in kwargs: - stack.setVariantById(kwargs["variant"]) + user_container = cls.createUserChangesContainer(new_stack_id + "_user", machine_definition_id, new_stack_id, + is_global_stack = False) - if "material" in kwargs: - stack.setMaterialById(kwargs["material"]) - - if "quality" in kwargs: - stack.setQualityById(kwargs["quality"]) - - if "quality_changes" in kwargs: - stack.setQualityChangesById(kwargs["quality_changes"]) + stack.definitionChanges = cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings") + stack.variant = variant_container + stack.material = material_container + stack.quality = quality_container + stack.qualityChanges = application.empty_quality_changes_container + stack.userChanges = user_container # Only add the created containers to the registry after we have set all the other # properties. This makes the create operation more transactional, since any problems @@ -153,42 +166,47 @@ class CuraStackBuilder: # # \return A new Global stack instance with the specified parameters. @classmethod - def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, **kwargs) -> GlobalStack: + def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, + variant_container, material_container, quality_container) -> GlobalStack: + from cura.CuraApplication import CuraApplication + application = CuraApplication.getInstance() + stack = GlobalStack(new_stack_id) stack.setDefinition(definition) - user_container = InstanceContainer(new_stack_id + "_user") - user_container.addMetaDataEntry("type", "user") - user_container.addMetaDataEntry("machine", new_stack_id) - from cura.CuraApplication import CuraApplication - user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - user_container.setDefinition(definition.getId()) + # Create user container + user_container = cls.createUserChangesContainer(new_stack_id + "_user", definition.getId(), new_stack_id, + is_global_stack = True) - stack.setUserChanges(user_container) - - # Important! The order here matters, because that allows the stack to - # assume the material and variant have already been set. - if "definition_changes" in kwargs: - stack.setDefinitionChangesById(kwargs["definition_changes"]) - else: - stack.setDefinitionChanges(cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings")) - - if "variant" in kwargs: - stack.setVariantById(kwargs["variant"]) - - if "material" in kwargs: - stack.setMaterialById(kwargs["material"]) - - if "quality" in kwargs: - stack.setQualityById(kwargs["quality"]) - - if "quality_changes" in kwargs: - stack.setQualityChangesById(kwargs["quality_changes"]) + stack.definitionChanges = cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings") + stack.variant = variant_container + stack.material = material_container + stack.quality = quality_container + stack.qualityChanges = application.empty_quality_changes_container + stack.userChanges = user_container ContainerRegistry.getInstance().addContainer(user_container) return stack + @classmethod + def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str, + is_global_stack: bool) -> "InstanceContainer": + from cura.CuraApplication import CuraApplication + + unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name) + + container = InstanceContainer(unique_container_name) + container.setDefinition(definition_id) + container.addMetaDataEntry("type", "user") + container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) + + metadata_key_to_add = "machine" if is_global_stack else "extruder" + container.addMetaDataEntry(metadata_key_to_add, stack_id) + + return container + + @classmethod def createDefinitionChangesContainer(cls, container_stack, container_name, container_index = None): from cura.CuraApplication import CuraApplication diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 80bb9b19b9..366158d042 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -11,8 +11,9 @@ "file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g", "visible": false, "has_materials": true, - "preferred_material": "*generic_pla*", + "preferred_material": "generic_pla", "preferred_quality": "*normal*", + "preferred_quality_type": "normal", "machine_extruder_trains": { "0": "fdmextruder" diff --git a/resources/definitions/ultimaker2_plus.def.json b/resources/definitions/ultimaker2_plus.def.json index 58833904d2..bc90776289 100644 --- a/resources/definitions/ultimaker2_plus.def.json +++ b/resources/definitions/ultimaker2_plus.def.json @@ -10,6 +10,7 @@ "platform": "ultimaker2_platform.obj", "platform_texture": "Ultimaker2Plusbackplate.png", "preferred_variant": "*0.4*", + "preferred_variant_name": "0.4 mm", "has_variants": true, "has_materials": true, "has_machine_materials": true, diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index 74e4a9551e..9a98b83efb 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -16,8 +16,9 @@ "has_variant_materials": true, "has_variants": true, "preferred_variant": "*aa04*", + "preferred_variant_name": "AA 0.4", "preferred_quality": "*Normal*", - "preferred_quality_type": "fine", + "preferred_quality_type": "normal", "variants_name": "Print core", "machine_extruder_trains": {