diff --git a/beeref/commands.py b/beeref/commands.py index a127809..0c27d64 100644 --- a/beeref/commands.py +++ b/beeref/commands.py @@ -43,18 +43,14 @@ class DeleteItems(QtGui.QUndoCommand): self.items = items def redo(self): - print(len(self.scene.items())) for item in self.items: self.scene.removeItem(item) - print(len(self.scene.items())) def undo(self): - print(len(self.scene.items())) self.scene.clearSelection() for item in self.items: item.setSelected(True) self.scene.addItem(item) - print(len(self.scene.items())) class MoveItemsBy(QtGui.QUndoCommand): diff --git a/beeref/items.py b/beeref/items.py index b8e525c..60acd47 100644 --- a/beeref/items.py +++ b/beeref/items.py @@ -111,7 +111,7 @@ class BeePixmapItem(QtWidgets.QGraphicsPixmapItem): return self.fixed_length_for_viewport(self.SELECT_ROTATE_SIZE) def draw_debug_shape(self, painter, shape, r, g, b): - color = QtGui.QColor(r, g, b, 20) + color = QtGui.QColor(r, g, b, 50) if isinstance(shape, QtCore.QRectF): painter.fillRect(shape, color) else: @@ -120,6 +120,10 @@ class BeePixmapItem(QtWidgets.QGraphicsPixmapItem): def paint(self, painter, option, widget): painter.drawPixmap(0, 0, self.pixmap()) + if self.select_debug: + self.draw_debug_shape(painter, self.boundingRect(), 0, 255, 0) + self.draw_debug_shape(painter, self.shape(), 255, 0, 0) + if not self.isSelected(): return @@ -139,10 +143,6 @@ class BeePixmapItem(QtWidgets.QGraphicsPixmapItem): painter.setPen(pen) painter.drawPoint(self.width, self.height) - if self.select_debug: - self.draw_debug_shape(painter, self.boundingRect(), 0, 255, 0) - self.draw_debug_shape(painter, self.shape(), 255, 0, 0) - @property def bottom_right_scale_bounds(self): """The interactable shape of the bottom right scale handle""" @@ -172,10 +172,13 @@ class BeePixmapItem(QtWidgets.QGraphicsPixmapItem): bounds.bottomRight().y() + 2 * margin) def shape(self): - path = QtGui.QPainterPath() - path.addRect(self.bottom_right_scale_bounds) - path.addRect(self.bottom_right_rotate_bounds) - return path + super().shape() + shape_ = super().shape() + if self.isSelected(): + path = QtGui.QPainterPath() + path.addRect(self.bottom_right_scale_bounds) + path.addRect(self.bottom_right_rotate_bounds) + shape_ = shape_ + path + return shape_ def update_selection(self): new_scale = self.fixed_length_for_viewport(1) diff --git a/tests/test_items.py b/tests/test_items.py index b84ae0b..b6be078 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -1,8 +1,9 @@ -from unittest.mock import patch, PropertyMock +from unittest.mock import patch, MagicMock, PropertyMock -from PyQt6 import QtGui, QtWidgets +from PyQt6 import QtCore, QtGui, QtWidgets from beeref.items import BeePixmapItem +from beeref.scene import BeeGraphicsScene from .base import BeeTestCase @@ -22,8 +23,10 @@ class BeePixmapItemTestCase(BeeTestCase): def test_set_scale(self): item = BeePixmapItem(QtGui.QImage()) + item.prepareGeometryChange = MagicMock() item.setScale(3) assert item.scale() == 3 + item.prepareGeometryChange.assert_called_once() def test_set_scale_ignores_zero(self): item = BeePixmapItem(QtGui.QImage()) @@ -44,3 +47,158 @@ class BeePixmapItemTestCase(BeeTestCase): item.set_pos_center(0, 0) assert item.pos().x() == -100 assert item.pos().y() == -50 + + +class BeePixmapItemPaintstuffTestCase(BeeTestCase): + + def setUp(self): + self.scene = BeeGraphicsScene(None) + self.item = BeePixmapItem(QtGui.QImage()) + self.scene.addItem(self.item) + self.view = MagicMock(get_scale=MagicMock(return_value=1)) + views_patcher = patch('beeref.scene.BeeGraphicsScene.views') + views_mock = views_patcher.start() + views_mock.return_value = [self.view] + self.addCleanup(views_patcher.stop) + + def test_fixed_length_for_viewport_when_default_scales(self): + self.view.get_scale = MagicMock(return_value=1) + assert self.item.fixed_length_for_viewport(100) == 100 + + def test_fixed_length_for_viewport_when_viewport_scaled(self): + self.view.get_scale = MagicMock(return_value=2) + assert self.item.fixed_length_for_viewport(100) == 50 + + def test_fixed_length_for_viewport_when_item_scaled(self): + self.view.get_scale = MagicMock(return_value=1) + self.item.setScale(5) + assert self.item.fixed_length_for_viewport(100) == 20 + + def test_resize_size_when_scaled(self): + self.view.get_scale = MagicMock(return_value=2) + self.item.setScale(2) + self.item.SELECT_RESIZE_SIZE = 100 + assert self.item.select_resize_size == 25 + + def test_rotate_size_when_scaled(self): + self.view.get_scale = MagicMock(return_value=2) + self.item.setScale(2) + self.item.SELECT_ROTATE_SIZE = 100 + assert self.item.select_rotate_size == 25 + + def test_paint_when_not_selected(self): + painter = MagicMock() + self.item.setSelected(False) + self.item.pixmap = MagicMock(return_value='pixmap') + self.item.paint(painter, None, None) + painter.drawPixmap.assert_called_once_with(0, 0, 'pixmap') + painter.drawRect.assert_not_called() + painter.drawPoint.assert_not_called() + + def test_paint_when_selected_single_selection(self): + painter = MagicMock() + self.item.setSelected(True) + self.item.paint(painter, None, None) + painter.drawPixmap.assert_called_once() + painter.drawRect.assert_called_once() + painter.drawPoint.assert_called() + + def test_paint_when_selected_multi_selection(self): + item2 = BeePixmapItem(QtGui.QImage()) + item2.setSelected(True) + self.scene.addItem(item2) + painter = MagicMock() + self.item.setSelected(True) + self.item.paint(painter, None, None) + painter.drawPixmap.assert_called_once() + painter.drawRect.assert_called_once() + painter.drawPoint.assert_not_called() + + def test_bottom_right_scale_bounds(self): + self.view.get_scale = MagicMock(return_value=1) + self.item.SELECT_RESIZE_SIZE = 10 + with patch('beeref.items.BeePixmapItem.width', + new_callable=PropertyMock, return_value=100): + with patch('beeref.items.BeePixmapItem.height', + new_callable=PropertyMock, return_value=80): + rect = self.item.bottom_right_scale_bounds + assert rect.topLeft().x() == 95 + assert rect.topLeft().y() == 75 + assert rect.bottomRight().x() == 105 + assert rect.bottomRight().y() == 85 + + def test_bottom_right_rotate_bounds(self): + self.view.get_scale = MagicMock(return_value=1) + self.item.SELECT_RESIZE_SIZE = 10 + self.item.SELECT_ROTATE_SIZE = 10 + with patch('beeref.items.BeePixmapItem.width', + new_callable=PropertyMock, return_value=100): + with patch('beeref.items.BeePixmapItem.height', + new_callable=PropertyMock, return_value=80): + rect = self.item.bottom_right_rotate_bounds + assert rect.topLeft().x() == 105 + assert rect.topLeft().y() == 85 + assert rect.bottomRight().x() == 115 + assert rect.bottomRight().y() == 95 + + def test_bounding_rect_when_not_selected(self): + self.view.get_scale = MagicMock(return_value=1) + self.item.setSelected(False) + with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.boundingRect', + return_value=QtCore.QRectF(0, 0, 100, 80)): + rect = self.item.boundingRect() + assert rect.topLeft().x() == 0 + assert rect.topLeft().y() == 0 + assert rect.bottomRight().x() == 100 + assert rect.bottomRight().y() == 80 + + def test_bounding_rect_when_selected(self): + self.item.SELECT_RESIZE_SIZE = 10 + self.item.SELECT_ROTATE_SIZE = 10 + self.view.get_scale = MagicMock(return_value=1) + self.item.setSelected(True) + with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.boundingRect', + return_value=QtCore.QRectF(0, 0, 100, 80)): + rect = self.item.boundingRect() + assert rect.topLeft().x() == -15 + assert rect.topLeft().y() == -15 + assert rect.bottomRight().x() == 115 + assert rect.bottomRight().y() == 95 + + def test_shape_when_not_selected(self): + self.view.get_scale = MagicMock(return_value=1) + self.item.setSelected(False) + path = QtGui.QPainterPath() + path.addRect(QtCore.QRectF(0, 0, 100, 80)) + + with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.shape', + return_value=path): + with patch('beeref.items.BeePixmapItem.width', + new_callable=PropertyMock, return_value=100): + with patch('beeref.items.BeePixmapItem.height', + new_callable=PropertyMock, return_value=80): + shape = self.item.shape().boundingRect() + assert shape.topLeft().x() == 0 + assert shape.topLeft().y() == 0 + assert shape.bottomRight().x() == 100 + assert shape.bottomRight().y() == 80 + + def test_shape_when_selected(self): + self.item.SELECT_RESIZE_SIZE = 10 + self.item.SELECT_ROTATE_SIZE = 10 + self.view.get_scale = MagicMock(return_value=1) + self.item.setSelected(True) + path = QtGui.QPainterPath() + path.addRect(QtCore.QRectF(0, 0, 100, 80)) + + with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.shape', + return_value=path): + with patch('beeref.items.BeePixmapItem.width', + new_callable=PropertyMock, return_value=100): + with patch('beeref.items.BeePixmapItem.height', + new_callable=PropertyMock, return_value=80): + shape = self.item.shape().boundingRect() + assert shape.topLeft().x() == 0 + assert shape.topLeft().y() == 0 + assert shape.bottomRight().x() == 115 + assert shape.bottomRight().y() == 95