diff --git a/beeref/scene.py b/beeref/scene.py index 328ea25..ed2b061 100644 --- a/beeref/scene.py +++ b/beeref/scene.py @@ -129,8 +129,9 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene): item = self.itemAt(event.scenePos(), self.views()[0].transform()) if item: self.move_active = False - self.views()[0].fit_rect(self.get_selection_rect(), - toggle_item=item) + self.views()[0].fit_rect( + self.itemsBoundingRect(selection_only=True), + toggle_item=item) return super().mouseDoubleClickEvent(event) @@ -178,11 +179,19 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene): for item in self.selectedItems(): item.on_view_scale_change() - def get_selection_rect(self): - """Returns the bounding rect of the currently selected items.""" + def itemsBoundingRect(self, selection_only=False): + """Returns the bounding rect of the scene's items; either all of them + or only selected ones. + + Re-implemented to not include the items's selection handles. + """ + + base = self.selectedItems() if selection_only else self.items() + items = list(filter(lambda i: hasattr(i, 'save_id'), base)) + + if not items: + return QtCore.QRectF(0, 0, 0, 0) - items = list(filter(lambda i: hasattr(i, 'save_id'), - self.selectedItems())) x = [] y = [] @@ -196,13 +205,13 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene): QtCore.QPointF(max(x), max(y))) def get_selection_center(self): - rect = self.get_selection_rect() + rect = self.itemsBoundingRect(selection_only=True) return (rect.topLeft() + rect.bottomRight()) / 2 def on_selection_change(self): if self.has_multi_selection(): self.multi_select_item.fit_selection_area( - self.get_selection_rect()) + self.itemsBoundingRect(selection_only=True)) if self.has_multi_selection() and not self.multi_select_item.scene(): logger.debug('Adding multi select outline') self.addItem(self.multi_select_item) @@ -216,7 +225,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene): 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()) + self.itemsBoundingRect(selection_only=True)) def add_item_later(self, item, selected=False): """Keep an item for adding later via ``add_delayed_items``""" diff --git a/beeref/view.py b/beeref/view.py index 5106e77..f6d6e4c 100644 --- a/beeref/view.py +++ b/beeref/view.py @@ -141,7 +141,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin): self.fit_rect(self.scene.itemsBoundingRect()) def on_action_fit_selection(self): - self.fit_rect(self.scene.get_selection_rect()) + self.fit_rect(self.scene.itemsBoundingRect(selection_only=True)) def on_action_undo(self): logger.debug('Undo: %s' % self.undo_stack.undoText()) diff --git a/tests/test_scene.py b/tests/test_scene.py index bc3ef95..12ee4b6 100644 --- a/tests/test_scene.py +++ b/tests/test_scene.py @@ -72,7 +72,7 @@ class BeeGraphicsSceneTestCase(BeeTestCase): self.scene.addItem(item) item.setSelected(True) self.scene.undo_stack = MagicMock(push=MagicMock()) - with patch('beeref.scene.BeeGraphicsScene.get_selection_rect', + with patch('beeref.scene.BeeGraphicsScene.itemsBoundingRect', return_value=QtCore.QRectF(10, 20, 100, 60)): self.scene.flip_items(vertical=True) args = self.scene.undo_stack.push.call_args_list[0][0] @@ -372,7 +372,7 @@ class BeeGraphicsSceneTestCase(BeeTestCase): self.scene.on_view_scale_change() item.on_view_scale_change.assert_called_once() - def test_get_selection_rect_two_items(self): + def test_items_bounding_rect_two_items_selection_only(self): item1 = BeePixmapItem(QtGui.QImage()) self.scene.addItem(item1) item1.setSelected(True) @@ -390,32 +390,50 @@ class BeeGraphicsSceneTestCase(BeeTestCase): new_callable=PropertyMock, return_value=100): with patch('beeref.items.BeePixmapItem.height', new_callable=PropertyMock, return_value=100): - rect = self.scene.get_selection_rect() + rect = self.scene.itemsBoundingRect(selection_only=True) assert rect.topLeft().x() == -33 assert rect.topLeft().y() == -6 assert rect.bottomRight().x() == 104 assert rect.bottomRight().y() == 122 - def test_get_selection_rect_rotated_item(self): + def test_items_bounding_rect_rotated_item(self): item = BeePixmapItem(QtGui.QImage()) self.scene.addItem(item) - item.setSelected(True) item.setRotation(-45) with patch('beeref.items.BeePixmapItem.width', new_callable=PropertyMock, return_value=100): with patch('beeref.items.BeePixmapItem.height', new_callable=PropertyMock, return_value=100): - rect = self.scene.get_selection_rect() + rect = self.scene.itemsBoundingRect() assert rect.topLeft().x() == 0 assert rect.topLeft().y() == approx(-math.sqrt(2) * 50) assert rect.bottomRight().x() == approx(math.sqrt(2) * 100) assert rect.bottomRight().y() == approx(math.sqrt(2) * 50) + def test_items_bounding_rect_flipped_item(self): + item = BeePixmapItem(QtGui.QImage()) + self.scene.addItem(item) + item.do_flip() + with patch('beeref.items.BeePixmapItem.width', + new_callable=PropertyMock, return_value=50): + with patch('beeref.items.BeePixmapItem.height', + new_callable=PropertyMock, return_value=100): + rect = self.scene.itemsBoundingRect() + + assert rect.topLeft().x() == -50 + assert rect.topLeft().y() == 0 + assert rect.bottomRight().x() == 0 + assert rect.bottomRight().y() == 100 + + def test_items_bounding_rect_when_no_items(self): + rect = self.scene.itemsBoundingRect() + assert rect == QtCore.QRectF(0, 0, 0, 0) + def test_get_selection_center(self): - with patch('beeref.scene.BeeGraphicsScene.get_selection_rect', + with patch('beeref.scene.BeeGraphicsScene.itemsBoundingRect', return_value=QtCore.QRectF(10, 20, 100, 60)): center = self.scene.get_selection_center() assert center == QtCore.QPointF(60, 50) @@ -424,7 +442,7 @@ class BeeGraphicsSceneTestCase(BeeTestCase): self.scene.has_multi_selection = MagicMock(return_value=True) self.scene.multi_select_item.fit_selection_area = MagicMock() self.scene.multi_select_item.bring_to_front = MagicMock() - self.scene.get_selection_rect = MagicMock( + self.scene.itemsBoundingRect = MagicMock( return_value=QtCore.QRectF(0, 0, 100, 80)) self.scene.addItem = MagicMock() @@ -441,7 +459,7 @@ class BeeGraphicsSceneTestCase(BeeTestCase): self.scene.has_multi_selection = MagicMock(return_value=True) self.scene.multi_select_item.fit_selection_area = MagicMock() self.scene.multi_select_item.bring_to_front = MagicMock() - self.scene.get_selection_rect = MagicMock( + self.scene.itemsBoundingRect = MagicMock( return_value=QtCore.QRectF(0, 0, 100, 80)) self.scene.addItem = MagicMock() @@ -458,7 +476,7 @@ class BeeGraphicsSceneTestCase(BeeTestCase): self.scene.has_multi_selection = MagicMock(return_value=False) self.scene.multi_select_item.fit_selection_area = MagicMock() self.scene.multi_select_item.bring_to_front = MagicMock() - self.scene.get_selection_rect = MagicMock( + self.scene.itemsBoundingRect = MagicMock( return_value=QtCore.QRectF(0, 0, 100, 80)) self.scene.removeItem = MagicMock() @@ -471,7 +489,6 @@ class BeeGraphicsSceneTestCase(BeeTestCase): 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) @@ -480,7 +497,6 @@ class BeeGraphicsSceneTestCase(BeeTestCase): 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) @@ -489,7 +505,6 @@ class BeeGraphicsSceneTestCase(BeeTestCase): 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) @@ -497,7 +512,6 @@ class BeeGraphicsSceneTestCase(BeeTestCase): 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)