diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index aadfd15f1a..efc447b2cf 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -16,13 +16,13 @@ from .ExtruderStack import ExtruderStack class CuraStackBuilder: """Contains helper functions to create new machines.""" - @classmethod - def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]: + def createMachine(cls, name: str, definition_id: str, machine_extruder_count: Optional[int] = None) -> Optional[GlobalStack]: """Create a new instance of a machine. :param name: The name of the new machine. :param definition_id: The ID of the machine definition to use. + :param machine_extruder_count: The number of extruders in the machine. :return: The new global stack or None if an error occurred. """ @@ -66,7 +66,14 @@ class CuraStackBuilder: Logger.logException("e", "Failed to create an extruder stack for position {pos}: {err}".format(pos = position, err = str(e))) return None - for new_extruder in new_global_stack.extruderList: # Only register the extruders if we're sure that all of them are correct. + # If given, set the machine_extruder_count when creating the machine, or else the extruderList used bellow will + # not return the correct extruder list (since by default, the machine_extruder_count is 1) in machines with + # settable number of extruders. + if machine_extruder_count and 0 <= machine_extruder_count <= len(extruder_dict): + new_global_stack.setProperty("machine_extruder_count", "value", machine_extruder_count) + + # Only register the extruders if we're sure that all of them are correct. + for new_extruder in new_global_stack.extruderList: registry.addContainer(new_extruder) # Register the global stack after the extruder stacks are created. This prevents the registry from adding another diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 9abab88e6a..6ed35fe72c 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -502,6 +502,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # Now we know which material is in which extruder. Let's use that to sort the material_labels according to # their extruder position material_labels = [material_name for pos, material_name in sorted(materials_in_extruders_dict.items())] + machine_extruder_count = self._getMachineExtruderCount() + if machine_extruder_count: + material_labels = material_labels[:machine_extruder_count] num_visible_settings = 0 try: @@ -663,7 +666,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # We need to create a new machine machine_name = self._container_registry.uniqueName(self._machine_info.name) - global_stack = CuraStackBuilder.createMachine(machine_name, self._machine_info.definition_id) + # Printers with modifiable number of extruders (such as CFFF) will specify a machine_extruder_count in their + # quality_changes file. If that's the case, take the extruder count into account when creating the machine + # or else the extruderList will return only the first extruder, leading to missing non-global settings in + # the other extruders. + machine_extruder_count = self._getMachineExtruderCount() # type: Optional[int] + global_stack = CuraStackBuilder.createMachine(machine_name, self._machine_info.definition_id, machine_extruder_count) if global_stack: # Only switch if creating the machine was successful. extruder_stack_dict = {str(position): extruder for position, extruder in enumerate(global_stack.extruderList)} @@ -918,6 +926,29 @@ class ThreeMFWorkspaceReader(WorkspaceReader): self._machine_info.quality_changes_info.name = quality_changes_name + def _getMachineExtruderCount(self) -> Optional[int]: + """ + Extracts the machine extruder count from the definition_changes file of the printer. If it is not specified in + the file, None is returned instead. + + :return: The count of the machine's extruders + """ + machine_extruder_count = None + if self._machine_info \ + and self._machine_info.definition_changes_info \ + and "values" in self._machine_info.definition_changes_info.parser \ + and "machine_extruder_count" in self._machine_info.definition_changes_info.parser["values"]: + try: + # Theoretically, if the machine_extruder_count is a setting formula (e.g. "=3"), this will produce a + # value error and the project file loading will load the settings in the first extruder only. + # This is not expected to happen though, since all machine definitions define the machine_extruder_count + # as an integer. + machine_extruder_count = int(self._machine_info.definition_changes_info.parser["values"]["machine_extruder_count"]) + except ValueError: + Logger.log("w", "'machine_extruder_count' in file '{file_name}' is not a number." + .format(file_name = self._machine_info.definition_changes_info.file_name)) + return machine_extruder_count + def _createNewQualityChanges(self, quality_type: str, intent_category: Optional[str], name: str, global_stack: GlobalStack, extruder_stack: Optional[ExtruderStack]) -> InstanceContainer: """Helper class to create a new quality changes profile.