Fix normalize for transformed items

This commit is contained in:
Rebecca Breu 2021-04-18 19:05:00 +02:00
parent 94117b82c1
commit e8e7a0fc1e
4 changed files with 155 additions and 38 deletions

View file

@ -137,7 +137,7 @@ class NormalizeItems(QtGui.QUndoCommand):
self.old_scale_factors = []
for item, factor in zip(self.items, self.scale_factors):
self.old_scale_factors.append(item.scale())
item.setScale(factor,
item.setScale(item.scale() * factor,
QtCore.QPointF(item.width, item.height) / 2)
def undo(self):

View file

@ -48,7 +48,10 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
:param mode: "width" or "height".
"""
values = [getattr(i, mode) for i in self.selectedItems(user_only=True)]
values = []
for item in self.selectedItems(user_only=True):
rect = self.itemsBoundingRect(items=[item])
values.append(getattr(rect, mode)())
if not values:
return
avg = sum(values) / len(values)
@ -57,7 +60,8 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
scale_factors = []
for item in self.selectedItems(user_only=True):
scale_factors.append(avg / getattr(item, mode))
rect = self.itemsBoundingRect(items=[item])
scale_factors.append(avg / getattr(rect, mode)())
self.undo_stack.push(
commands.NormalizeItems(
self.selectedItems(user_only=True), scale_factors))
@ -75,8 +79,11 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
Size meaning the area = widh * height.
"""
sizes = [i.width * i.height
for i in self.selectedItems(user_only=True)]
sizes = []
for item in self.selectedItems(user_only=True):
rect = self.itemsBoundingRect(items=[item])
sizes.append(rect.width() * rect.height())
if not sizes:
return
@ -86,7 +93,8 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
scale_factors = []
for item in self.selectedItems(user_only=True):
scale_factors.append(math.sqrt(avg / item.width / item.height))
rect = self.itemsBoundingRect(items=[item])
scale_factors.append(math.sqrt(avg / rect.width() / rect.height()))
self.undo_stack.push(
commands.NormalizeItems(
self.selectedItems(user_only=True), scale_factors))
@ -140,7 +148,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
if not item.isSelected():
item.setSelected(True)
self.views()[0].fit_rect(
self.itemsBoundingRect(selection_only=True),
self.itemsBoundingRect(items=[item]),
toggle_item=item)
return
super().mouseDoubleClickEvent(event)
@ -202,23 +210,30 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
for item in self.selectedItems():
item.on_view_scale_change()
def itemsBoundingRect(self, selection_only=False):
def itemsBoundingRect(self, selection_only=False, items=None):
"""Returns the bounding rect of the scene's items; either all of them
or only selected ones.
or only selected ones, or the items givin in ``items``.
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))
def filter_user_items(ilist):
return list(filter(lambda i: hasattr(i, 'save_id'), ilist))
if not items:
if selection_only:
base = filter_user_items(self.selectedItems())
elif items:
base = items
else:
base = filter_user_items(self.items())
if not base:
return QtCore.QRectF(0, 0, 0, 0)
x = []
y = []
for item in items:
for item in base:
for corner in item.corners_scene_coords:
x.append(corner.x())
y.append(corner.y())

View file

@ -229,8 +229,8 @@ class NormalizeItemsTestCase(BeeTestCase):
command.redo()
assert item1.scale() == 2
assert item1.pos() == QtCore.QPointF(-50, -40)
assert item2.scale() == 0.5
assert item2.pos() == QtCore.QPointF(125, 100)
assert item2.scale() == 1.5
assert item2.pos() == QtCore.QPointF(75, 60)
command.undo()
assert item1.scale() == 1
assert item1.pos() == QtCore.QPointF(0, 0)

View file

@ -24,45 +24,122 @@ class BeeGraphicsSceneTestCase(BeeTestCase):
self.addCleanup(views_patcher.stop)
def test_normalize_height(self):
item1 = MagicMock(width=200, height=100, scale_factor=1)
item2 = MagicMock(width=300, height=200, scale_factor=3)
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(True)
item2.setScale(3)
with patch.object(self.scene, 'selectedItems',
return_value=[item1, item2]):
self.scene.normalize_height()
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=80):
self.scene.normalize_height()
item1.setScale.assert_called_once_with(1.5, QtCore.QPointF(100, 50))
item2.setScale.assert_called_once_with(0.75, QtCore.QPointF(150, 100))
assert item1.scale() == 2
assert item1.pos() == QtCore.QPointF(-50, -40)
assert item2.scale() == 2
assert item2.pos() == QtCore.QPointF(50, 40)
def test_normalize_height_with_rotation(self):
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(True)
item2.setRotation(90)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=200):
self.scene.normalize_height()
assert item1.scale() == 0.75
assert item2.scale() == 1.5
def test_normalize_height_when_no_items(self):
self.scene.normalize_height()
def test_normalize_width(self):
item1 = MagicMock(width=100, height=200, scale_factor=1)
item2 = MagicMock(width=200, height=300, scale_factor=3)
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(True)
item2.setScale(3)
with patch.object(self.scene, 'selectedItems',
return_value=[item1, item2]):
self.scene.normalize_width()
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=80):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
self.scene.normalize_width()
item1.setScale.assert_called_once_with(1.5, QtCore.QPointF(50, 100))
item2.setScale.assert_called_once_with(0.75, QtCore.QPointF(100, 150))
assert item1.scale() == 2
assert item1.pos() == QtCore.QPointF(-40, -50)
assert item2.scale() == 2
assert item2.pos() == QtCore.QPointF(40, 50)
def test_normalize_width_with_rotation(self):
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(True)
item2.setRotation(90)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=200):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
self.scene.normalize_height()
assert item1.scale() == 1.5
assert item2.scale() == 0.75
def test_normalize_width_when_no_items(self):
self.scene.normalize_width()
def test_normalize_size(self):
item1 = MagicMock(width=100, height=200, scale_factor=1)
item2 = MagicMock(width=400, height=100, scale_factor=3)
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(True)
item2.setScale(2)
with patch.object(self.scene, 'selectedItems',
return_value=[item1, item2]):
self.scene.normalize_size()
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=100):
self.scene.normalize_size()
item1.setScale.assert_called_once_with(
math.sqrt(1.5), QtCore.QPointF(50, 100))
item2.setScale.assert_called_once_with(
math.sqrt(0.75), QtCore.QPointF(200, 50))
assert item1.scale() == approx(math.sqrt(2.5))
assert item2.scale() == approx(math.sqrt(2.5))
def test_normalize_size_with_rotation(self):
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(True)
item2.setRotation(90)
with patch('beeref.items.BeePixmapItem.width',
new_callable=PropertyMock, return_value=100):
with patch('beeref.items.BeePixmapItem.height',
new_callable=PropertyMock, return_value=200):
self.scene.normalize_size()
assert item1.scale() == 1
assert item2.scale() == 1
def test_normalize_size_when_no_items(self):
self.scene.normalize_size()
@ -459,6 +536,31 @@ class BeeGraphicsSceneTestCase(BeeTestCase):
self.scene.on_view_scale_change()
item.on_view_scale_change.assert_called_once()
def test_items_bounding_rect_given_items(self):
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)
item1.setSelected(True)
item1.setPos(4, -6)
item2 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item2)
item2.setSelected(True)
item2.setPos(-33, 22)
item3 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item3)
item3.setSelected(True)
item3.setPos(1000, 1000)
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.itemsBoundingRect(items=[item1, item2])
assert rect.topLeft().x() == -33
assert rect.topLeft().y() == -6
assert rect.bottomRight().x() == 104
assert rect.bottomRight().y() == 122
def test_items_bounding_rect_two_items_selection_only(self):
item1 = BeePixmapItem(QtGui.QImage())
self.scene.addItem(item1)