mirror of
https://github.com/rbreu/beeref.git
synced 2026-03-11 08:54:28 +00:00
Add actions for undoing transformations
This commit is contained in:
parent
3b919e4b7b
commit
6f77c43d76
6 changed files with 240 additions and 1 deletions
|
|
@ -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,
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@ menu_structure = [
|
|||
'items': [
|
||||
'flip_horizontally',
|
||||
'flip_vertically',
|
||||
MENU_SEPARATOR,
|
||||
'reset_scale',
|
||||
'reset_rotation',
|
||||
'reset_flip',
|
||||
'reset_transforms',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue