From 2d3e6613bf115ca295f146c5087e7af9b4b07508 Mon Sep 17 00:00:00 2001 From: Rebecca Breu Date: Mon, 5 Apr 2021 19:34:26 +0200 Subject: [PATCH] Small enhancements around rotating --- beeref/commands.py | 5 ---- beeref/scene.py | 8 +++++++ beeref/selection.py | 3 ++- tests/test_commands.py | 52 ++++++++++++++++++++++++++++++++++++++++++ tests/test_scene.py | 35 ++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 6 deletions(-) diff --git a/beeref/commands.py b/beeref/commands.py index f2a4ea1..9d33092 100644 --- a/beeref/commands.py +++ b/beeref/commands.py @@ -120,16 +120,11 @@ class RotateItemsBy(QtGui.QUndoCommand): for item in self.items: item.setRotation(item.rotation() + self.delta, item.mapFromScene(self.anchor)) - item.scene().on_selection_change() def undo(self): - if self.ignore_first_redo: - self.ignore_first_redo = False - return for item in self.items: item.setRotation(item.rotation() - self.delta, item.mapFromScene(self.anchor)) - item.scene().on_selection_change() class NormalizeItems(QtGui.QUndoCommand): diff --git a/beeref/scene.py b/beeref/scene.py index 09d86c9..6915097 100644 --- a/beeref/scene.py +++ b/beeref/scene.py @@ -37,6 +37,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene): self.multi_select_item = MultiSelectItem() self.rubberband_item = RubberbandItem() self.selectionChanged.connect(self.on_selection_change) + self.changed.connect(self.on_change) def normalize_width_or_height(self, mode): """Scale the selected images to have the same width or height, as @@ -186,3 +187,10 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene): if not self.has_multi_selection() and self.multi_select_item.scene(): logger.debug('Removing multi select outline') self.removeItem(self.multi_select_item) + + def on_change(self, region): + if (self.multi_select_item.scene() + and not self.multi_select_item.scale_active + and not self.multi_select_item.rotate_active): + self.multi_select_item.fit_selection_area( + self.get_selection_rect()) diff --git a/beeref/selection.py b/beeref/selection.py index 95506da..5efc4c4 100644 --- a/beeref/selection.py +++ b/beeref/selection.py @@ -68,7 +68,7 @@ class SelectableMixin(BaseItemMixin): SELECT_LINE_WIDTH = 4 # line width for the selection box SELECT_HANDLE_SIZE = 15 # size of selection handles for scaling SELECT_RESIZE_SIZE = 20 # size of hover area for scaling - SELECT_ROTATE_SIZE = 20 # size of hover area for rotating + SELECT_ROTATE_SIZE = 15 # size of hover area for rotating def init_selectable(self): self.setAcceptHoverEvents(True) @@ -129,6 +129,7 @@ class SelectableMixin(BaseItemMixin): # If it's a single selection, draw the handles: if self.has_selection_handles(): pen.setWidth(self.SELECT_HANDLE_SIZE) + pen.setCapStyle(Qt.PenCapStyle.RoundCap) painter.setPen(pen) for corner in self.corners: painter.drawPoint(corner) diff --git a/tests/test_commands.py b/tests/test_commands.py index 1ec5c5a..00d0022 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -150,6 +150,58 @@ class ScaleItemsByTestCase(BeeTestCase): assert item2.pos().y() == 100 +class RotateItemsByTestCase(BeeTestCase): + + def test_redo_undo(self): + item1 = BeePixmapItem(QtGui.QImage()) + item1.setRotation(0) + + item2 = BeePixmapItem(QtGui.QImage()) + item2.setRotation(30) + item2.setPos(100, 100) + command = commands.RotateItemsBy([item1, item2], -90, + QtCore.QPointF(100, 100)) + command.redo() + assert item1.rotation() == -90 + assert item1.pos().x() == 0 + assert item1.pos().y() == 200 + assert item2.rotation() == -60 + assert item2.pos().x() == 100 + assert item2.pos().y() == 100 + command.undo() + assert item1.rotation() == 0 + assert item1.pos().x() == 0 + assert item1.pos().y() == 0 + assert item2.rotation() == 30 + assert item2.pos().x() == 100 + assert item2.pos().y() == 100 + + def test_ignore_first_redo(self): + item1 = BeePixmapItem(QtGui.QImage()) + item1.setRotation(0) + + item2 = BeePixmapItem(QtGui.QImage()) + item2.setRotation(30) + item2.setPos(100, 100) + command = commands.RotateItemsBy([item1, item2], -90, + QtCore.QPointF(100, 100), + ignore_first_redo=True) + command.redo() + assert item1.rotation() == 0 + assert item1.pos().x() == 0 + assert item1.pos().y() == 0 + assert item2.rotation() == 30 + assert item2.pos().x() == 100 + assert item2.pos().y() == 100 + command.redo() + assert item1.rotation() == -90 + assert item1.pos().x() == 0 + assert item1.pos().y() == 200 + assert item2.rotation() == -60 + assert item2.pos().x() == 100 + assert item2.pos().y() == 100 + + class NormalizeItemsTestCase(BeeTestCase): def test_redo_undo(self): diff --git a/tests/test_scene.py b/tests/test_scene.py index 6a86c53..c3631e3 100644 --- a/tests/test_scene.py +++ b/tests/test_scene.py @@ -412,3 +412,38 @@ class BeeGraphicsSceneTestCase(BeeTestCase): m_item = self.scene.multi_select_item m_item.fit_selection_area.assert_not_called() self.scene.removeItem.assert_called_once_with(m_item) + + def test_on_change_when_multi_select_when_no_scale_no_rotate(self): + self.scene.addItem(self.scene.multi_select_item) + self.scene.multi_select_item.fit_selection_area = MagicMock() + self.scene.get_selection_rect = MagicMock() + self.scene.multi_select_item.scale_active = False + self.scene.multi_select_item.rotate_active = False + self.scene.on_change(None) + self.scene.multi_select_item.fit_selection_area.assert_called_once() + + def test_on_change_when_multi_select_when_scale_active(self): + self.scene.addItem(self.scene.multi_select_item) + self.scene.multi_select_item.fit_selection_area = MagicMock() + self.scene.get_selection_rect = MagicMock() + self.scene.multi_select_item.scale_active = True + self.scene.multi_select_item.rotate_active = False + self.scene.on_change(None) + self.scene.multi_select_item.fit_selection_area.assert_not_called() + + def test_on_change_when_multi_select_when_rotate_active(self): + self.scene.addItem(self.scene.multi_select_item) + self.scene.multi_select_item.fit_selection_area = MagicMock() + self.scene.get_selection_rect = MagicMock() + self.scene.multi_select_item.scale_active = False + self.scene.multi_select_item.rotate_active = True + self.scene.on_change(None) + self.scene.multi_select_item.fit_selection_area.assert_not_called() + + def test_on_change_when_no_multi_select(self): + self.scene.multi_select_item.fit_selection_area = MagicMock() + self.scene.get_selection_rect = MagicMock() + self.scene.multi_select_item.scale_active = True + self.scene.multi_select_item.rotate_active = True + self.scene.on_change(None) + self.scene.multi_select_item.fit_selection_area.assert_not_called()