From d03ecf3cba84d1551d6cb979ef0fb094da92a3e2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 19 Jan 2018 09:16:22 +0100 Subject: [PATCH 01/12] CURA-4821 Remove debug info --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 85699ea0f5..26731337dd 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -430,7 +430,6 @@ class CuraEngineBackend(QObject, Backend): # cached layer data is removed so the previous data is not rendered - CURA-4821 if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"): if self._stored_optimized_layer_data: - print(self._stored_optimized_layer_data) del self._stored_optimized_layer_data[source.callDecoration("getBuildPlateNumber")] build_plate_changed = set() @@ -698,6 +697,7 @@ class CuraEngineBackend(QObject, Backend): self._process_layers_job.setBuildPlate(build_plate_number) self._process_layers_job.finished.connect(self._onProcessLayersFinished) self._process_layers_job.start() + del self._stored_optimized_layer_data[build_plate_number] ## Called when the user changes the active view mode. def _onActiveViewChanged(self): From a6acf4a4af1dfd6bd6f4537891754d3b9612482b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 19 Jan 2018 10:34:04 +0100 Subject: [PATCH 02/12] CURA-4829 Do not save the quality changes profile in the GCode if the containers are empty. Change the message when trying to import a GCode as a profile, but not profile was stored. --- cura/Settings/CuraContainerRegistry.py | 4 ++++ plugins/GCodeWriter/GCodeWriter.py | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index b945ec0609..b73bec11a7 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -205,6 +205,7 @@ class CuraContainerRegistry(ContainerRegistry): profile_reader = plugin_registry.getPluginObject(plugin_id) try: profile_or_list = profile_reader.read(file_name) # Try to open the file with the profile reader. + print except Exception as e: # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None. Logger.log("e", "Failed to import profile from %s: %s while using profile reader. Got exception %s", file_name,profile_reader.getPluginId(), str(e)) @@ -245,6 +246,9 @@ class CuraContainerRegistry(ContainerRegistry): return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())} + # This message is throw when the profile reader doesn't find any profile in the file + return {"status": "error", "message": catalog.i18nc("@info:status", "File {0} does not contain any valid profile.", file_name)} + # If it hasn't returned by now, none of the plugins loaded the profile successfully. return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)} diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 95c48c4d9e..2dfaf5aef7 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -107,7 +107,7 @@ class GCodeWriter(MeshWriter): prefix_length = len(prefix) container_with_profile = stack.qualityChanges - if not container_with_profile: + if container_with_profile.getId() == "empty_quality_changes": Logger.log("e", "No valid quality profile found, not writing settings to GCode!") return "" @@ -123,9 +123,9 @@ class GCodeWriter(MeshWriter): serialized = flat_global_container.serialize() data = {"global_quality": serialized} - for extruder in sorted(ExtruderManager.getInstance().getMachineExtruders(stack.getId()), key = lambda k: k.getMetaDataEntry("position")): + for extruder in sorted(stack.extruders.values(), key = lambda k: k.getMetaDataEntry("position")): extruder_quality = extruder.qualityChanges - if not extruder_quality: + if extruder_quality.getId() == "empty_quality_changes": Logger.log("w", "No extruder quality profile found, not writing quality for extruder %s to file!", extruder.getId()) continue flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality) From e4a111dd2ee758af58049e5253c3c77d2f1ad3ad Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 19 Jan 2018 10:36:25 +0100 Subject: [PATCH 03/12] Delay adding global stack when creating new machine until after extruder stacks are created - CURA-4828 --- cura/Settings/CuraStackBuilder.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 8d2d66ea87..608a13c600 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -63,6 +63,7 @@ class CuraStackBuilder: next_stack = new_global_stack ) new_global_stack.addExtruder(new_extruder) + registry.addContainer(new_extruder) else: # create extruder stack for each found extruder definition for extruder_definition in registry.findDefinitionContainers(machine = machine_definition.id): @@ -81,6 +82,11 @@ class CuraStackBuilder: next_stack = new_global_stack ) new_global_stack.addExtruder(new_extruder) + registry.addContainer(new_extruder) + + # Register the global stack after the extruder stacks are created. This prevents the registry from adding another + # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1). + registry.addContainer(new_global_stack) return new_global_stack @@ -135,9 +141,7 @@ class CuraStackBuilder: # Only add the created containers to the registry after we have set all the other # properties. This makes the create operation more transactional, since any problems # setting properties will not result in incomplete containers being added. - registry = ContainerRegistry.getInstance() - registry.addContainer(stack) - registry.addContainer(user_container) + ContainerRegistry.getInstance().addContainer(user_container) return stack @@ -181,9 +185,7 @@ class CuraStackBuilder: if "quality_changes" in kwargs: stack.setQualityChangesById(kwargs["quality_changes"]) - registry = ContainerRegistry.getInstance() - registry.addContainer(stack) - registry.addContainer(user_container) + ContainerRegistry.getInstance().addContainer(user_container) return stack From a76c84ca30b2f31713bcd9596c7e1c0188381a3d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 19 Jan 2018 10:42:04 +0100 Subject: [PATCH 04/12] Finally remove annoying firmware check log line for non-UM3 printers --- plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py | 1 - plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py index 458aca5787..8e4c70517f 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py @@ -49,7 +49,6 @@ class FirmwareUpdateChecker(Extension): def _onContainerAdded(self, container): # Only take care when a new GlobalStack was added if isinstance(container, GlobalStack): - Logger.log("i", "You have a '%s' in printer list. Let's check the firmware!", container.getId()) self.checkFirmwareVersion(container, True) ## Connect with software.ultimaker.com, load latest.version and check version info. diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index 6dd7338cfd..fd6c4680e8 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -44,6 +44,8 @@ class FirmwareUpdateCheckerJob(Job): # Now we just do that if the active printer is Ultimaker 3 or Ultimaker 3 Extended or any # other Ultimaker 3 that will come in the future if len(machine_name_parts) >= 2 and machine_name_parts[:2] == ["ultimaker", "3"]: + Logger.log("i", "You have a UM3 in printer list. Let's check the firmware!") + # Nothing to parse, just get the string # TODO: In the future may be done by parsing a JSON file with diferent version for each printer model current_version = reader(current_version_file).readline().rstrip() From ce709bf24a507f6855b1f8e8cee2bf0d58e17445 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 19 Jan 2018 10:48:45 +0100 Subject: [PATCH 05/12] Remove debug print - CURA-4829 --- cura/Settings/CuraContainerRegistry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index b73bec11a7..4567226060 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -205,7 +205,6 @@ class CuraContainerRegistry(ContainerRegistry): profile_reader = plugin_registry.getPluginObject(plugin_id) try: profile_or_list = profile_reader.read(file_name) # Try to open the file with the profile reader. - print except Exception as e: # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None. Logger.log("e", "Failed to import profile from %s: %s while using profile reader. Got exception %s", file_name,profile_reader.getPluginId(), str(e)) From 02291bb6a319a7410344aa1dcbcaff697f38084b Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 19 Jan 2018 11:14:40 +0100 Subject: [PATCH 06/12] better name: "Compatible material diameter" in Machine Settings CURA-4832 --- plugins/MachineSettingsAction/MachineSettingsAction.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index b815d71acc..536c6c30d6 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -390,7 +390,7 @@ Cura.MachineAction visible: Cura.MachineManager.hasMaterials sourceComponent: numericTextFieldWithUnit property string settingKey: "material_diameter" - property string label: catalog.i18nc("@label", "Material diameter") + property string label: catalog.i18nc("@label", "Compatible material diameter") property string unit: catalog.i18nc("@label", "mm") property string tooltip: catalog.i18nc("@tooltip", "The nominal diameter of filament supported by the printer. The exact diameter will be overridden by the material and/or the profile.") function afterOnEditingFinished() From 1f4e7421c1e776c68f309f3caf46db53aa1f275d Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 19 Jan 2018 11:17:42 +0100 Subject: [PATCH 07/12] Revert "CURA-4821 Remove debug info" This reverts commit d03ecf3cba84d1551d6cb979ef0fb094da92a3e2. --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 26731337dd..85699ea0f5 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -430,6 +430,7 @@ class CuraEngineBackend(QObject, Backend): # cached layer data is removed so the previous data is not rendered - CURA-4821 if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"): if self._stored_optimized_layer_data: + print(self._stored_optimized_layer_data) del self._stored_optimized_layer_data[source.callDecoration("getBuildPlateNumber")] build_plate_changed = set() @@ -697,7 +698,6 @@ class CuraEngineBackend(QObject, Backend): self._process_layers_job.setBuildPlate(build_plate_number) self._process_layers_job.finished.connect(self._onProcessLayersFinished) self._process_layers_job.start() - del self._stored_optimized_layer_data[build_plate_number] ## Called when the user changes the active view mode. def _onActiveViewChanged(self): From f25aad8091b9eb651d958bbb5e4a9e8960dc9ec5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 19 Jan 2018 11:18:46 +0100 Subject: [PATCH 08/12] CURA-4821 Remove debug output --- plugins/CuraEngineBackend/CuraEngineBackend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 85699ea0f5..47f7a07b94 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -430,7 +430,6 @@ class CuraEngineBackend(QObject, Backend): # cached layer data is removed so the previous data is not rendered - CURA-4821 if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"): if self._stored_optimized_layer_data: - print(self._stored_optimized_layer_data) del self._stored_optimized_layer_data[source.callDecoration("getBuildPlateNumber")] build_plate_changed = set() From 99c40d09e937859070f3386f7b27e0afab5b53d6 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 19 Jan 2018 11:32:55 +0100 Subject: [PATCH 09/12] Hitting enter on project loading dialog now loads the project instead of just closing the dialog - CURA-4735 --- .../qml/AskOpenAsProjectOrModelsDialog.qml | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/resources/qml/AskOpenAsProjectOrModelsDialog.qml b/resources/qml/AskOpenAsProjectOrModelsDialog.qml index a53c711f9d..bd37d1acdb 100644 --- a/resources/qml/AskOpenAsProjectOrModelsDialog.qml +++ b/resources/qml/AskOpenAsProjectOrModelsDialog.qml @@ -26,33 +26,54 @@ UM.Dialog minimumHeight: maximumHeight minimumWidth: maximumWidth - modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal; + modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal property var fileUrl - function loadProjectFile(projectFile) - { - UM.WorkspaceFileHandler.readLocalFile(projectFile); + // load the entire project + function loadProjectFile() { - var meshName = backgroundItem.getMeshName(projectFile.toString()); - backgroundItem.hasMesh(decodeURIComponent(meshName)); - } - - function loadModelFiles(fileUrls) - { - for (var i in fileUrls) - { - CuraApplication.readLocalFile(fileUrls[i]); + // update preference + if (rememberChoiceCheckBox.checked) { + UM.Preferences.setValue("cura/choice_on_open_project", "open_as_project") } - var meshName = backgroundItem.getMeshName(fileUrls[0].toString()); - backgroundItem.hasMesh(decodeURIComponent(meshName)); + UM.WorkspaceFileHandler.readLocalFile(base.fileUrl) + var meshName = backgroundItem.getMeshName(base.fileUrl.toString()) + backgroundItem.hasMesh(decodeURIComponent(meshName)) + + base.hide() } - onVisibleChanged: - { - if (visible) - { + // load the project file as separated models + function loadModelFiles() { + + // update preference + if (rememberChoiceCheckBox.checked) { + UM.Preferences.setValue("cura/choice_on_open_project", "open_as_model") + } + + CuraApplication.readLocalFile(base.fileUrl) + var meshName = backgroundItem.getMeshName(base.fileUrl.toString()) + backgroundItem.hasMesh(decodeURIComponent(meshName)) + + base.hide() + } + + // override UM.Dialog accept + function accept () { + var openAsPreference = UM.Preferences.getValue("cura/choice_on_open_project") + + // when hitting 'enter', we always open as project unless open_as_model was explicitly stored as preference + if (openAsPreference == "open_as_model") { + loadModelFiles() + } else { + loadProjectFile() + } + } + + onVisibleChanged: { + if (visible) { var rememberMyChoice = UM.Preferences.getValue("cura/choice_on_open_project") != "always_ask"; rememberChoiceCheckBox.checked = rememberMyChoice; } @@ -90,47 +111,26 @@ UM.Dialog } // Buttons - Item - { + Item { id: buttonBar anchors.right: parent.right anchors.left: parent.left height: childrenRect.height - Button - { + Button { id: openAsProjectButton - text: catalog.i18nc("@action:button", "Open as project"); + text: catalog.i18nc("@action:button", "Open as project") anchors.right: importModelsButton.left anchors.rightMargin: UM.Theme.getSize("default_margin").width isDefault: true - onClicked: - { - // update preference - if (rememberChoiceCheckBox.checked) - UM.Preferences.setValue("cura/choice_on_open_project", "open_as_project"); - - // load this file as project - base.hide(); - loadProjectFile(base.fileUrl); - } + onClicked: loadProjectFile() } - Button - { + Button { id: importModelsButton - text: catalog.i18nc("@action:button", "Import models"); + text: catalog.i18nc("@action:button", "Import models") anchors.right: parent.right - onClicked: - { - // update preference - if (rememberChoiceCheckBox.checked) - UM.Preferences.setValue("cura/choice_on_open_project", "open_as_model"); - - // load models from this project file - base.hide(); - loadModelFiles([base.fileUrl]); - } + onClicked: loadModelFiles() } } } From 7c85db4a180230137f4410f6762e601ceb3649d9 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 19 Jan 2018 12:30:04 +0100 Subject: [PATCH 10/12] Fix material_diameter copy over for Extruders CURA-4835 --- cura/Settings/ExtruderManager.py | 6 +++++ cura/Settings/ExtruderStack.py | 44 +++++++++++++++++++++----------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index b5f9a35914..6d52ce87fd 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -407,6 +407,12 @@ class ExtruderManager(QObject): extruder_train.setNextStack(global_stack) extruders_changed = True + # FIX: We have to remove those settings here because we know that those values have been copied to all + # the extruders at this point. + for key in ("material_diameter", "machine_nozzle_size"): + if global_stack.definitionChanges.hasProperty(key, "value"): + global_stack.definitionChanges.removeInstance(key, postpone_emit = True) + if extruders_changed: self.extrudersChanged.emit(global_stack_id) self.extrudersAdded.emit() diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 6dffeda6c2..dd03ce7b3b 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -18,10 +18,6 @@ if TYPE_CHECKING: from cura.Settings.GlobalStack import GlobalStack -_EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS = ["machine_nozzle_size", - "material_diameter"] - - ## Represents an Extruder and its related containers. # # @@ -53,20 +49,38 @@ class ExtruderStack(CuraContainerStack): # when we are upgrading a definition_changes container file, there is NO guarantee that other files such as # machine an extruder stack files are upgraded before this, so we cannot read those files assuming they are in # the latest format. + # + # MORE: + # For single-extrusion machines, nozzle size is saved in the global stack, so the nozzle size value should be + # carried to the first extruder. + # For material diameter, it was supposed to be applied to all extruders, so its value should be copied to all + # extruders. + # + keys_to_copy = ["material_diameter"] # material diameter will be copied to all extruders if self.getMetaDataEntry("position") == "0": - for key in _EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS: - setting_value = stack.definitionChanges.getProperty(key, "value") - if setting_value is None: - continue + keys_to_copy.append("machine_nozzle_size") - setting_definition = stack.getSettingDefinition(key) - new_instance = SettingInstance(setting_definition, self.definitionChanges) - new_instance.setProperty("value", setting_value) - new_instance.resetState() # Ensure that the state is not seen as a user state. - self.definitionChanges.addInstance(new_instance) - self.definitionChanges.setDirty(True) + for key in keys_to_copy: + # Only copy the value when this extruder doesn't have the value. + if self.definitionChanges.hasProperty(key, "value"): + continue + setting_value = stack.definitionChanges.getProperty(key, "value") + if setting_value is None: + continue - stack.definitionChanges.removeInstance(key, postpone_emit = True) + setting_definition = stack.getSettingDefinition(key) + new_instance = SettingInstance(setting_definition, self.definitionChanges) + new_instance.setProperty("value", setting_value) + new_instance.resetState() # Ensure that the state is not seen as a user state. + self.definitionChanges.addInstance(new_instance) + self.definitionChanges.setDirty(True) + + # 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 + # keep this setting for any remaining extruders that haven't been loaded yet. + # + # Those settings will be removed in ExtruderManager which knows all those info. @override(ContainerStack) def getNextStack(self) -> Optional["GlobalStack"]: From e0c69eb67511b6f675906e38c66e7eca2ac7bb47 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 19 Jan 2018 12:51:09 +0100 Subject: [PATCH 11/12] Pressing enter on project overview will load the project file CURA-4735 --- plugins/3MFReader/WorkspaceDialog.qml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index 826b488e02..5418dcef6d 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -390,6 +390,13 @@ UM.Dialog } } + function accept() { + manager.closeBackend(); + manager.onOkButtonClicked(); + base.visible = false; + base.accept(); + } + function reject() { manager.onCancelButtonClicked(); base.visible = false; From d035ace40dc73a2c3109381fadde778c42ed2e67 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 19 Jan 2018 13:32:25 +0100 Subject: [PATCH 12/12] Add method to set active extruder count to machine manager so other plugins can use it, add empty definitions container template --- cura/CuraApplication.py | 5 ++ cura/Settings/MachineManager.py | 65 +++++++++++++++++++ .../MachineSettingsAction.py | 57 +--------------- 3 files changed, 73 insertions(+), 54 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 0ac50c9e5e..342f6cb91d 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -278,6 +278,11 @@ class CuraApplication(QtApplication): # We need them to simplify the switching between materials. empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() + empty_definition_changes_container = copy.deepcopy(empty_container) + empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes") + empty_definition_changes_container.addMetaDataEntry("type", "definition_changes") + ContainerRegistry.getInstance().addContainer(empty_definition_changes_container) + empty_variant_container = copy.deepcopy(empty_container) empty_variant_container.setMetaDataEntry("id", "empty_variant") empty_variant_container.addMetaDataEntry("type", "variant") diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 05aed1f5e2..23799e34b1 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -3,6 +3,8 @@ #Type hinting. from typing import Union, List, Dict + +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Signal import Signal from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer @@ -74,6 +76,7 @@ class MachineManager(QObject): self._stacks_have_errors = None + self._empty_definition_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0] self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0] self._empty_material_container = ContainerRegistry.getInstance().findContainers(id = "empty_material")[0] self._empty_quality_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0] @@ -1236,6 +1239,68 @@ class MachineManager(QObject): if containers: return containers[0].definition.getId() + ## Set the amount of extruders on the active machine (global stack) + # \param extruder_count int the number of extruders to set + def setActiveMachineExtruderCount(self, extruder_count): + extruder_manager = Application.getInstance().getExtruderManager() + + definition_changes_container = self._global_container_stack.definitionChanges + if not self._global_container_stack or definition_changes_container == self._empty_definition_changes_container: + return + + previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value") + if extruder_count == previous_extruder_count: + return + + # reset all extruder number settings whose value is no longer valid + for setting_instance in self._global_container_stack.userChanges.findInstances(): + setting_key = setting_instance.definition.key + if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"): + continue + + old_value = int(self._global_container_stack.userChanges.getProperty(setting_key, "value")) + if old_value >= extruder_count: + self._global_container_stack.userChanges.removeInstance(setting_key) + Logger.log("d", "Reset [%s] because its old value [%s] is no longer valid ", setting_key, old_value) + + # Check to see if any objects are set to print with an extruder that will no longer exist + root_node = Application.getInstance().getController().getScene().getRoot() + for node in DepthFirstIterator(root_node): + if node.getMeshData(): + extruder_nr = node.callDecoration("getActiveExtruderPosition") + + if extruder_nr is not None and int(extruder_nr) > extruder_count - 1: + node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId()) + + definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count) + + # Make sure one of the extruder stacks is active + extruder_manager.setActiveExtruderIndex(0) + + # Move settable_per_extruder values out of the global container + # After CURA-4482 this should not be the case anymore, but we still want to support older project files. + global_user_container = self._global_container_stack.getTop() + + # Make sure extruder_stacks exists + extruder_stacks = [] + + if previous_extruder_count == 1: + extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() + global_user_container = self._global_container_stack.getTop() + + for setting_instance in global_user_container.findInstances(): + setting_key = setting_instance.definition.key + settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder") + + if settable_per_extruder: + limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder")) + extruder_stack = extruder_stacks[max(0, limit_to_extruder)] + extruder_stack.getTop().setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value")) + global_user_container.removeInstance(setting_key) + + # Signal that the global stack has changed + Application.getInstance().globalContainerStackChanged.emit() + @staticmethod def createMachineManager(): return MachineManager() diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.py b/plugins/MachineSettingsAction/MachineSettingsAction.py index 101ba54ed0..ae1c1663dd 100755 --- a/plugins/MachineSettingsAction/MachineSettingsAction.py +++ b/plugins/MachineSettingsAction/MachineSettingsAction.py @@ -105,60 +105,9 @@ class MachineSettingsAction(MachineAction): @pyqtSlot(int) def setMachineExtruderCount(self, extruder_count): - extruder_manager = Application.getInstance().getExtruderManager() - - definition_changes_container = self._global_container_stack.definitionChanges - if not self._global_container_stack or definition_changes_container == self._empty_container: - return - - previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value") - if extruder_count == previous_extruder_count: - return - - # reset all extruder number settings whose value is no longer valid - for setting_instance in self._global_container_stack.userChanges.findInstances(): - setting_key = setting_instance.definition.key - if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"): - continue - - old_value = int(self._global_container_stack.userChanges.getProperty(setting_key, "value")) - if old_value >= extruder_count: - self._global_container_stack.userChanges.removeInstance(setting_key) - Logger.log("d", "Reset [%s] because its old value [%s] is no longer valid ", setting_key, old_value) - - # Check to see if any objects are set to print with an extruder that will no longer exist - root_node = Application.getInstance().getController().getScene().getRoot() - for node in DepthFirstIterator(root_node): - if node.getMeshData(): - extruder_nr = node.callDecoration("getActiveExtruderPosition") - - if extruder_nr is not None and int(extruder_nr) > extruder_count - 1: - node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId()) - - definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count) - - # Make sure one of the extruder stacks is active - extruder_manager.setActiveExtruderIndex(0) - - # Move settable_per_extruder values out of the global container - # After CURA-4482 this should not be the case anymore, but we still want to support older project files. - global_user_container = self._global_container_stack.getTop() - - if previous_extruder_count == 1: - extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() - global_user_container = self._global_container_stack.getTop() - - for setting_instance in global_user_container.findInstances(): - setting_key = setting_instance.definition.key - settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder") - - if settable_per_extruder: - limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder")) - extruder_stack = extruder_stacks[max(0, limit_to_extruder)] - extruder_stack.getTop().setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value")) - global_user_container.removeInstance(setting_key) - - self.forceUpdate() + # Note: this method was in this class before, but since it's quite generic and other plugins also need it + # it was moved to the machine manager instead. Now this method just calls the machine manager. + Application.getInstance().getMachineManager().setActiveMachineExtruderCount(extruder_count) @pyqtSlot() def forceUpdate(self):