diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 1975458548..7ffed279fc 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -402,96 +402,92 @@ class CuraContainerRegistry(ContainerRegistry): return new_stack def _registerSingleExtrusionMachinesExtruderStacks(self): - machines = ContainerRegistry.getInstance().findContainerStacks(machine_extruder_trains = {"0": "fdmextruder"}) + machines = self.findContainerStacks(type = "machine", machine_extruder_trains = {"0": "fdmextruder"}) for machine in machines: - self.addExtruderStackForSingleExtrusionMachine(machine, "fdmextruder") + extruder_stacks = self.findContainerStacks(type = "extruder_train", machine = machine.getId()) + if not extruder_stacks: + self.addExtruderStackForSingleExtrusionMachine(machine, "fdmextruder") def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id): new_extruder_id = extruder_id - extruder_stack = None - # if extruders are defined in the machine definition use those instead - if machine.extruders and "0" in machine.extruders: - new_extruder_id = machine.extruders["0"].getId() - extruder_stack = machine.extruders["0"] + 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 - # if the extruder stack doesn't exist yet we create and add it - if not extruder_stack: - 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_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")) + extruder_stack.setNextStack(machine) - 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")) - extruder_stack.setNextStack(machine) + # create empty user changes container otherwise + user_container = InstanceContainer(extruder_stack.id + "_user") + user_container.addMetaDataEntry("type", "user") + user_container.addMetaDataEntry("machine", extruder_stack.getId()) + from cura.CuraApplication import CuraApplication + user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) + user_container.setDefinition(machine.definition) - # create empty user changes container otherwise - user_container = InstanceContainer(extruder_stack.id + "_user") - user_container.addMetaDataEntry("type", "user") - user_container.addMetaDataEntry("machine", extruder_stack.getId()) - from cura.CuraApplication import CuraApplication - user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - user_container.setDefinition(extruder_definition) + if machine.userChanges: + # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes + # container to the extruder stack. + for user_setting_key in machine.userChanges.getAllKeys(): + settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder") + if settable_per_extruder: + user_container.addInstance(machine.userChanges.getInstance(user_setting_key)) + machine.userChanges.removeInstance(user_setting_key, postpone_emit = True) - if machine.userChanges: - # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes - # container to the extruder stack. - for user_setting_key in machine.userChanges.getAllKeys(): - settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder") - if settable_per_extruder: - user_container.addInstance(machine.userChanges.getInstance(user_setting_key)) - machine.userChanges.removeInstance(user_setting_key, postpone_emit = True) + extruder_stack.setUserChanges(user_container) + self.addContainer(user_container) - extruder_stack.setUserChanges(user_container) - self.addContainer(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) - variant_id = "default" - if machine.variant.getId() not in ("empty", "empty_variant"): - variant_id = machine.variant.getId() + 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: - 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) + # Some extruder quality_changes containers can be created at runtime as files in the qualities + # folder. Those files won't be loaded in the registry immediately. So we also need to search + # the folder to see if the quality_changes exists. + extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName()) 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: - # Some extruder quality_changes containers can be created at runtime as files in the qualities - # folder. Those files won't be loaded in the registry immediately. So we also need to search - # the folder to see if the quality_changes exists. - 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()) + 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) + self.addContainer(extruder_stack) return extruder_stack diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 7a260e2145..a661237722 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -108,7 +108,7 @@ class CuraStackBuilder: user_container.addMetaDataEntry("extruder", new_stack_id) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - user_container.setDefinition(definition) + user_container.setDefinition(machine_definition) stack.setUserChanges(user_container) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 6bacae11a0..7237aa5674 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -763,29 +763,30 @@ class MachineManager(QObject): quality_type = old_quality_changes.getMetaDataEntry("quality_type") new_quality_id = old_quality_changes.getId() - # See if the requested quality type is available in the new situation. - machine_definition = self._active_container_stack.getBottom() - quality_manager = QualityManager.getInstance() - candidate_quality = None - if quality_type: - candidate_quality = quality_manager.findQualityByQualityType(quality_type, - quality_manager.getWholeMachineDefinition(material_container.getDefinition()), - [material_container]) + global_stack = Application.getInstance().getGlobalContainerStack() + if global_stack: + quality_manager = QualityManager.getInstance() - if not candidate_quality or isinstance(candidate_quality, type(self._empty_quality_changes_container)): - Logger.log("d", "Attempting to find fallback quality") - # Fall back to a quality (which must be compatible with all other extruders) - new_qualities = quality_manager.findAllUsableQualitiesForMachineAndExtruders( - self._global_container_stack, ExtruderManager.getInstance().getExtruderStacks()) - if new_qualities: - new_quality_id = new_qualities[0].getId() # Just pick the first available one + candidate_quality = None + if quality_type: + candidate_quality = quality_manager.findQualityByQualityType(quality_type, + quality_manager.getWholeMachineDefinition(global_stack.definition), + [material_container]) + + if not candidate_quality or isinstance(candidate_quality, type(self._empty_quality_changes_container)): + Logger.log("d", "Attempting to find fallback quality") + # Fall back to a quality (which must be compatible with all other extruders) + new_qualities = quality_manager.findAllUsableQualitiesForMachineAndExtruders( + self._global_container_stack, ExtruderManager.getInstance().getExtruderStacks()) + if new_qualities: + new_quality_id = new_qualities[0].getId() # Just pick the first available one + else: + Logger.log("w", "No quality profile found that matches the current machine and extruders.") else: - Logger.log("w", "No quality profile found that matches the current machine and extruders.") - else: - if not old_quality_changes: - new_quality_id = candidate_quality.getId() + if not old_quality_changes: + new_quality_id = candidate_quality.getId() - self.setActiveQuality(new_quality_id) + self.setActiveQuality(new_quality_id) @pyqtSlot(str) def setActiveVariant(self, variant_id: str): diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index e7a47561ce..acd4242fff 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -715,11 +715,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader): extruder_file_content = archive.open(extruder_stack_file, "r").read().decode("utf-8") if self._resolve_strategies["machine"] == "override": - if global_stack.getProperty("machine_extruder_count", "value") > 1: - # deserialize new extruder stack over the current ones (if any) - stack = self._overrideExtruderStack(global_stack, extruder_file_content, extruder_stack_file) - if stack is None: - continue + # deserialize new extruder stack over the current ones (if any) + stack = self._overrideExtruderStack(global_stack, extruder_file_content, extruder_stack_file) + if stack is None: + continue elif self._resolve_strategies["machine"] == "new": new_id = extruder_stack_id_map[container_id] @@ -760,7 +759,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if not extruder_stacks: stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder") if stack: - extruder_stacks.append(stack) + if self._resolve_strategies["machine"] == "override": + # in case the extruder is newly created (for a single-extrusion machine), we need to override + # the existing extruder stack. + existing_extruder_stack = global_stack.extruders[stack.getMetaDataEntry("position")] + for idx in range(len(_ContainerIndexes.IndexTypeMap)): + existing_extruder_stack.replaceContainer(idx, stack._containers[idx], postpone_emit = True) + extruder_stacks.append(existing_extruder_stack) + else: + extruder_stacks.append(stack) except: Logger.logException("w", "We failed to serialize the stack. Trying to clean up.") @@ -812,6 +819,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader): available_quality_types = [q.getMetaDataEntry("quality_type") for q in available_quality] if global_stack.quality.getMetaDataEntry("quality_type") not in available_quality_types: + # We are here because the quality_type specified in the project is not supported any more, + # so we need to switch it to the "preferred quality" if present, otherwise "normal". quality_has_been_changed = True # find the preferred quality @@ -845,6 +854,41 @@ class ThreeMFWorkspaceReader(WorkspaceReader): else: # we cannot find the preferred quality. THIS SHOULD NOT HAPPEN Logger.log("e", "Cannot find the preferred quality for machine [%s]", global_stack.getId()) + else: + # The quality_type specified in the project file is usable, but the quality container itself may not + # be correct. For example, for UM2, the quality container can be "draft" while it should be "um2_draft" + # instead. + # In this code branch, we try to fix those incorrect quality containers. + search_criteria = {"type": "quality", + "quality_type": global_stack.quality.getMetaDataEntry("quality_type")} + search_criteria["definition"] = global_stack.definition.getId() + if not parseBool(global_stack.getMetaDataEntry("has_machine_quality", "False")): + search_criteria["definition"] = "fdmprinter" + + containers = self._container_registry.findInstanceContainers(**search_criteria) + containers = [c for c in containers if not c.getMetaDataEntry("material", "")] + if not containers: + # cannot find machine-specific qualities, so just use fdmprinter to search again + search_criteria["definition"] = "fdmprinter" + containers = self._container_registry.findInstanceContainers(**search_criteria) + containers = [c for c in containers if not c.getMetaDataEntry("material", "")] + + if containers: + new_quality_container = containers[0] + global_stack.quality = new_quality_container + + for extruder_stack in extruder_stacks: + search_criteria = {"type": "quality", + "quality_type": global_stack.quality.getMetaDataEntry("quality_type")} + search_criteria["definition"] = global_stack.definition.getId() + if not parseBool(global_stack.getMetaDataEntry("has_machine_quality", "False")): + search_criteria["definition"] = "fdmprinter" + + if global_stack.getMetaDataEntry("has_machine_materials") and extruder_stack.material.getId() not in ("empty", "empty_material"): + search_criteria["material"] = extruder_stack.material.getId() + containers = self._container_registry.findInstanceContainers(**search_criteria) + if containers: + extruder_stack.quality = containers[0] # Replacing the old containers if resolve is "new". # When resolve is "new", some containers will get renamed, so all the other containers that reference to those diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index d1c8ae2c51..1ebb5cc40e 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -337,7 +337,7 @@ Item // machine gets changed. var activeMachineId = Cura.MachineManager.activeMachineId; - if(!model.settable_per_extruder || machineExtruderCount.properties.value == 1) + if(!model.settable_per_extruder) { //Not settable per extruder or there only is global, so we must pick global. return activeMachineId;