diff --git a/Jenkinsfile b/Jenkinsfile index 20c7303719..83104aea18 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ -timeout(time: 2, unit: "HOURS") { - parallel_nodes(['linux && cura', 'windows && cura']) { +parallel_nodes(['linux && cura', 'windows && cura']) { + timeout(time: 2, unit: "HOURS") { // Prepare building stage('Prepare') { // Ensure we start with a clean build directory. diff --git a/cura/CuraActions.py b/cura/CuraActions.py index f5aace805b..f517ec4217 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -94,6 +94,10 @@ class CuraActions(QObject): removed_group_nodes.append(group_node) op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent())) op.addOperation(RemoveSceneNodeOperation(group_node)) + + # Reset the print information + Application.getInstance().getController().getScene().sceneChanged.emit(node) + op.push() ## Set the extruder that should be used to print the selection. diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 0bf6924121..c58f62869c 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1078,7 +1078,7 @@ class CuraApplication(QtApplication): continue # Node that doesnt have a mesh and is not a group. if only_selectable and not node.isSelectable(): continue - if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData"): + if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData") and not node.callDecoration("isGroup"): continue # Only remove nodes that are selectable. if node.getParent() and node.getParent().callDecoration("isGroup"): continue # Grouped nodes don't need resetting as their parent (the group) is resetted) @@ -1089,12 +1089,12 @@ class CuraApplication(QtApplication): for node in nodes: op.addOperation(RemoveSceneNodeOperation(node)) + # Reset the print information + self.getController().getScene().sceneChanged.emit(node) + op.push() Selection.clear() - # Reset the print information: - self.getController().getScene().sceneChanged.emit(node) - ## Reset all translation on nodes with mesh data. @pyqtSlot() def resetAllTranslation(self): diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 5d5d59ed3b..c03cafe667 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -8,7 +8,9 @@ from UM.Application import Application from UM.Logger import Logger from UM.Qt.Duration import Duration from UM.Preferences import Preferences +from UM.Scene.SceneNode import SceneNode from UM.Settings.ContainerRegistry import ContainerRegistry +from cura.Scene.CuraSceneNode import CuraSceneNode from cura.Settings.ExtruderManager import ExtruderManager from typing import Dict @@ -65,7 +67,7 @@ class PrintInformation(QObject): self._backend = Application.getInstance().getBackend() if self._backend: self._backend.printDurationMessage.connect(self._onPrintDurationMessage) - Application.getInstance().getController().getScene().sceneChanged.connect(self.setToZeroPrintInformation) + Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) self._base_name = "" self._abbr_machine = "" @@ -395,12 +397,25 @@ class PrintInformation(QObject): return result # Simulate message with zero time duration - def setToZeroPrintInformation(self, build_plate_number): - temp_message = {} - if build_plate_number not in self._print_time_message_values: - self._print_time_message_values[build_plate_number] = {} - for key in self._print_time_message_values[build_plate_number].keys(): - temp_message[key] = 0 + def setToZeroPrintInformation(self, build_plate): + # Construct the 0-time message + temp_message = {} + if build_plate not in self._print_time_message_values: + self._print_time_message_values[build_plate] = {} + for key in self._print_time_message_values[build_plate].keys(): + temp_message[key] = 0 temp_material_amounts = [0] - self._onPrintDurationMessage(build_plate_number, temp_message, temp_material_amounts) + + self._onPrintDurationMessage(build_plate, temp_message, temp_material_amounts) + + ## Listen to scene changes to check if we need to reset the print information + def _onSceneChanged(self, scene_node): + + # Ignore any changes that are not related to sliceable objects + if not isinstance(scene_node, SceneNode)\ + or not scene_node.callDecoration("isSliceable")\ + or not scene_node.callDecoration("getBuildPlateNumber") == self._active_build_plate: + return + + self.setToZeroPrintInformation(self._active_build_plate) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 7231fa1f72..5d81188750 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -449,7 +449,13 @@ class CuraContainerRegistry(ContainerRegistry): if not extruder_stacks: self.addExtruderStackForSingleExtrusionMachine(container, "fdmextruder") - def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id): + # + # new_global_quality_changes is optional. It is only used in project loading for a scenario like this: + # - override the current machine + # - create new for custom quality profile + # new_global_quality_changes is the new global quality changes container in this scenario. + # + def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id, new_global_quality_changes = None): new_extruder_id = extruder_id extruder_definitions = self.findDefinitionContainers(id = new_extruder_id) @@ -545,8 +551,12 @@ class CuraContainerRegistry(ContainerRegistry): 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) + machine_quality_changes = machine.qualityChanges + if new_global_quality_changes is not None: + machine_quality_changes = new_global_quality_changes + + if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"): + extruder_quality_changes_container = self.findInstanceContainers(name = machine_quality_changes.getName(), extruder = extruder_id) if extruder_quality_changes_container: extruder_quality_changes_container = extruder_quality_changes_container[0] @@ -556,34 +566,34 @@ class CuraContainerRegistry(ContainerRegistry): # 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()) + extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName()) if extruder_quality_changes_container: quality_changes_id = extruder_quality_changes_container.getId() extruder_stack.setQualityChangesById(quality_changes_id) else: # if we still cannot find a quality changes container for the extruder, create a new one - container_name = machine.qualityChanges.getName() + container_name = machine_quality_changes.getName() container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name) extruder_quality_changes_container = InstanceContainer(container_id) extruder_quality_changes_container.setName(container_name) extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes") extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) extruder_quality_changes_container.addMetaDataEntry("extruder", extruder_stack.definition.getId()) - extruder_quality_changes_container.addMetaDataEntry("quality_type", machine.qualityChanges.getMetaDataEntry("quality_type")) - extruder_quality_changes_container.setDefinition(machine.qualityChanges.getDefinition().getId()) + extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type")) + extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId()) self.addContainer(extruder_quality_changes_container) extruder_stack.qualityChanges = extruder_quality_changes_container 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()) + machine_quality_changes.getName(), extruder_stack.getId()) else: # move all per-extruder settings to the extruder's quality changes - for qc_setting_key in machine.qualityChanges.getAllKeys(): + for qc_setting_key in machine_quality_changes.getAllKeys(): settable_per_extruder = machine.getProperty(qc_setting_key, "settable_per_extruder") if settable_per_extruder: - setting_value = machine.qualityChanges.getProperty(qc_setting_key, "value") + setting_value = machine_quality_changes.getProperty(qc_setting_key, "value") setting_definition = machine.getSettingDefinition(qc_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) @@ -592,7 +602,7 @@ class CuraContainerRegistry(ContainerRegistry): extruder_quality_changes_container.addInstance(new_instance) extruder_quality_changes_container.setDirty(True) - machine.qualityChanges.removeInstance(qc_setting_key, postpone_emit=True) + machine_quality_changes.removeInstance(qc_setting_key, postpone_emit=True) else: extruder_stack.setQualityChangesById("empty_quality_changes") @@ -600,8 +610,8 @@ class CuraContainerRegistry(ContainerRegistry): # Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have # per-extruder settings in the container for the machine instead of the extruder. - if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"): - quality_changes_machine_definition_id = machine.qualityChanges.getDefinition().getId() + if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"): + quality_changes_machine_definition_id = machine_quality_changes.getDefinition().getId() else: whole_machine_definition = machine.definition machine_entry = machine.definition.getMetaDataEntry("machine") @@ -621,7 +631,7 @@ class CuraContainerRegistry(ContainerRegistry): qc_groups[qc_name] = [] qc_groups[qc_name].append(qc) # try to find from the quality changes cura directory too - quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName()) + quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName()) if quality_changes_container: qc_groups[qc_name].append(quality_changes_container) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 6d52ce87fd..f9f0fbb401 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -502,6 +502,89 @@ class ExtruderManager(QObject): def getInstanceExtruderValues(self, key): return ExtruderManager.getExtruderValues(key) + ## Updates the material container to a material that matches the material diameter set for the printer + def updateMaterialForDiameter(self, extruder_position: int): + global_stack = Application.getInstance().getGlobalContainerStack() + if not global_stack: + return + + if not global_stack.getMetaDataEntry("has_materials", False): + return + + extruder_stack = global_stack.extruders[str(extruder_position)] + + material_diameter = extruder_stack.material.getProperty("material_diameter", "value") + if not material_diameter: + # in case of "empty" material + material_diameter = 0 + + material_approximate_diameter = str(round(material_diameter)) + machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value") + if not machine_diameter: + if extruder_stack.definition.hasProperty("material_diameter", "value"): + machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value") + else: + machine_diameter = global_stack.definition.getProperty("material_diameter", "value") + machine_approximate_diameter = str(round(machine_diameter)) + + if material_approximate_diameter != machine_approximate_diameter: + Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.") + + if global_stack.getMetaDataEntry("has_machine_materials", False): + materials_definition = global_stack.definition.getId() + has_material_variants = global_stack.getMetaDataEntry("has_variants", False) + else: + materials_definition = "fdmprinter" + has_material_variants = False + + old_material = extruder_stack.material + search_criteria = { + "type": "material", + "approximate_diameter": machine_approximate_diameter, + "material": old_material.getMetaDataEntry("material", "value"), + "brand": old_material.getMetaDataEntry("brand", "value"), + "supplier": old_material.getMetaDataEntry("supplier", "value"), + "color_name": old_material.getMetaDataEntry("color_name", "value"), + "definition": materials_definition + } + if has_material_variants: + search_criteria["variant"] = extruder_stack.variant.getId() + + container_registry = Application.getInstance().getContainerRegistry() + empty_material = container_registry.findInstanceContainers(id = "empty_material")[0] + + if old_material == empty_material: + search_criteria.pop("material", None) + search_criteria.pop("supplier", None) + search_criteria.pop("brand", None) + search_criteria.pop("definition", None) + search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material") + + materials = container_registry.findInstanceContainers(**search_criteria) + if not materials: + # Same material with new diameter is not found, search for generic version of the same material type + search_criteria.pop("supplier", None) + search_criteria.pop("brand", None) + search_criteria["color_name"] = "Generic" + materials = container_registry.findInstanceContainers(**search_criteria) + if not materials: + # Generic material with new diameter is not found, search for preferred material + search_criteria.pop("color_name", None) + search_criteria.pop("material", None) + search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material") + materials = container_registry.findInstanceContainers(**search_criteria) + if not materials: + # Preferred material with new diameter is not found, search for any material + search_criteria.pop("id", None) + materials = container_registry.findInstanceContainers(**search_criteria) + if not materials: + # Just use empty material as a final fallback + materials = [empty_material] + + Logger.log("i", "Selecting new material: %s", materials[0].getId()) + + extruder_stack.material = materials[0] + ## Get the value for a setting from a specific extruder. # # This is exposed to SettingFunction to use in value functions. diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 51b27fea57..0854964898 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -3,6 +3,7 @@ from typing import Any, TYPE_CHECKING, Optional +from UM.Application import Application from UM.Decorators import override from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase from UM.Settings.ContainerStack import ContainerStack @@ -59,14 +60,14 @@ class ExtruderStack(CuraContainerStack): keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders for key in keys_to_copy: + # Since material_diameter is not on the extruder definition, we need to add it here + # WARNING: this might be very dangerous and should be refactored ASAP! + definition = stack.getSettingDefinition(key) + if definition: + self.definition.addDefinition(definition) # Only copy the value when this extruder doesn't have the value. if self.definitionChanges.hasProperty(key, "value"): - # If the first extruder has a value for this setting, we must copy it to the other extruders via the global stack. - # Note: this assumes the extruders are loaded in the same order as they are positioned on the machine. - if self.getMetaDataEntry("position") == "0": - setting_value = self.definitionChanges.getProperty(key, "value") - stack.definitionChanges.setProperty(key, "value", setting_value) continue setting_value = stack.definitionChanges.getProperty(key, "value") @@ -80,6 +81,11 @@ class ExtruderStack(CuraContainerStack): self.definitionChanges.addInstance(new_instance) self.definitionChanges.setDirty(True) + # Make sure the material diameter is up to date for the extruder stack. + if key == "material_diameter": + position = self.getMetaDataEntry("position", "0") + Application.getInstance().getExtruderManager().updateMaterialForDiameter(position) + # NOTE: We cannot remove the setting from the global stack's definition changes container because for # material diameter, it needs to be applied to all extruders, but here we don't know how many extruders # a machine actually has and how many extruders has already been loaded for that machine, so we have to diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index b98fcccbb2..acb7980382 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1,6 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import time #Type hinting. from typing import Union, List, Dict @@ -407,15 +408,28 @@ class MachineManager(QObject): Logger.log("w", "Failed creating a new machine!") def _checkStacksHaveErrors(self) -> bool: + time_start = time.time() if self._global_container_stack is None: #No active machine. return False if self._global_container_stack.hasErrors(): + Logger.log("d", "Checking global stack for errors took %0.2f s and we found and error" % (time.time() - time_start)) return True - for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()): + + # Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are + machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value") + extruder_stacks = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()) + count = 1 # we start with the global stack + for stack in extruder_stacks: + md = stack.getMetaData() + if "position" in md and int(md["position"]) >= machine_extruder_count: + continue + count += 1 if stack.hasErrors(): + Logger.log("d", "Checking %s stacks for errors took %.2f s and we found an error in stack [%s]" % (count, time.time() - time_start, str(stack))) return True + Logger.log("d", "Checking %s stacks for errors took %.2f s" % (count, time.time() - time_start)) return False ## Remove all instances from the top instanceContainer (effectively removing all user-changed settings) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 638971d109..3267bf486f 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -31,10 +31,42 @@ import zipfile import io import configparser import os +import threading i18n_catalog = i18nCatalog("cura") +# +# HACK: +# +# In project loading, when override the existing machine is selected, the stacks and containers that are correctly +# active in the system will be overridden at runtime. Because the project loading is done in a different thread than +# the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access +# the current stacks and container, which have not completely been updated yet, so Cura will crash in this case. +# +# This "@call_on_qt_thread" decorator makes sure that a function will always be called on the Qt thread (blocking). +# It is applied to the read() function of project loading so it can be guaranteed that only after the project loading +# process is completely done, everything else that needs to occupy the QT thread will be executed. +# +class InterCallObject: + def __init__(self): + self.finish_event = threading.Event() + self.result = None + + +def call_on_qt_thread(func): + def _call_on_qt_thread_wrapper(*args, **kwargs): + def _handle_call(ico, *args, **kwargs): + ico.result = func(*args, **kwargs) + ico.finish_event.set() + inter_call_object = InterCallObject() + new_args = tuple([inter_call_object] + list(args)[:]) + CuraApplication.getInstance().callLater(_handle_call, *new_args, **kwargs) + inter_call_object.finish_event.wait() + return inter_call_object.result + return _call_on_qt_thread_wrapper + + ## Base implementation for reading 3MF workspace files. class ThreeMFWorkspaceReader(WorkspaceReader): def __init__(self): @@ -401,6 +433,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # containing global.cfg / extruder.cfg # # \param file_name + @call_on_qt_thread def read(self, file_name): archive = zipfile.ZipFile(file_name, "r") @@ -525,6 +558,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)] user_instance_containers = [] quality_and_definition_changes_instance_containers = [] + quality_changes_instance_containers = [] for instance_container_file in instance_container_files: container_id = self._stripFileToId(instance_container_file) serialized = archive.open(instance_container_file).read().decode("utf-8") @@ -630,6 +664,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # The ID already exists, but nothing in the values changed, so do nothing. pass quality_and_definition_changes_instance_containers.append(instance_container) + if container_type == "quality_changes": + quality_changes_instance_containers.append(instance_container) if container_type == "definition_changes": definition_changes_extruder_count = instance_container.getProperty("machine_extruder_count", "value") @@ -754,25 +790,30 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # If not extruder stacks were saved in the project file (pre 3.1) create one manually # We re-use the container registry's addExtruderStackForSingleExtrusionMachine method for this if not extruder_stacks: - if self._resolve_strategies["machine"] == "new": - stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder") + # If we choose to override a machine but to create a new custom quality profile, the custom quality + # profile is not immediately applied to the global_stack, so this fix for single extrusion machines + # will use the current custom quality profile on the existing machine. The extra optional argument + # in that function is used in thia case to specify a new global stack quality_changes container so + # the fix can correctly create and copy over the custom quality settings to the newly created extruder. + new_global_quality_changes = None + if self._resolve_strategies["quality_changes"] == "new" and len(quality_changes_instance_containers) > 0: + new_global_quality_changes = quality_changes_instance_containers[0] + stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder", + new_global_quality_changes) + if new_global_quality_changes is not None: + quality_changes_instance_containers.append(stack.qualityChanges) + quality_and_definition_changes_instance_containers.append(stack.qualityChanges) + if global_stack.quality.getId() in ("empty", "empty_quality"): + stack.quality = empty_quality_container + 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: - stack = global_stack.extruders.get("0") - if not stack: - # this should not happen - Logger.log("e", "Cannot find any extruder in an existing global stack [%s].", global_stack.getId()) - if stack: - if global_stack.quality.getId() in ("empty", "empty_quality"): - stack.quality = empty_quality_container - 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) + extruder_stacks.append(stack) except: Logger.logException("w", "We failed to serialize the stack. Trying to clean up.") @@ -870,7 +911,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # We will first find the correct quality profile for the extruder, then apply the same # quality profile for the global stack. # - if len(extruder_stacks) == 1: + if has_extruder_stack_files and len(extruder_stacks) == 1: extruder_stack = extruder_stacks[0] search_criteria = {"type": "quality", "quality_type": global_stack.quality.getMetaDataEntry("quality_type")} @@ -1002,8 +1043,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): stack.setNextStack(global_stack) stack.containersChanged.emit(stack.getTop()) else: - if quality_has_been_changed: - CuraApplication.getInstance().getMachineManager().activeQualityChanged.emit() + CuraApplication.getInstance().getMachineManager().activeQualityChanged.emit() # Actually change the active machine. Application.getInstance().setGlobalContainerStack(global_stack) diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.py b/plugins/MachineSettingsAction/MachineSettingsAction.py index ae1c1663dd..baa0639d3f 100755 --- a/plugins/MachineSettingsAction/MachineSettingsAction.py +++ b/plugins/MachineSettingsAction/MachineSettingsAction.py @@ -158,79 +158,4 @@ class MachineSettingsAction(MachineAction): @pyqtSlot(int) def updateMaterialForDiameter(self, extruder_position: int): # Updates the material container to a material that matches the material diameter set for the printer - if not self._global_container_stack: - return - - if not self._global_container_stack.getMetaDataEntry("has_materials", False): - return - - extruder_stack = self._global_container_stack.extruders[str(extruder_position)] - - material_diameter = extruder_stack.material.getProperty("material_diameter", "value") - if not material_diameter: - # in case of "empty" material - material_diameter = 0 - - material_approximate_diameter = str(round(material_diameter)) - machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value") - if not machine_diameter: - if extruder_stack.definition.hasProperty("material_diameter", "value"): - machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value") - else: - machine_diameter = self._global_container_stack.definition.getProperty("material_diameter", "value") - machine_approximate_diameter = str(round(machine_diameter)) - - if material_approximate_diameter != machine_approximate_diameter: - Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.") - - if self._global_container_stack.getMetaDataEntry("has_machine_materials", False): - materials_definition = self._global_container_stack.definition.getId() - has_material_variants = self._global_container_stack.getMetaDataEntry("has_variants", False) - else: - materials_definition = "fdmprinter" - has_material_variants = False - - old_material = extruder_stack.material - search_criteria = { - "type": "material", - "approximate_diameter": machine_approximate_diameter, - "material": old_material.getMetaDataEntry("material", "value"), - "brand": old_material.getMetaDataEntry("brand", "value"), - "supplier": old_material.getMetaDataEntry("supplier", "value"), - "color_name": old_material.getMetaDataEntry("color_name", "value"), - "definition": materials_definition - } - if has_material_variants: - search_criteria["variant"] = extruder_stack.variant.getId() - - if old_material == self._empty_container: - search_criteria.pop("material", None) - search_criteria.pop("supplier", None) - search_criteria.pop("brand", None) - search_criteria.pop("definition", None) - search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material") - - materials = self._container_registry.findInstanceContainers(**search_criteria) - if not materials: - # Same material with new diameter is not found, search for generic version of the same material type - search_criteria.pop("supplier", None) - search_criteria.pop("brand", None) - search_criteria["color_name"] = "Generic" - materials = self._container_registry.findInstanceContainers(**search_criteria) - if not materials: - # Generic material with new diameter is not found, search for preferred material - search_criteria.pop("color_name", None) - search_criteria.pop("material", None) - search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material") - materials = self._container_registry.findInstanceContainers(**search_criteria) - if not materials: - # Preferred material with new diameter is not found, search for any material - search_criteria.pop("id", None) - materials = self._container_registry.findInstanceContainers(**search_criteria) - if not materials: - # Just use empty material as a final fallback - materials = [self._empty_container] - - Logger.log("i", "Selecting new material: %s", materials[0].getId()) - - extruder_stack.material = materials[0] + Application.getInstance().getExtruderManager().updateMaterialForDiameter(extruder_position)