diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 742d1872cb5..e9e1544afd3 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -166,6 +166,7 @@ def _onSceneChangeTimerFinished(self): if active_extruder_changed is not None: node.callDecoration("getActiveExtruderChangedSignal").disconnect(self._updateDisallowedAreasAndRebuild) node.decoratorsChanged.disconnect(self._updateNodeListeners) + self._updateUsedExtruders() self.rebuild() self._scene_objects = new_scene_objects @@ -202,7 +203,7 @@ def _updateNodeListeners(self, node: SceneNode): per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged) active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal") if active_extruder_changed is not None: - active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild) + active_extruder_changed.connect(self._nodeActiveExtruderChanged) def setWidth(self, width: float) -> None: self._width = width @@ -687,6 +688,7 @@ def _onStackChangeTimerFinished(self) -> None: self._depth = self._global_container_stack.getProperty("machine_depth", "value") self._shape = self._global_container_stack.getProperty("machine_shape", "value") + self._updateUsedExtruders() self._updateDisallowedAreas() self._updateRaftThickness() self._extra_z_clearance = self._calculateExtraZClearance(ExtruderManager.getInstance().getUsedExtruderStacks()) @@ -713,6 +715,7 @@ def _onSettingChangeTimerFinished(self) -> None: update_disallowed_areas = False update_raft_thickness = False update_extra_z_clearance = True + update_used_extruders = False for setting_key in self._changed_settings_since_last_rebuild: if setting_key in ["print_sequence", "support_mesh", "infill_mesh", "cutting_mesh", "anti_overhang_mesh"]: @@ -746,12 +749,17 @@ def _onSettingChangeTimerFinished(self) -> None: if setting_key in self._raft_settings: update_raft_thickness = True + update_used_extruders = True if setting_key in self._extra_z_settings: update_extra_z_clearance = True if setting_key in self._limit_to_extruder_settings: update_disallowed_areas = True + update_used_extruders = True + + if setting_key in self._extruder_settings: + update_used_extruders = True rebuild_me = update_extra_z_clearance or update_disallowed_areas or update_raft_thickness @@ -765,6 +773,9 @@ def _onSettingChangeTimerFinished(self) -> None: if update_extra_z_clearance: self._extra_z_clearance = self._calculateExtraZClearance(ExtruderManager.getInstance().getUsedExtruderStacks()) + if update_used_extruders: + self._updateUsedExtruders() + if rebuild_me: self.rebuild() @@ -791,6 +802,20 @@ def _updateMachineSizeProperties(self) -> None: self._depth = self._global_container_stack.getProperty("machine_depth", "value") self._shape = self._global_container_stack.getProperty("machine_shape", "value") + def _updateUsedExtruders(self): + global_container_stack = self._application.getGlobalContainerStack() + if not global_container_stack: + return + used_extruders = ExtruderManager.getInstance().getUsedExtruderStacks() + for extruder in global_container_stack.extruderList: + used = extruder in used_extruders + extruder.definitionChanges.setProperty("extruder_used", "value", used) + global_container_stack.definitionChanges.setProperty("extruders_used", "value", [extruder.position for extruder in used_extruders]) + + def _nodeActiveExtruderChanged(self): + self._updateDisallowedAreasAndRebuild() + self._updateUsedExtruders() + def _updateDisallowedAreasAndRebuild(self): """Calls :py:meth:`cura.BuildVolume._updateDisallowedAreas` and makes sure the changes appear in the scene. diff --git a/cura/Settings/CuraFormulaFunctions.py b/cura/Settings/CuraFormulaFunctions.py index 93a4de28ec4..517226ef066 100644 --- a/cura/Settings/CuraFormulaFunctions.py +++ b/cura/Settings/CuraFormulaFunctions.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Any, List, Optional, TYPE_CHECKING +from typing import Any, List, Optional, Union, TYPE_CHECKING from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext from UM.Settings.SettingFunction import SettingFunction @@ -61,30 +61,43 @@ def getValueInExtruder(self, extruder_position: int, property_key: str, return value - def _getActiveExtruders(self, context: Optional["PropertyEvaluationContext"] = None) -> List[str]: + def _getActiveExtruders(self, context: Optional["PropertyEvaluationContext"] = None, + where: Union[str, List[str]] = None, where_not: Union[str, List[str]] = None) -> List[str]: machine_manager = self._application.getMachineManager() extruder_manager = self._application.getExtruderManager() global_stack = machine_manager.activeMachine - result = [] + if isinstance(where, str): + where = [where] + if isinstance(where_not, str): + where_not = [where_not] + + enabled_extruders = [] + filtered_extruders = [] for extruder in extruder_manager.getActiveExtruderStacks(): if not extruder.isEnabled: continue # only include values from extruders that are "active" for the current machine instance if int(extruder.getMetaDataEntry("position")) >= global_stack.getProperty("machine_extruder_count", "value", context = context): continue - result.append(extruder) + enabled_extruders.append(extruder) + if where and not all(extruder.getProperty(key, "value", context=context) for key in where): + continue + if where_not and any(extruder.getProperty(key, "value", context=context) for key in where_not): + continue + filtered_extruders.append(extruder) - return result + return filtered_extruders if filtered_extruders else enabled_extruders # Gets all extruder values as a list for the given property. def getValuesInAllExtruders(self, property_key: str, - context: Optional["PropertyEvaluationContext"] = None) -> List[Any]: + context: Optional["PropertyEvaluationContext"] = None, + *, where: str = None, where_not: str = None) -> List[Any]: global_stack = self._application.getMachineManager().activeMachine result = [] - for extruder in self._getActiveExtruders(context): + for extruder in self._getActiveExtruders(context, where=where, where_not=where_not): value = extruder.getRawProperty(property_key, "value", context = context) if value is None: diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 3ce83d27e30..230858fe796 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -282,8 +282,8 @@ def getUsedExtruderStacks(self) -> List["ExtruderStack"]: adhesion_type = global_stack.getProperty("adhesion_type", "value") if adhesion_type == "skirt" and (global_stack.getProperty("skirt_line_count", "value") > 0 or global_stack.getProperty("skirt_brim_minimal_length", "value") > 0): used_adhesion_extruders.add("skirt_brim_extruder_nr") # There's a skirt. - if (adhesion_type == "brim" or global_stack.getProperty("prime_tower_brim_enable", "value")) and (global_stack.getProperty("brim_line_count", "value") > 0 or global_stack.getProperty("skirt_brim_minimal_length", "value") > 0): - used_adhesion_extruders.add("skirt_brim_extruder_nr") # There's a brim or prime tower brim. + if adhesion_type == "brim" and (global_stack.getProperty("brim_line_count", "value") > 0 or global_stack.getProperty("skirt_brim_minimal_length", "value") > 0): + used_adhesion_extruders.add("skirt_brim_extruder_nr") # There's a brim. if adhesion_type == "raft": used_adhesion_extruders.add("raft_base_extruder_nr") if global_stack.getProperty("raft_interface_layers", "value") > 0: diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index 101efee3b10..506d1676cd9 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -39,6 +39,17 @@ "type": "float", "unit": "mm" }, + "extruder_used": + { + "default_value": true, + "description": "Is this extruder used for printing. This setting is controlled by Cura automatically.", + "label": "Extruder Used", + "settable_globally": false, + "settable_per_extruder": true, + "settable_per_mesh": false, + "settable_per_meshgroup": false, + "type": "bool" + }, "machine_extruder_cooling_fan_number": { "default_value": 0, diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 571d5012126..f65a5a727fb 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -289,6 +289,29 @@ "settable_per_extruder": false, "settable_per_meshgroup": false }, + "extruders_used": + { + "default_value": [], + "description": "A list of extruder positions that are used. This setting is controlled by Cura automatically.", + "label": "Extruders Used", + "settable_globally": true, + "settable_per_extruder": false, + "settable_per_mesh": false, + "settable_per_meshgroup": false, + "type": "[int]" + }, + "extruder_used": + { + "default_value": true, + "description": "Is this extruder used? This setting is controlled by Cura automatically.", + "label": "Extruder Used", + "settable_globally": false, + "settable_per_extruder": true, + "settable_per_mesh": false, + "settable_per_meshgroup": false, + "type": "bool", + "force_depends_on_settings": [ "extruders_used" ] + }, "machine_nozzle_tip_outer_diameter": { "label": "Outer Nozzle Diameter", @@ -2476,7 +2499,7 @@ "unit": "\u00b0C", "type": "float", "default_value": 0, - "resolve": "min(extruderValues('build_volume_temperature'))", + "resolve": "min(extruderValues('build_volume_temperature', where='extruder_used'))", "minimum_value": "-273.15", "minimum_value_warning": "0", "maximum_value_warning": "285", @@ -2568,7 +2591,7 @@ "description": "The default temperature used for the heated build plate. This should be the \"base\" temperature of a build plate. All other print temperatures should use offsets based on this value", "unit": "\u00b0C", "type": "float", - "resolve": "max(extruderValues('default_material_bed_temperature'))", + "resolve": "max(extruderValues('default_material_bed_temperature', where='extruder_used'))", "default_value": 60, "minimum_value": "-273.15", "minimum_value_warning": "build_volume_temperature", @@ -2587,7 +2610,7 @@ "type": "float", "default_value": 60, "value": "default_material_bed_temperature", - "resolve": "max(extruderValues('material_bed_temperature'))", + "resolve": "max(extruderValues('material_bed_temperature', where='extruder_used'))", "minimum_value": "-273.15", "minimum_value_warning": "build_volume_temperature", "maximum_value_warning": "130",