diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 68e894642d..aff79218c8 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -537,6 +537,16 @@ class MaterialManager(QObject): return nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list + # Sort all nodes with respect to the container ID lengths in the ascending order so the base material container + # will be the first one to be removed. We need to do this to ensure that all containers get loaded & deleted. + nodes_to_remove = sorted(nodes_to_remove, key = lambda x: len(x.getMetaDataEntry("id", ""))) + # Try to load all containers first. If there is any faulty ones, they will be put into the faulty container + # list, so removeContainer() can ignore those ones. + for node in nodes_to_remove: + container_id = node.getMetaDataEntry("id", "") + results = self._container_registry.findContainers(id = container_id) + if not results: + self._container_registry.addWrongContainerId(container_id) for node in nodes_to_remove: self._container_registry.removeContainer(node.getMetaDataEntry("id", "")) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index f8dccb4ba6..b6b7b31936 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -338,7 +338,7 @@ class ExtruderManager(QObject): extruder_train.setNextStack(global_stack) extruders_changed = True - self._fixSingleExtrusionMachineExtruderDefinition(global_stack) + self.fixSingleExtrusionMachineExtruderDefinition(global_stack) if extruders_changed: self.extrudersChanged.emit(global_stack_id) self.setActiveExtruderIndex(0) @@ -346,7 +346,7 @@ class ExtruderManager(QObject): # After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing # "fdmextruder". We need to check a machine here so its extruder definition is correct according to this. - def _fixSingleExtrusionMachineExtruderDefinition(self, global_stack: "GlobalStack") -> None: + def fixSingleExtrusionMachineExtruderDefinition(self, global_stack: "GlobalStack") -> None: container_registry = ContainerRegistry.getInstance() expected_extruder_definition_0_id = global_stack.getMetaDataEntry("machine_extruder_trains")["0"] extruder_stack_0 = global_stack.extruders.get("0") diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 3416f0a321..25eef60268 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -357,7 +357,7 @@ class MachineManager(QObject): # Make sure that the default machine actions for this machine have been added self._application.getMachineActionManager().addDefaultMachineActions(global_stack) - ExtruderManager.getInstance()._fixSingleExtrusionMachineExtruderDefinition(global_stack) + ExtruderManager.getInstance().fixSingleExtrusionMachineExtruderDefinition(global_stack) if not global_stack.isValid(): # Mark global stack as invalid ConfigurationErrorMessage.getInstance().addFaultyContainers(global_stack.getId()) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index bf190f7e39..38652361a5 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -26,6 +26,7 @@ from UM.Preferences import Preferences from cura.Machines.VariantType import VariantType from cura.Settings.CuraStackBuilder import CuraStackBuilder +from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.GlobalStack import GlobalStack from cura.Settings.CuraContainerStack import _ContainerIndexes @@ -781,6 +782,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if not quality_changes_info.extruder_info_dict: container_info = ContainerInfo(None, None, None) quality_changes_info.extruder_info_dict["0"] = container_info + # If the global stack we're "targeting" has never been active, but was updated from Cura 3.4, + # it might not have it's extruders set properly. + if not global_stack.extruders: + ExtruderManager.getInstance().fixSingleExtrusionMachineExtruderDefinition(global_stack) extruder_stack = global_stack.extruders["0"] container = quality_manager._createQualityChanges(quality_changes_quality_type, quality_changes_name, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 7b5add276a..d052d925d2 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -12,8 +12,10 @@ from UM.Backend.Backend import BackendState from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.Message import Message +from UM.PluginRegistry import PluginRegistry from UM.Qt.Duration import Duration, DurationFormat from UM.Scene.SceneNode import SceneNode + from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel @@ -82,8 +84,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._account = api_client.account # We use the Cura Connect monitor tab to get most functionality right away. - self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "../../resources/qml/MonitorStage.qml") + if PluginRegistry.getInstance() is not None: + self._monitor_view_qml_path = os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "qml", "MonitorStage.qml" + ) # Trigger the printersChanged signal when the private signal is triggered. self.printersChanged.connect(self._clusterPrintersChanged) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index c1a6362455..c7dd33f1fc 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -10,13 +10,13 @@ import os from UM.FileHandler.FileHandler import FileHandler from UM.FileHandler.WriteFileJob import WriteFileJob # To call the file writer asynchronously. -from UM.Logger import Logger -from UM.Settings.ContainerRegistry import ContainerRegistry from UM.i18n import i18nCatalog -from UM.Qt.Duration import Duration, DurationFormat - +from UM.Logger import Logger from UM.Message import Message +from UM.PluginRegistry import PluginRegistry +from UM.Qt.Duration import Duration, DurationFormat from UM.Scene.SceneNode import SceneNode # For typing. +from UM.Settings.ContainerRegistry import ContainerRegistry from cura.CuraApplication import CuraApplication from cura.PrinterOutput.ConfigurationModel import ConfigurationModel @@ -65,7 +65,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._received_print_jobs = False # type: bool - self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorStage.qml") + if PluginRegistry.getInstance() is not None: + self._monitor_view_qml_path = os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "qml", "MonitorStage.qml" + ) # Trigger the printersChanged signal when the private signal is triggered self.printersChanged.connect(self._clusterPrintersChanged) @@ -126,8 +130,12 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _spawnPrinterSelectionDialog(self): if self._printer_selection_dialog is None: - path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/PrintWindow.qml") - self._printer_selection_dialog = self._application.createQmlComponent(path, {"OutputDevice": self}) + if PluginRegistry.getInstance() is not None: + path = os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "qml", "PrintWindow.qml" + ) + self._printer_selection_dialog = self._application.createQmlComponent(path, {"OutputDevice": self}) if self._printer_selection_dialog is not None: self._printer_selection_dialog.show() diff --git a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py index 3ce0460d6b..2c7c33d382 100644 --- a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py @@ -1,7 +1,5 @@ from typing import List, Optional -from UM.FileHandler.FileHandler import FileHandler -from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel @@ -12,10 +10,13 @@ from cura.PrinterOutputDevice import ConnectionType from cura.Settings.ContainerManager import ContainerManager from cura.Settings.ExtruderManager import ExtruderManager -from UM.Logger import Logger -from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.FileHandler.FileHandler import FileHandler from UM.i18n import i18nCatalog +from UM.Logger import Logger from UM.Message import Message +from UM.PluginRegistry import PluginRegistry +from UM.Scene.SceneNode import SceneNode +from UM.Settings.ContainerRegistry import ContainerRegistry from PyQt5.QtNetwork import QNetworkRequest from PyQt5.QtCore import QTimer, QUrl @@ -76,7 +77,11 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice): self.setIconName("print") - self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorItem.qml") + if PluginRegistry.getInstance() is not None: + self._monitor_view_qml_path = os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "qml", "MonitorStage.qml" + ) self._output_controller = LegacyUM3PrinterOutputController(self) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 723bcf2b7c..3fce903b1a 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -14,12 +14,14 @@ from PyQt5.QtGui import QDesktopServices from cura.CuraApplication import CuraApplication from cura.PrinterOutputDevice import ConnectionType from cura.Settings.GlobalStack import GlobalStack # typing -from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin + +from UM.i18n import i18nCatalog from UM.Logger import Logger +from UM.Message import Message +from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin +from UM.PluginRegistry import PluginRegistry from UM.Signal import Signal, signalemitter from UM.Version import Version -from UM.Message import Message -from UM.i18n import i18nCatalog from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice from .Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager @@ -455,8 +457,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._start_cloud_flow_message = Message( text = i18n_catalog.i18nc("@info:status", "Send and monitor print jobs from anywhere using your Ultimaker account."), lifetime = 0, - image_source = QUrl.fromLocalFile(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", - "resources", "svg", "cloud-flow-start.svg")), + image_source = QUrl.fromLocalFile(os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "svg", "cloud-flow-start.svg" + )), image_caption = i18n_catalog.i18nc("@info:status", "Connect to Ultimaker Cloud"), option_text = i18n_catalog.i18nc("@action", "Don't ask me again for this printer."), option_state = False @@ -477,8 +481,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._cloud_flow_complete_message = Message( text = i18n_catalog.i18nc("@info:status", "You can now send and monitor print jobs from anywhere using your Ultimaker account."), lifetime = 30, - image_source = QUrl.fromLocalFile(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", - "resources", "svg", "cloud-flow-completed.svg")), + image_source = QUrl.fromLocalFile(os.path.join( + PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), + "resources", "svg", "cloud-flow-completed.svg" + )), image_caption = i18n_catalog.i18nc("@info:status", "Connected!") ) # Don't show the review connection link if we're not on the local network diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 7319bac606..b4a7788c92 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -4579,7 +4579,7 @@ "description": "Whether to prime the filament with a blob before printing. Turning this setting on will ensure that the extruder will have material ready at the nozzle before printing. Printing Brim or Skirt can act like priming too, in which case turning this setting off saves some time.", "type": "bool", "resolve": "any(extruderValues('prime_blob_enable'))", - "default_value": true, + "default_value": false, "settable_per_mesh": false, "settable_per_extruder": true, "enabled": false diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index 72756de2a5..9e017688a2 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -78,7 +78,7 @@ "prime_tower_position_x": { "value": "machine_depth - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) - 30" }, "prime_tower_wipe_enabled": { "default_value": false }, - "prime_blob_enable": { "enabled": true }, + "prime_blob_enable": { "enabled": true, "default_value": true }, "acceleration_enabled": { "value": "True" }, "acceleration_layer_0": { "value": "acceleration_topbottom" },