diff --git a/cura/BuildPlateModel.py b/cura/BuildPlateModel.py new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/cura/BuildPlateModel.py @@ -0,0 +1,2 @@ + + diff --git a/cura/CuraActions.py b/cura/CuraActions.py index b51728f028..663da3ec09 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -19,6 +19,11 @@ from cura.MultiplyObjectsJob import MultiplyObjectsJob from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation from cura.Settings.ExtruderManager import ExtruderManager +from cura.Operations.SetBuildPlateNumberOperation import SetBuildPlateNumberOperation + +from UM.Logger import Logger + + class CuraActions(QObject): def __init__(self, parent = None): super().__init__(parent) @@ -124,5 +129,43 @@ class CuraActions(QObject): operation.addOperation(SetObjectExtruderOperation(node, extruder_id)) operation.push() + @pyqtSlot(int) + def setBuildPlateForSelection(self, build_plate_nr: int) -> None: + Logger.log("d", "Setting build plate number... %d" % build_plate_nr) + operation = GroupedOperation() + + nodes_to_change = [] + for node in Selection.getAllSelectedObjects(): + # Do not change any nodes that already have the right extruder set. + if node.callDecoration("getBuildPlateNumber") == build_plate_nr: + continue + + # If the node is a group, apply the active extruder to all children of the group. + if node.callDecoration("isGroup"): + for grouped_node in BreadthFirstIterator(node): + if grouped_node.callDecoration("getBuildPlateNumber") == build_plate_nr: + continue + + if grouped_node.callDecoration("isGroup"): + continue + + nodes_to_change.append(grouped_node) + continue + + nodes_to_change.append(node) + + if not nodes_to_change: + Logger.log("d", "Nothing to change.") + # If there are no changes to make, we still need to reset the selected extruders. + # This is a workaround for checked menu items being deselected while still being + # selected. + #ExtruderManager.getInstance().resetSelectedObjectExtruders() + return + + Logger.log("d", "Yes: %s", nodes_to_change) + for node in nodes_to_change: + operation.addOperation(SetBuildPlateNumberOperation(node, build_plate_nr)) + operation.push() + def _openUrl(self, url): QDesktopServices.openUrl(url) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index b09371ae0d..ff8dfd021a 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1,4 +1,5 @@ # Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtNetwork import QLocalServer @@ -53,6 +54,9 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager from cura.Settings.UserProfilesModel import UserProfilesModel from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager +# research +from cura.Scene.BuildPlateDecorator import BuildPlateDecorator + from . import PlatformPhysics from . import BuildVolume from . import CameraAnimation @@ -376,6 +380,10 @@ class CuraApplication(QtApplication): self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin") + # research + self._num_build_plates = 1 # default + self._active_build_plate = 1 + def _onEngineCreated(self): self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) @@ -855,6 +863,8 @@ class CuraApplication(QtApplication): activityChanged = pyqtSignal() sceneBoundingBoxChanged = pyqtSignal() preferredOutputMimetypeChanged = pyqtSignal() + numBuildPlatesChanged = pyqtSignal() + activeBuildPlateChanged = pyqtSignal() @pyqtProperty(bool, notify = activityChanged) def platformActivity(self): @@ -1025,7 +1035,7 @@ class CuraApplication(QtApplication): op.push() Selection.clear() - ## Reset all translation on nodes with mesh data. + ## Reset all translation on nodes with mesh data. @pyqtSlot() def resetAllTranslation(self): Logger.log("i", "Resetting all scene translations") @@ -1150,7 +1160,7 @@ class CuraApplication(QtApplication): job.start() else: Logger.log("w", "Unable to reload data because we don't have a filename.") - + ## Get logging data of the backend engine # \returns \type{string} Logging data @pyqtSlot(result = str) @@ -1373,6 +1383,7 @@ class CuraApplication(QtApplication): for node in nodes: node.setSelectable(True) node.setName(os.path.basename(filename)) + node.addDecorator(BuildPlateDecorator()) extension = os.path.splitext(filename)[1] if extension.lower() in self._non_sliceable_extensions: @@ -1445,3 +1456,24 @@ class CuraApplication(QtApplication): node = node.getParent() Selection.add(node) + + #### research - hacky place for these kind of thing + @pyqtSlot(int) + def setActiveBuildPlate(self, nr): + Logger.log("d", "Select build plate: %s" % nr) + self._active_build_plate = nr + self.activeBuildPlateChanged.emit() + + @pyqtSlot() + def newBuildPlate(self): + Logger.log("d", "New build plate") + self._num_build_plates += 1 + self.numBuildPlatesChanged.emit() + + @pyqtProperty(int, notify = numBuildPlatesChanged) + def numBuildPlates(self): + return self._num_build_plates + + @pyqtProperty(int, notify = activeBuildPlateChanged) + def activeBuildPlate(self): + return self._active_build_plate diff --git a/cura/Operations/SetBuildPlateNumberOperation.py b/cura/Operations/SetBuildPlateNumberOperation.py new file mode 100644 index 0000000000..bbef4caf84 --- /dev/null +++ b/cura/Operations/SetBuildPlateNumberOperation.py @@ -0,0 +1,27 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from UM.Scene.SceneNode import SceneNode +from UM.Operations.Operation import Operation + +from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator + +## Simple operation to set the extruder a certain object should be printed with. +class SetBuildPlateNumberOperation(Operation): + def __init__(self, node: SceneNode, build_plate_nr: int) -> None: + self._node = node + self._build_plate_nr = build_plate_nr + self._previous_build_plate_nr = None + self._decorator_added = False + + def undo(self): + if self._previous_build_plate_nr: + self._node.callDecoration("setBuildPlateNumber", self._previous_build_plate_nr) + + def redo(self): + stack = self._node.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway. + if not stack: + self._node.addDecorator(SettingOverrideDecorator()) + + self._previous_build_plate_nr = self._node.callDecoration("getBuildPlateNumber") + self._node.callDecoration("setBuildPlateNumber", self._build_plate_nr) diff --git a/cura/Scene/BuildPlateDecorator.py b/cura/Scene/BuildPlateDecorator.py new file mode 100644 index 0000000000..8d91f9e90a --- /dev/null +++ b/cura/Scene/BuildPlateDecorator.py @@ -0,0 +1,34 @@ +from UM.Scene.SceneNodeDecorator import SceneNodeDecorator +from UM.Scene.Selection import Selection + + +class BuildPlateDecorator(SceneNodeDecorator): + def __init__(self): + super().__init__() + self._build_plate_number = -1 + + def setBuildPlateNumber(self, nr): + self._build_plate_number = nr + # self.getNode().childrenChanged.connect(self._onChildrenChanged) + + def getBuildPlateNumber(self): + return self._build_plate_number + + # def setNode(self, node): + # super().setNode(node) + # self.getNode().childrenChanged.connect(self._onChildrenChanged) + + # def _onChildrenChanged(self, node): + # if not self.getNode().hasChildren(): + # # A group that no longer has children may remove itself from the scene + # self._old_parent = self.getNode().getParent() + # self.getNode().setParent(None) + # Selection.remove(self.getNode()) + # else: + # # A group that has removed itself from the scene because it had no children may add itself back to the scene when a child is added to it + # if not self.getNode().getParent() and self._old_parent: + # self.getNode().setParent(self._old_parent) + # self._old_parent = None + + def __deepcopy__(self, memo): + return BuildPlateDecorator() diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 39d497722f..175410773e 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -39,6 +39,26 @@ Menu onObjectRemoved: base.removeItem(object) } + MenuSeparator {} + MenuItem { + text: "build plate 0"; + onTriggered: CuraActions.setBuildPlateForSelection(0); + checkable: true + checked: false + } + MenuItem { + text: "build plate 1"; + onTriggered: CuraActions.setBuildPlateForSelection(1); + checkable: true + checked: false + } + MenuItem { + text: "build plate 2"; + onTriggered: CuraActions.setBuildPlateForSelection(2); + checkable: true + checked: false + } + // Global actions MenuSeparator {} MenuItem { action: Cura.Actions.selectAll; } diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index bb5999edb9..3c5485da32 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -28,6 +28,25 @@ Menu } ExclusiveGroup { id: group; } + MenuSeparator {} + MenuItem { + text: "build plate 0"; + onTriggered: CuraApplication.setActiveBuildPlate(0); + } + MenuItem { + text: "build plate 1"; + onTriggered: CuraApplication.setActiveBuildPlate(1); + } + MenuItem { + text: "build plate 2"; + onTriggered: CuraApplication.setActiveBuildPlate(2); + } + ExclusiveGroup { id: buildPlateGroup; } + + MenuItem { + text: "New build plate"; + onTriggered: CuraApplication.newBuildPlate(); + } MenuSeparator {} MenuItem { action: Cura.Actions.homeCamera; } }