Add actions for undoing transformations

This commit is contained in:
Rebecca Breu 2021-04-16 22:28:13 +02:00
parent 3b919e4b7b
commit 6f77c43d76
6 changed files with 240 additions and 1 deletions

View file

@ -134,4 +134,33 @@ actions = [
'group': 'active_when_selection',
'enabled': False,
},
{
'id': 'reset_scale',
'text': 'Reset &Scale',
'callback': 'on_action_reset_scale',
'group': 'active_when_selection',
'enabled': False,
},
{
'id': 'reset_rotation',
'text': 'Reset &Rotation',
'callback': 'on_action_reset_rotation',
'group': 'active_when_selection',
'enabled': False,
},
{
'id': 'reset_flip',
'text': 'Reset &Flip',
'callback': 'on_action_reset_flip',
'group': 'active_when_selection',
'enabled': False,
},
{
'id': 'reset_transforms',
'text': 'Reset &All',
'shortcuts': ['R'],
'callback': 'on_action_reset_transforms',
'group': 'active_when_selection',
'enabled': False,
},
]

View file

@ -50,6 +50,11 @@ menu_structure = [
'items': [
'flip_horizontally',
'flip_vertically',
MENU_SEPARATOR,
'reset_scale',
'reset_rotation',
'reset_flip',
'reset_transforms',
],
},
{

View file

@ -160,3 +160,84 @@ class FlipItems(QtGui.QUndoCommand):
def undo(self):
self.redo()
class ResetScale(QtGui.QUndoCommand):
def __init__(self, items):
super().__init__('Reset Scale')
self.items = items
def redo(self):
self.old_scale_factors = []
for item in self.items:
self.old_scale_factors.append(item.scale())
item.setScale(1, anchor=item.center)
def undo(self):
for item, scale_factor in zip(self.items, self.old_scale_factors):
item.setScale(scale_factor, anchor=item.center)
class ResetRotation(QtGui.QUndoCommand):
def __init__(self, items):
super().__init__('Reset Rotation')
self.items = items
def redo(self):
self.old_rotations = []
for item in self.items:
self.old_rotations.append(item.rotation())
item.setRotation(0, anchor=item.center)
def undo(self):
for item, rotation in zip(self.items, self.old_rotations):
item.setRotation(rotation, anchor=item.center)
class ResetFlip(QtGui.QUndoCommand):
def __init__(self, items):
super().__init__('Reset Flip')
self.items = items
def redo(self):
self.old_flips = []
for item in self.items:
self.old_flips.append(item.flip())
if item.flip() == -1:
item.do_flip(anchor=item.center)
def undo(self):
for item, flip in zip(self.items, self.old_flips):
if flip == -1:
item.do_flip(anchor=item.center)
class ResetTransforms(QtGui.QUndoCommand):
def __init__(self, items):
super().__init__('Reset All Transformations')
self.items = items
def redo(self):
self.old_values = []
for item in self.items:
self.old_values.append({
'scale': item.scale(),
'rotation': item.rotation(),
'flip': item.flip(),
})
item.setScale(1, anchor=item.center)
item.setRotation(0, anchor=item.center)
if item.flip() == -1:
item.do_flip(anchor=item.center)
def undo(self):
for item, old in zip(self.items, self.old_values):
item.setScale(old['scale'], anchor=item.center)
item.setRotation(old['rotation'], anchor=item.center)
if old['flip'] == -1:
item.do_flip(anchor=item.center)

View file

@ -96,10 +96,14 @@ class BaseItemMixin:
if vertical:
self.setRotation(self.rotation() + 180)
@property
def center(self):
return QtCore.QPointF(self.width, self.height) / 2
@property
def center_scene_coords(self):
"""The item's center in scene coordinates."""
return self.mapToScene(QtCore.QPointF(self.width, self.height) / 2)
return self.mapToScene(self.center)
class SelectableMixin(BaseItemMixin):

View file

@ -171,6 +171,22 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
def on_action_flip_vertically(self):
self.scene.flip_items(vertical=True)
def on_action_reset_scale(self):
self.undo_stack.push(commands.ResetScale(
self.scene.selectedItems()))
def on_action_reset_rotation(self):
self.undo_stack.push(commands.ResetRotation(
self.scene.selectedItems()))
def on_action_reset_flip(self):
self.undo_stack.push(commands.ResetFlip(
self.scene.selectedItems()))
def on_action_reset_transforms(self):
self.undo_stack.push(commands.ResetTransforms(
self.scene.selectedItems()))
def on_items_loaded(self, value):
self.scene.add_delayed_items()

View file

@ -291,3 +291,107 @@ class FlipItemsTestCase(BeeTestCase):
assert item2.flip() == -1
assert item2.rotation() == 30
assert item2.pos() == QtCore.QPointF(100, 100)
class ResetScaleTestCase(BeeTestCase):
def test_redo_undo(self):
item = BeePixmapItem(QtGui.QImage())
item.setScale(2)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetScale([item])
command.redo()
assert item.scale() == 1
assert item.pos().x() == 50
assert item.pos().y() == 40
command.undo()
assert item.scale() == 2
assert item.pos().x() == 0
assert item.pos().y() == 0
class ResetRotateTestCase(BeeTestCase):
def test_redo_undo(self):
item = BeePixmapItem(QtGui.QImage())
item.setRotation(180)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetRotation([item])
command.redo()
assert item.rotation() == 0
assert item.pos().x() == -100
assert item.pos().y() == -80
command.undo()
assert item.rotation() == 180
assert item.pos().x() == 0
assert item.pos().y() == 0
class ResetFlipTestCase(BeeTestCase):
def test_redo_undo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.do_flip()
item2 = BeePixmapItem(QtGui.QImage())
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetFlip([item1, item2])
command.redo()
assert item1.flip() == 1
assert item1.pos().x() == -100
assert item1.pos().y() == 0
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0
command.undo()
assert item1.flip() == -1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0
class ResetTransformsTestCase(BeeTestCase):
def test_redo_undo(self):
item1 = BeePixmapItem(QtGui.QImage())
item1.setScale(2)
item1.do_flip()
item2 = BeePixmapItem(QtGui.QImage())
item2.setRotation(180)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
command = commands.ResetTransforms([item1, item2])
command.redo()
assert item1.scale() == 1
assert item1.rotation() == 0
assert item1.flip() == 1
assert item1.pos().x() == -150
assert item1.pos().y() == 40
assert item2.scale() == 1
assert item2.rotation() == 0
assert item2.flip() == 1
assert item2.pos().x() == -100
assert item2.pos().y() == -80
command.undo()
assert item1.scale() == 2
assert item1.rotation() == 0
assert item1.flip() == -1
assert item1.pos().x() == 0
assert item1.pos().y() == 0
assert item2.scale() == 1
assert item2.rotation() == 180
assert item2.flip() == 1
assert item2.pos().x() == 0
assert item2.pos().y() == 0