Refactor grouping/ungrouping into an operation that is undoable

CURA-1543
This commit is contained in:
fieldOfView 2016-05-11 15:21:01 +02:00
parent 0b0e53dcf5
commit 4830943113
2 changed files with 72 additions and 23 deletions

View file

@ -23,6 +23,7 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.SetTransformOperation import SetTransformOperation from UM.Operations.SetTransformOperation import SetTransformOperation
from cura.SetParentOperation import SetParentOperation
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
@ -543,6 +544,7 @@ class CuraApplication(QtApplication):
@pyqtSlot() @pyqtSlot()
def groupSelected(self): def groupSelected(self):
# Create a group-node
group_node = SceneNode() group_node = SceneNode()
group_decorator = GroupDecorator() group_decorator = GroupDecorator()
group_node.addDecorator(group_decorator) group_node.addDecorator(group_decorator)
@ -552,40 +554,37 @@ class CuraApplication(QtApplication):
group_node.setPosition(center) group_node.setPosition(center)
group_node.setCenterPosition(center) group_node.setCenterPosition(center)
for node in Selection.getAllSelectedObjects(): # Move selected nodes into the group-node
world = node.getWorldPosition() op = GroupedOperation()
node.setParent(group_node) nodes = Selection.getAllSelectedObjects()
node.setPosition(world - center) for node in nodes:
op.addOperation(SetParentOperation(node, group_node))
op.push()
# Deselect individual nodes and select the groupnode instead
for node in group_node.getChildren(): for node in group_node.getChildren():
Selection.remove(node) Selection.remove(node)
Selection.add(group_node) Selection.add(group_node)
@pyqtSlot() @pyqtSlot()
def ungroupSelected(self): def ungroupSelected(self):
ungrouped_nodes = []
selected_objects = Selection.getAllSelectedObjects()[:] #clone the list selected_objects = Selection.getAllSelectedObjects()[:] #clone the list
for node in selected_objects: for node in selected_objects:
if node.callDecoration("isGroup" ): if node.callDecoration("isGroup"):
children_to_move = [] op = GroupedOperation()
for child in node.getChildren():
if type(child) is SceneNode:
children_to_move.append(child)
for child in children_to_move: group_parent = node.getParent()
position = child.getWorldPosition() children = node.getChildren()[:] #clone the list
child.setParent(node.getParent()) for child in children:
child.setPosition(position - node.getParent().getWorldPosition()) # Set the parent of the children to the parent of the group-node
child.scale(node.getScale()) op.addOperation(SetParentOperation(child, group_parent))
child.rotate(node.getOrientation())
# Add all individual nodes to the selection
Selection.add(child) Selection.add(child)
child.callDecoration("setConvexHull",None) child.callDecoration("setConvexHull", None)
node.setParent(None)
ungrouped_nodes.append(node) op.push()
for node in ungrouped_nodes: # Note: The group removes itself from the scene once all its children have left it, see GroupDecorator._onChildrenChanged
Selection.remove(node)
def _createSplashScreen(self): def _createSplashScreen(self):
return CuraSplashScreen.CuraSplashScreen() return CuraSplashScreen.CuraSplashScreen()

View file

@ -0,0 +1,50 @@
# Copyright (c) 2016 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from UM.Scene.SceneNode import SceneNode
from UM.Operations import Operation
from UM.Math.Vector import Vector
## An operation that parents a scene node to another scene node.
class SetParentOperation(Operation.Operation):
## Initialises this SetParentOperation.
#
# \param node The node which will be reparented.
# \param parent_node The node which will be the parent.
def __init__(self, node, parent_node):
super().__init__()
self._node = node
self._parent = parent_node
self._old_parent = node.getParent() # To restore the previous parent in case of an undo.
## Undoes the set-parent operation, restoring the old parent.
def undo(self):
self._set_parent(self._old_parent)
## Re-applies the set-parent operation.
def redo(self):
self._set_parent(self._parent)
## Sets the parent of the node while applying transformations to the world-transform of the node stays the same.
#
# \param new_parent The new parent. Note: this argument can be None, which would hide the node from the scene.
def _set_parent(self, new_parent):
if new_parent:
self._node.setPosition(self._node.getWorldPosition() - new_parent.getWorldPosition())
current_parent = self._node.getParent()
if current_parent:
self._node.scale(current_parent.getScale() / new_parent.getScale())
self._node.rotate(current_parent.getOrientation())
else:
self._node.scale(Vector(1, 1, 1) / new_parent.getScale())
self._node.rotate(new_parent.getOrientation().getInverse())
self._node.setParent(new_parent)
## Returns a programmer-readable representation of this operation.
#
# \return A programmer-readable representation of this operation.
def __repr__(self):
return "SetParentOperation(node = {0}, parent_node={1})".format(self._node, self._parent)