From 619a8ccce5db5d72e0e87c95aa2c3613bebb8f78 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 22 Jan 2018 15:02:55 +0100 Subject: [PATCH 01/11] CURA-4839 Fix single extrusing for overrite the extruder stack. --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 31 ++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 638971d109..2c92b4cb6d 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -754,25 +754,18 @@ 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") + stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder") + 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 +863,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")} From e17fbb0db20e3a8a0b2e55f92b495856f87b0c62 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 22 Jan 2018 16:05:43 +0100 Subject: [PATCH 02/11] Remove copying 1st extruder value to all extruders as that breaks re-loading from files --- cura/Settings/ExtruderStack.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 51b27fea57..d6a72b72e4 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -62,11 +62,6 @@ class ExtruderStack(CuraContainerStack): # 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") From d8ae78d80c6cb389a3b2dd3b51c34a77dbe2539f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 22 Jan 2018 16:12:11 +0100 Subject: [PATCH 03/11] CURA-4821 Select the groups to be deleted also when clearing the scene --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 26f9516a89..bfcbe529f7 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1055,7 +1055,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) From 204af1b6edbe488e93cc97977d088f60f73550fe Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 22 Jan 2018 16:17:37 +0100 Subject: [PATCH 04/11] CURA-4785 fix slow cura for Custom FDM printer: now it's not checking for errors in unused extruders --- cura/Settings/MachineManager.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 23799e34b1..8089cde876 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -395,7 +395,13 @@ class MachineManager(QObject): if self._global_container_stack.hasErrors(): 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()) + if len(extruder_stacks) > machine_extruder_count: + extruder_stacks = extruder_stacks[:machine_extruder_count] # we only have to check the used extruders + for stack in extruder_stacks: if stack.hasErrors(): return True From 129f9cc16c33fcc3c4449f795c9a72bb389f1faf Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 22 Jan 2018 17:18:09 +0100 Subject: [PATCH 05/11] Fixes for custom FMD printer material diameter upgrade and storage - CURA-4835 --- cura/Settings/ExtruderManager.py | 84 +++++++++++++++++++ cura/Settings/ExtruderStack.py | 12 +++ .../MachineSettingsAction.py | 77 +---------------- 3 files changed, 97 insertions(+), 76 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 6d52ce87fd..6b52c629d0 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -502,6 +502,90 @@ 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 d6a72b72e4..204b0e2dae 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 @@ -60,6 +61,12 @@ class ExtruderStack(CuraContainerStack): 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"): continue @@ -75,6 +82,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/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) From 3fb9877a30abb395ae9f854df6e71ff6d237ad83 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 22 Jan 2018 14:59:04 +0100 Subject: [PATCH 06/11] Add call_on_qt_thread to fix project loading crashing on rendering CURA-4839 See comments... --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 2c92b4cb6d..059a1c5d1f 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") From dc119d74bdea44fa8bc572a3da7003adc7c57b87 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 23 Jan 2018 09:21:38 +0100 Subject: [PATCH 07/11] CURA-4785 added logging for measuring time for validation check --- cura/Settings/MachineManager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 8089cde876..1fcb42e824 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 @@ -390,6 +391,7 @@ 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 @@ -405,6 +407,7 @@ class MachineManager(QObject): if stack.hasErrors(): return True + Logger.log("d", "Checking stacks for errors took %.2f s" % (time.time() - time_start)) return False ## Remove all instances from the top instanceContainer (effectively removing all user-changed settings) From 92e99012d3050f22f34f09d349937aa0eb1c6ff2 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 23 Jan 2018 09:33:52 +0100 Subject: [PATCH 08/11] CURA-4785 now actually checking for extruder position, improved logs --- cura/Settings/MachineManager.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 1fcb42e824..e84f8d2237 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -396,18 +396,23 @@ class MachineManager(QObject): 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 # 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()) - if len(extruder_stacks) > machine_extruder_count: - extruder_stacks = extruder_stacks[:machine_extruder_count] # we only have to check the used extruders + 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 stacks for errors took %.2f s" % (time.time() - time_start)) + 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) From 6c9d7b5a2ef29f0d022d09d66c216e85e38053c9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 23 Jan 2018 09:49:26 +0100 Subject: [PATCH 09/11] Fixes for resetting print time information when removing all or last model, reduce signals being used for print time resetting - CURA-4852 --- cura/CuraActions.py | 4 ++++ cura/CuraApplication.py | 6 +++--- cura/PrintInformation.py | 31 +++++++++++++++++++++++-------- 3 files changed, 30 insertions(+), 11 deletions(-) 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 bfcbe529f7..8df9f01e91 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1066,12 +1066,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) From c0bce6ffd49338f53f7edf82d2541982b882543a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 23 Jan 2018 10:25:39 +0100 Subject: [PATCH 10/11] Move Jenkins timeout into parallel_nodes block --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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. From 1c605a5108771add6f3fd180f5073f5c33391eab Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 23 Jan 2018 10:58:43 +0100 Subject: [PATCH 11/11] Fix different strategies for machine and quality in project loading CURA-4839 --- cura/Settings/CuraContainerRegistry.py | 38 +++++++++++++-------- cura/Settings/ExtruderManager.py | 1 - cura/Settings/ExtruderStack.py | 1 - plugins/3MFReader/ThreeMFWorkspaceReader.py | 20 +++++++++-- 4 files changed, 41 insertions(+), 19 deletions(-) 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 6b52c629d0..f9f0fbb401 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -504,7 +504,6 @@ class ExtruderManager(QObject): ## 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 diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 204b0e2dae..0854964898 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -60,7 +60,6 @@ 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) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 059a1c5d1f..3267bf486f 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -558,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") @@ -663,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") @@ -787,7 +790,19 @@ 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: - 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": @@ -1028,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)