diff --git a/UM/Controller.py b/UM/Controller.py index 451a899ddb..46fe91810e 100644 --- a/UM/Controller.py +++ b/UM/Controller.py @@ -464,7 +464,7 @@ def getToolsEnabled(self) -> bool: def setToolsEnabled(self, enabled: bool) -> None: self._tools_enabled = enabled - def deleteAllNodesWithMeshData(self, only_selectable:bool = True) -> None: + def deleteAllNodesWithMeshData(self, only_selectable:bool = True, clear_all:bool = False) -> None: Logger.log("i", "Clearing scene") if not self.getToolsEnabled(): return @@ -492,9 +492,12 @@ def deleteAllNodesWithMeshData(self, only_selectable:bool = True) -> None: self.getScene().sceneChanged.emit(node) op.push() + if clear_all: + op.clear() from UM.Scene.Selection import Selection Selection.clear() + # Rotate camera view according defined angle def setCameraRotation(self, coordinate: str = "x", angle: int = 0) -> None: camera = self._scene.getActiveCamera() diff --git a/UM/Operations/AddSceneNodeOperation.py b/UM/Operations/AddSceneNodeOperation.py index 2be09ef331..b86d20c0da 100644 --- a/UM/Operations/AddSceneNodeOperation.py +++ b/UM/Operations/AddSceneNodeOperation.py @@ -46,3 +46,8 @@ def redo(self) -> None: self._node.setParent(self._parent) if self._selected: # It was selected while the operation was undone. We should restore that selection. Selection.add(self._node) + + def delete(self) -> None: + self._node.reset() + if self._parent.getName() != "Root": + self._parent.reset() \ No newline at end of file diff --git a/UM/Operations/GroupedOperation.py b/UM/Operations/GroupedOperation.py index 840d45a555..6df468123f 100644 --- a/UM/Operations/GroupedOperation.py +++ b/UM/Operations/GroupedOperation.py @@ -91,3 +91,7 @@ def __repr__(self): for child in self._children: output += "{0!r}\n".format(child) return output + + def delete(self) -> None: + for node in self._children: + node.delete() \ No newline at end of file diff --git a/UM/Operations/Operation.py b/UM/Operations/Operation.py index e2fb360b2d..72da53f664 100644 --- a/UM/Operations/Operation.py +++ b/UM/Operations/Operation.py @@ -57,3 +57,11 @@ def push(self) -> None: # Because of circular dependency from UM.Application import Application Application.getInstance().getOperationStack().push(self) + + def clear(self) -> None: + # Because of circular dependency + from UM.Application import Application + Application.getInstance().getOperationStack().clearStack() + + def delete(self) -> None: + pass diff --git a/UM/Operations/OperationStack.py b/UM/Operations/OperationStack.py index de8b9d43f9..50ba139fb3 100644 --- a/UM/Operations/OperationStack.py +++ b/UM/Operations/OperationStack.py @@ -1,6 +1,5 @@ # Copyright (c) 2019 Ultimaker B.V. # Uranium is released under the terms of the LGPLv3 or higher. - import threading import time @@ -34,6 +33,23 @@ def __init__(self, controller) -> None: def _onToolOperationStarted(self, tool): self._merge_operations = False + def clearStackMemory(self, indexStart, indexEnd): + for node in self._operations[indexStart:indexEnd]: + node.delete() + del self._operations[indexStart:indexEnd] + + + def clearStack(self): + try: + with self._lock: + self.clearStackMemory(0, len(self._operations)) + self._current_index = -1 + self._merge_operations = False + self.changed.emit() + except Exception as e: + print("Error in clearing memory", e) + + def _onToolOperationStopped(self, tool): self._merge_operations = False @@ -56,7 +72,7 @@ def push(self, operation): try: if self._current_index < len(self._operations) - 1: - del self._operations[self._current_index + 1:len(self._operations)] + self.clearStackMemory(self._current_index + 1, len(self._operations)) self._operations.append(operation) operation.redo() diff --git a/UM/Operations/RemoveSceneNodeOperation.py b/UM/Operations/RemoveSceneNodeOperation.py index 90a14f4878..eb3712d203 100644 --- a/UM/Operations/RemoveSceneNodeOperation.py +++ b/UM/Operations/RemoveSceneNodeOperation.py @@ -43,3 +43,8 @@ def redo(self) -> None: pass if Selection.isSelected(self._node): # Also remove the selection. Selection.remove(self._node) + + def delete(self) -> None: + self._node.reset() + if self._parent.getName() != "Root": + self._parent.reset() diff --git a/UM/Qt/QtApplication.py b/UM/Qt/QtApplication.py index c4575fd42d..de85e835db 100644 --- a/UM/Qt/QtApplication.py +++ b/UM/Qt/QtApplication.py @@ -627,18 +627,18 @@ def createQmlComponent(self, qml_file_path: str, context_properties: Dict[str, " return result @pyqtSlot() - def deleteAll(self, only_selectable = True) -> None: + def deleteAll(self, only_selectable = True, clear_all:bool = False) -> None: """Delete all nodes containing mesh data in the scene. :param only_selectable:. Set this to False to delete objects from all build plates """ - self.getController().deleteAllNodesWithMeshData(only_selectable) + self.getController().deleteAllNodesWithMeshData(only_selectable, clear_all = clear_all) @pyqtSlot() def resetWorkspace(self) -> None: self._workspace_metadata_storage.clear() self._current_workspace_information.clear() - self.deleteAll() + self.deleteAll(clear_all = True) self.workspaceLoaded.emit("") self.getController().getScene().clearMetaData() diff --git a/UM/Scene/SceneNode.py b/UM/Scene/SceneNode.py index 0ec1af8a7f..6a9165b6ed 100644 --- a/UM/Scene/SceneNode.py +++ b/UM/Scene/SceneNode.py @@ -101,6 +101,31 @@ def __init__(self, parent: Optional["SceneNode"] = None, visible: bool = True, n if parent: parent.addChild(self) + def reset(self): + """Reset this scene node instance""" + self._children = [] + self._mesh_data = None + self.metadata = {} + self._transformation = Matrix() + self._position = Vector() + self._scale = Vector(1.0, 1.0, 1.0) + self._shear = Vector(0.0, 0.0, 0.0) + self._mirror = Vector(1.0, 1.0, 1.0) + self._orientation = Quaternion() + self._world_transformation = Matrix() + self._cached_normal_matrix = Matrix() + self._derived_position = Vector() + self._derived_orientation = Quaternion() + self._derived_scale = Vector() + self._enabled = False + self._selectable = False + self._resetAABB() + self._visible = False + self._name = "" + self._id = "" + self.removeDecorators() + self._settings = {} + def __deepcopy__(self, memo: Dict[int, object]) -> "SceneNode": copy = self.__class__() copy.setTransformation(self.getLocalTransformation()) @@ -262,9 +287,8 @@ def getDecorator(self, dec_type: type) -> Optional[SceneNodeDecorator]: def removeDecorators(self): """Remove all decorators""" - for decorator in self._decorators: - decorator.clear() + decorator.clearDecoratorData() self._decorators = [] self.decoratorsChanged.emit(self) diff --git a/UM/Scene/SceneNodeDecorator.py b/UM/Scene/SceneNodeDecorator.py index c7c9562895..c6efe88ccf 100644 --- a/UM/Scene/SceneNodeDecorator.py +++ b/UM/Scene/SceneNodeDecorator.py @@ -24,10 +24,9 @@ def setNode(self, node: "SceneNode") -> None: def getNode(self) -> Optional["SceneNode"]: return self._node - def clear(self) -> None: + def clearDecoratorData(self) -> None: """Clear all data associated with this decorator. This will be called before the decorator is removed""" - - pass + self._node = None def __deepcopy__(self, memo: Dict[int, object]) -> "SceneNodeDecorator": raise NotImplementedError("Subclass {0} of SceneNodeDecorator should implement their own __deepcopy__() method.".format(str(self)))