mirror of
https://github.com/rbreu/beeref.git
synced 2026-03-11 08:54:28 +00:00
Fix scaling for rotated items
This commit is contained in:
parent
2d3e6613bf
commit
a34231cd7d
4 changed files with 134 additions and 117 deletions
|
|
@ -74,37 +74,31 @@ class MoveItemsBy(QtGui.QUndoCommand):
|
|||
|
||||
|
||||
class ScaleItemsBy(QtGui.QUndoCommand):
|
||||
"""Scale items by a given factor around the given anchor point."""
|
||||
"""Scale items by a given factor around the given anchor."""
|
||||
|
||||
def __init__(self, items, factor, ignore_first_redo=False):
|
||||
def __init__(self, items, factor, anchor, ignore_first_redo=False):
|
||||
super().__init__('Scale items')
|
||||
self.ignore_first_redo = ignore_first_redo
|
||||
self.items = items
|
||||
self.factor = factor
|
||||
self.item_data = [
|
||||
{'anchor': item.scale_anchor,
|
||||
'orig_factor': item.scale_orig_factor,
|
||||
'orig_pos': item.scale_orig_pos} for item in items]
|
||||
self.anchor = anchor
|
||||
|
||||
def redo(self):
|
||||
if self.ignore_first_redo:
|
||||
self.ignore_first_redo = False
|
||||
return
|
||||
for item, data in zip(self.items, self.item_data):
|
||||
item.scale_orig_factors = data['orig_factor']
|
||||
item.scale_orig_pos = data['orig_pos']
|
||||
item.scale_anchor = data['anchor']
|
||||
item.setScale(item.scale() * self.factor)
|
||||
item.translate_for_scale_anchor(self.factor)
|
||||
for item in self.items:
|
||||
item.setScale(item.scale() * self.factor,
|
||||
item.mapFromScene(self.anchor))
|
||||
|
||||
def undo(self):
|
||||
for item, data in zip(self.items, self.item_data):
|
||||
item.setScale(item.scale() / self.factor)
|
||||
item.setPos(data['orig_pos'])
|
||||
for item in self.items:
|
||||
item.setScale(item.scale() / self.factor,
|
||||
item.mapFromScene(self.anchor))
|
||||
|
||||
|
||||
class RotateItemsBy(QtGui.QUndoCommand):
|
||||
"""Rotate items by a given deltan around the given anchor."""
|
||||
"""Rotate items by a given delta around the given anchor."""
|
||||
|
||||
def __init__(self, items, delta, anchor, ignore_first_redo=False):
|
||||
super().__init__('Scale items')
|
||||
|
|
|
|||
|
|
@ -33,15 +33,35 @@ logger = logging.getLogger('BeeRef')
|
|||
SELECT_COLOR = QtGui.QColor(116, 234, 231, 255)
|
||||
|
||||
|
||||
def with_anchor(func):
|
||||
"""Decorator that handles anchors for translate operations.
|
||||
|
||||
The anchor is given in item coordinates.
|
||||
"""
|
||||
|
||||
def wrapper(self, value, anchor=None):
|
||||
# We caculate where the anchor is before and after the transformation
|
||||
# and then move the item accordingly to keep the anchor fixed
|
||||
|
||||
anchor = anchor if anchor else QtCore.QPointF(0, 0)
|
||||
prev = self.mapToScene(anchor)
|
||||
func(self, value, anchor)
|
||||
diff = self.mapToScene(anchor) - prev
|
||||
self.setPos(self.pos() - diff)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class BaseItemMixin:
|
||||
|
||||
def setScale(self, factor):
|
||||
if factor <= 0:
|
||||
@with_anchor
|
||||
def setScale(self, value, anchor=None):
|
||||
if value <= 0:
|
||||
return
|
||||
|
||||
logger.debug(f'Setting scale for {self} to {factor}')
|
||||
logger.debug(f'Setting scale for {self} to {value}')
|
||||
self.prepareGeometryChange()
|
||||
super().setScale(factor)
|
||||
super().setScale(value)
|
||||
|
||||
def setZValue(self, value):
|
||||
logger.debug(f'Setting z-value for {self} to {value}')
|
||||
|
|
@ -51,15 +71,15 @@ class BaseItemMixin:
|
|||
def bring_to_front(self):
|
||||
self.setZValue(self.scene().max_z + 0.001)
|
||||
|
||||
def setRotation(self, rotation, anchor=None):
|
||||
anchor = anchor or QtCore.QPointF(0, 0)
|
||||
prev = self.mapToScene(anchor)
|
||||
super().setRotation(rotation)
|
||||
@with_anchor
|
||||
def setRotation(self, value, anchor):
|
||||
logger.debug(f'Setting rotation for {self} to {value}')
|
||||
super().setRotation(value)
|
||||
|
||||
# Calculate where the anchor moved to so that we can shift the
|
||||
# item back to keep the anchor fixed
|
||||
diff = self.mapToScene(anchor) - prev
|
||||
self.setPos(self.pos() - diff)
|
||||
@property
|
||||
def center_scene_coords(self):
|
||||
"""The item's center in scene coordinates."""
|
||||
return self.mapToScene(QtCore.QPointF(self.width/2, self.height/2))
|
||||
|
||||
|
||||
class SelectableMixin(BaseItemMixin):
|
||||
|
|
@ -237,22 +257,22 @@ class SelectableMixin(BaseItemMixin):
|
|||
# Start scale action for this corner
|
||||
self.scale_active = True
|
||||
self.event_start = event.scenePos()
|
||||
self.event_direction = self.get_mouse_event_direction(
|
||||
event)
|
||||
self.event_anchor = self.mapToScene(
|
||||
self.get_scale_anchor(corner))
|
||||
self.scale_direction = self.get_corner_direction(corner)
|
||||
for item in self.selection_action_items():
|
||||
item.scale_anchor = self.get_scale_anchor(item, corner)
|
||||
item.scale_orig_factor = item.scale()
|
||||
item.scale_orig_pos = item.pos()
|
||||
event.accept()
|
||||
return
|
||||
# Check if we are in one of the corner's rotate areas
|
||||
if self.get_rotate_bounds(corner).contains(event.pos()):
|
||||
# Start rotate action
|
||||
self.rotate_active = True
|
||||
self.rotate_anchor = self.mapToScene(
|
||||
QtCore.QPointF(self.width/2, self.height/2))
|
||||
self.event_anchor = self.center_scene_coords
|
||||
self.rotate_start_angle = self.get_rotate_angle(event)
|
||||
for item in self.selection_action_items():
|
||||
item.rotate_anchor = self.rotate_anchor
|
||||
item.rotate_orig_degrees = item.rotation()
|
||||
event.accept()
|
||||
return
|
||||
|
|
@ -261,16 +281,16 @@ class SelectableMixin(BaseItemMixin):
|
|||
|
||||
def get_scale_factor(self, event):
|
||||
"""Get the scale factor for the current mouse movement."""
|
||||
imgsize = self.width + self.height
|
||||
imgsize = math.sqrt(self.width**2 + self.height**2)
|
||||
p = event.scenePos() - self.event_start
|
||||
direction = self.scale_direction
|
||||
direction = self.event_direction
|
||||
delta = QtCore.QPointF.dotProduct(direction, p) / imgsize
|
||||
return (self.scale_orig_factor + delta) / self.scale_orig_factor
|
||||
|
||||
def get_scale_anchor(self, item, corner):
|
||||
def get_scale_anchor(self, corner):
|
||||
"""Get the anchor around which the scale for this corner operates."""
|
||||
return item.mapFromScene(
|
||||
self.mapToScene(self.width - corner.x(), self.height - corner.y()))
|
||||
return QtCore.QPointF(self.width - corner.x(),
|
||||
self.height - corner.y())
|
||||
|
||||
def get_corner_direction(self, corner):
|
||||
"""Get the direction facing away from the center, e.g. the direction
|
||||
|
|
@ -278,24 +298,18 @@ class SelectableMixin(BaseItemMixin):
|
|||
return QtCore.QPointF(1 if corner.x() > 0 else -1,
|
||||
1 if corner.y() > 0 else -1)
|
||||
|
||||
def translate_for_scale_anchor(self, scale_factor):
|
||||
"""Adjust the item's position so that a scale with the given scale
|
||||
factor appears to operate around the scale anchor. ``setScale``
|
||||
needs to be called separately with ``scale_factor`` multiplied by
|
||||
the item's current scale factor.
|
||||
def get_mouse_event_direction(self, event):
|
||||
"""The direction of a mouse event in relation to the item's center.
|
||||
"""
|
||||
|
||||
factor = self.scale_orig_factor * (scale_factor - 1)
|
||||
self.setPos(
|
||||
self.scale_orig_pos.x() - self.scale_anchor.x() * factor,
|
||||
self.scale_orig_pos.y() - self.scale_anchor.y() * factor,
|
||||
)
|
||||
diff = event.scenePos() - self.center_scene_coords
|
||||
length = math.sqrt(QtCore.QPointF.dotProduct(diff, diff))
|
||||
return diff / length
|
||||
|
||||
def get_rotate_angle(self, event):
|
||||
"""Get the angle of the current mouse position towards the
|
||||
scale center."""
|
||||
|
||||
diff = event.scenePos() - self.rotate_anchor
|
||||
diff = event.scenePos() - self.event_anchor
|
||||
return -math.degrees(math.atan2(diff.x(), diff.y()))
|
||||
|
||||
def get_rotate_delta(self, event):
|
||||
|
|
@ -306,14 +320,14 @@ class SelectableMixin(BaseItemMixin):
|
|||
if self.scale_active:
|
||||
factor = self.get_scale_factor(event)
|
||||
for item in self.selection_action_items():
|
||||
item.setScale(item.scale_orig_factor * factor)
|
||||
item.translate_for_scale_anchor(factor)
|
||||
item.setScale(item.scale_orig_factor * factor,
|
||||
item.mapFromScene(self.event_anchor))
|
||||
event.accept()
|
||||
elif self.rotate_active:
|
||||
delta = self.get_rotate_delta(event)
|
||||
for item in self.selection_action_items():
|
||||
item.setRotation(item.rotate_orig_degrees + delta,
|
||||
item.mapFromScene(self.rotate_anchor))
|
||||
item.mapFromScene(self.event_anchor))
|
||||
else:
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
|
|
@ -323,6 +337,7 @@ class SelectableMixin(BaseItemMixin):
|
|||
commands.ScaleItemsBy(
|
||||
self.selection_action_items(),
|
||||
self.get_scale_factor(event),
|
||||
self.event_anchor,
|
||||
ignore_first_redo=True))
|
||||
self.scale_active = False
|
||||
event.accept()
|
||||
|
|
@ -332,7 +347,7 @@ class SelectableMixin(BaseItemMixin):
|
|||
commands.RotateItemsBy(
|
||||
self.selection_action_items(),
|
||||
self.get_rotate_delta(event),
|
||||
self.rotate_anchor,
|
||||
self.event_anchor,
|
||||
ignore_first_redo=True))
|
||||
self.rotate_active = False
|
||||
event.accept()
|
||||
|
|
|
|||
|
|
@ -95,16 +95,11 @@ class ScaleItemsByTestCase(BeeTestCase):
|
|||
def test_redo_undo(self):
|
||||
item1 = BeePixmapItem(QtGui.QImage())
|
||||
item1.setScale(1)
|
||||
item1.scale_orig_factor = 1
|
||||
item1.scale_anchor = QtCore.QPointF(100, 100)
|
||||
item1.scale_orig_pos = QtCore.QPointF(0, 0)
|
||||
item2 = BeePixmapItem(QtGui.QImage())
|
||||
item2.setScale(3)
|
||||
item2.scale_orig_factor = 3
|
||||
item2.scale_anchor = QtCore.QPointF(0, 0)
|
||||
item2.setPos(100, 100)
|
||||
item2.scale_orig_pos = QtCore.QPointF(100, 100)
|
||||
command = commands.ScaleItemsBy([item1, item2], 2)
|
||||
command = commands.ScaleItemsBy([item1, item2], 2,
|
||||
QtCore.QPointF(100, 100))
|
||||
command.redo()
|
||||
assert item1.scale() == 2
|
||||
assert item1.pos().x() == -100
|
||||
|
|
@ -123,22 +118,17 @@ class ScaleItemsByTestCase(BeeTestCase):
|
|||
def test_ignore_first_redo(self):
|
||||
item1 = BeePixmapItem(QtGui.QImage())
|
||||
item1.setScale(1)
|
||||
item1.scale_orig_factor = 1
|
||||
item1.scale_anchor = QtCore.QPointF(100, 100)
|
||||
item1.scale_orig_pos = QtCore.QPointF(0, 0)
|
||||
item2 = BeePixmapItem(QtGui.QImage())
|
||||
item2.setScale(3)
|
||||
item2.scale_orig_factor = 3
|
||||
item2.scale_anchor = QtCore.QPointF(0, 0)
|
||||
item2.setPos(100, 100)
|
||||
item2.scale_orig_pos = QtCore.QPointF(100, 100)
|
||||
command = commands.ScaleItemsBy([item1, item2], 2,
|
||||
QtCore.QPointF(100, 100),
|
||||
ignore_first_redo=True)
|
||||
command.redo()
|
||||
assert item1.scale() == 1
|
||||
assert item2.scale() == 3
|
||||
assert item1.pos().x() == 0
|
||||
assert item1.pos().y() == 0
|
||||
assert item2.scale() == 3
|
||||
assert item2.pos().x() == 100
|
||||
assert item2.pos().y() == 100
|
||||
command.redo()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import math
|
||||
from pytest import approx
|
||||
from unittest.mock import patch, MagicMock, PropertyMock
|
||||
|
||||
from PyQt6 import QtCore, QtGui
|
||||
|
|
@ -35,6 +37,12 @@ class BaseItemMixinTestCase(BeeTestCase):
|
|||
item.setScale(-0.1)
|
||||
assert item.scale() == 1
|
||||
|
||||
def test_set_scale_with_anchor(self):
|
||||
item = BeePixmapItem(QtGui.QImage())
|
||||
item.setScale(2, anchor=QtCore.QPointF(100, 100))
|
||||
assert item.scale() == 2
|
||||
assert item.pos() == QtCore.QPointF(-100, -100)
|
||||
|
||||
def test_set_zvalue_sets_new_max(self):
|
||||
item = BeePixmapItem(QtGui.QImage())
|
||||
self.scene.addItem(item)
|
||||
|
|
@ -60,7 +68,7 @@ class BaseItemMixinTestCase(BeeTestCase):
|
|||
assert item2.zValue() > item1.zValue()
|
||||
assert item2.zValue() == self.scene.max_z
|
||||
|
||||
def test_set_rotation_anchor_topleft(self):
|
||||
def test_set_rotation_anchor(self):
|
||||
item = BeePixmapItem(QtGui.QImage())
|
||||
item.setRotation(45)
|
||||
assert item.rotation() == 45
|
||||
|
|
@ -81,6 +89,17 @@ class BaseItemMixinTestCase(BeeTestCase):
|
|||
assert item.pos().x() == 0
|
||||
assert item.pos().y() == 200
|
||||
|
||||
def test_center_scene_coords(self):
|
||||
item = BeePixmapItem(QtGui.QImage())
|
||||
self.scene.addItem(item)
|
||||
item.setPos(5, 5)
|
||||
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):
|
||||
assert item.center_scene_coords == QtCore.QPointF(105, 85)
|
||||
|
||||
|
||||
class SelectableMixinBaseTestCase(BeeTestCase):
|
||||
|
||||
|
|
@ -329,47 +348,40 @@ class SelectableMixinScalingTestCase(SelectableMixinBaseTestCase):
|
|||
|
||||
def test_get_scale_factor_bottomright(self):
|
||||
self.item.event_start = QtCore.QPointF(10, 10)
|
||||
self.item.scale_direction = QtCore.QPointF(1, 1)
|
||||
self.item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
|
||||
self.item.scale_orig_factor = 1
|
||||
event = MagicMock()
|
||||
event.scenePos = MagicMock(return_value=QtCore.QPointF(20, 90))
|
||||
assert self.item.get_scale_factor(event) == 1.5
|
||||
assert self.item.get_scale_factor(event) == approx(1.5, 0.01)
|
||||
|
||||
def test_get_scale_factor_topleft(self):
|
||||
self.item.event_start = QtCore.QPointF(10, 10)
|
||||
self.item.scale_direction = QtCore.QPointF(-1, -1)
|
||||
self.item.event_direction = QtCore.QPointF(-1, -1) / math.sqrt(2)
|
||||
self.item.scale_orig_factor = 0.5
|
||||
event = MagicMock()
|
||||
event.scenePos = MagicMock(return_value=QtCore.QPointF(-10, -60))
|
||||
assert self.item.get_scale_factor(event) == 2
|
||||
assert self.item.get_scale_factor(event) == approx(2, 0.01)
|
||||
|
||||
def test_get_scale_anchor_topleft(self):
|
||||
anchor = self.item.get_scale_anchor(self.item, QtCore.QPointF(0, 0))
|
||||
anchor = self.item.get_scale_anchor(QtCore.QPointF(0, 0))
|
||||
assert anchor.x() == 100
|
||||
assert anchor.y() == 80
|
||||
|
||||
def test_get_scale_anchor_bottomright(self):
|
||||
anchor = self.item.get_scale_anchor(self.item, QtCore.QPointF(100, 80))
|
||||
anchor = self.item.get_scale_anchor(QtCore.QPointF(100, 80))
|
||||
assert anchor.x() == 0
|
||||
assert anchor.y() == 0
|
||||
|
||||
def test_get_scale_anchor_topright(self):
|
||||
anchor = self.item.get_scale_anchor(self.item, QtCore.QPointF(100, 0))
|
||||
anchor = self.item.get_scale_anchor(QtCore.QPointF(100, 0))
|
||||
assert anchor.x() == 0
|
||||
assert anchor.y() == 80
|
||||
|
||||
def test_get_scale_anchor_bottomleft(self):
|
||||
anchor = self.item.get_scale_anchor(self.item, QtCore.QPointF(0, 80))
|
||||
anchor = self.item.get_scale_anchor(QtCore.QPointF(0, 80))
|
||||
assert anchor.x() == 100
|
||||
assert anchor.y() == 0
|
||||
|
||||
def test_get_scale_anchor_topleft_when_anchor_other_item(self):
|
||||
item2 = BeePixmapItem(QtGui.QImage())
|
||||
item2.setPos(22, 33)
|
||||
anchor = self.item.get_scale_anchor(item2, QtCore.QPointF(0, 0))
|
||||
assert anchor.x() == 78
|
||||
assert anchor.y() == 47
|
||||
|
||||
def test_get_corner_direction_topleft(self):
|
||||
assert self.item.get_corner_direction(
|
||||
QtCore.QPointF(0, 0)) == QtCore.QPointF(-1, -1)
|
||||
|
|
@ -386,22 +398,33 @@ class SelectableMixinScalingTestCase(SelectableMixinBaseTestCase):
|
|||
assert self.item.get_corner_direction(
|
||||
QtCore.QPointF(0, 80)) == QtCore.QPointF(-1, 1)
|
||||
|
||||
def test_translate_for_scale_anchor(self):
|
||||
self.item.scale_orig_pos = QtCore.QPoint(50, 70)
|
||||
self.item.scale_anchor = QtCore.QPoint(100, 80)
|
||||
self.item.scale_orig_factor = 1
|
||||
self.item.translate_for_scale_anchor(3)
|
||||
assert self.item.pos().x() == -150
|
||||
assert self.item.pos().y() == -90
|
||||
def test_get_mouse_event_direction_bottomright(self):
|
||||
event = MagicMock()
|
||||
event.scenePos = MagicMock(return_value=QtCore.QPointF(100, 90))
|
||||
direction = self.item.get_mouse_event_direction(event)
|
||||
assert direction == approx(QtCore.QPointF(1, 1) / math.sqrt(2))
|
||||
|
||||
def test_get_mouse_event_direction_topleft(self):
|
||||
event = MagicMock()
|
||||
event.scenePos = MagicMock(return_value=QtCore.QPointF(0, -10))
|
||||
direction = self.item.get_mouse_event_direction(event)
|
||||
assert direction == approx(QtCore.QPointF(-1, -1) / math.sqrt(2))
|
||||
|
||||
def test_get_mouse_event_direction_bottomright_when_rotated_180(self):
|
||||
self.item.setRotation(180, QtCore.QPointF(50, 40))
|
||||
event = MagicMock()
|
||||
event.scenePos = MagicMock(return_value=QtCore.QPointF(100, 90))
|
||||
direction = self.item.get_mouse_event_direction(event)
|
||||
assert direction == approx(QtCore.QPointF(1, 1) / math.sqrt(2))
|
||||
|
||||
def test_get_rotate_angle(self):
|
||||
self.item.rotate_anchor = QtCore.QPointF(10, 20)
|
||||
self.item.event_anchor = QtCore.QPointF(10, 20)
|
||||
event = MagicMock()
|
||||
event.scenePos = MagicMock(return_value=QtCore.QPointF(15, 25))
|
||||
assert self.item.get_rotate_angle(event) == -45
|
||||
|
||||
def test_get_rotate_delta(self):
|
||||
self.item.rotate_anchor = QtCore.QPointF(10, 20)
|
||||
self.item.event_anchor = QtCore.QPointF(10, 20)
|
||||
self.item.rotate_start_angle = -3
|
||||
event = MagicMock()
|
||||
event.scenePos = MagicMock(return_value=QtCore.QPointF(15, 25))
|
||||
|
|
@ -470,28 +493,28 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase):
|
|||
def test_mouse_press_event_topleft_scale(self):
|
||||
self.item.setSelected(True)
|
||||
self.event.pos = MagicMock(return_value=QtCore.QPointF(2, 2))
|
||||
self.event.scenePos = MagicMock(return_value=QtCore.QPointF(66, 99))
|
||||
self.event.scenePos = MagicMock(return_value=QtCore.QPointF(-1, -1))
|
||||
self.event.button = MagicMock(
|
||||
return_value=Qt.MouseButtons.LeftButton)
|
||||
self.item.mousePressEvent(self.event)
|
||||
assert self.item.scale_active is True
|
||||
assert self.item.event_start == QtCore.QPointF(66, 99)
|
||||
assert self.item.scale_direction == QtCore.QPointF(-1, -1)
|
||||
assert self.item.event_start == QtCore.QPointF(-1, -1)
|
||||
assert self.item.event_direction.x() < 0
|
||||
assert self.item.event_direction.y() < 0
|
||||
assert self.item.scale_orig_factor == 1
|
||||
assert self.item.scale_orig_pos == QtCore.QPointF(0, 0)
|
||||
|
||||
def test_mouse_press_event_bottomright_scale(self):
|
||||
self.item.setSelected(True)
|
||||
self.event.pos = MagicMock(return_value=QtCore.QPointF(99, 79))
|
||||
self.event.scenePos = MagicMock(return_value=QtCore.QPointF(66, 99))
|
||||
self.event.scenePos = MagicMock(return_value=QtCore.QPointF(101, 81))
|
||||
self.event.button = MagicMock(
|
||||
return_value=Qt.MouseButtons.LeftButton)
|
||||
self.item.mousePressEvent(self.event)
|
||||
assert self.item.scale_active is True
|
||||
assert self.item.scale_direction == QtCore.QPointF(1, 1)
|
||||
assert self.item.event_start == QtCore.QPointF(66, 99)
|
||||
assert self.item.event_start == QtCore.QPointF(101, 81)
|
||||
assert self.item.event_direction.x() > 0
|
||||
assert self.item.event_direction.y() > 0
|
||||
assert self.item.scale_orig_factor == 1
|
||||
assert self.item.scale_orig_pos == QtCore.QPointF(0, 0)
|
||||
|
||||
def test_mouse_press_event_rotate(self):
|
||||
self.item.setSelected(True)
|
||||
|
|
@ -501,7 +524,7 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase):
|
|||
return_value=Qt.MouseButtons.LeftButton)
|
||||
self.item.mousePressEvent(self.event)
|
||||
assert self.item.rotate_active is True
|
||||
assert self.item.rotate_anchor == QtCore.QPointF(50, 40)
|
||||
assert self.item.event_anchor == QtCore.QPointF(50, 40)
|
||||
assert self.item.rotate_orig_degrees == 0
|
||||
|
||||
def test_mouse_press_event_not_selected(self):
|
||||
|
|
@ -511,7 +534,7 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase):
|
|||
m.assert_called_once_with(self.event)
|
||||
assert self.item.scale_active is False
|
||||
|
||||
def test_mouse_press_not_in_handles(self):
|
||||
def test_mouse_press_event_not_in_handles(self):
|
||||
self.item.setSelected(True)
|
||||
self.event.pos = MagicMock(return_value=QtCore.QPointF(50, 40))
|
||||
self.event.button = MagicMock(
|
||||
|
|
@ -529,21 +552,20 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase):
|
|||
def test_mouse_move_event_when_scale_action(self):
|
||||
self.event.scenePos = MagicMock(return_value=QtCore.QPointF(20, 90))
|
||||
self.item.scale_active = True
|
||||
self.item.scale_direction = QtCore.QPointF(1, 1)
|
||||
self.item.scale_anchor = QtCore.QPointF(100, 80)
|
||||
self.item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
|
||||
self.item.event_anchor = QtCore.QPointF(100, 80)
|
||||
self.item.event_start = QtCore.QPointF(10, 10)
|
||||
self.item.scale_orig_factor = 1
|
||||
self.item.scale_orig_pos = QtCore.QPointF(0, 0)
|
||||
|
||||
self.item.mouseMoveEvent(self.event)
|
||||
assert self.item.scale() == 1.5
|
||||
assert self.item.scale() == approx(1.5, 0.01)
|
||||
|
||||
def test_mouse_move_event_when_rotate_action(self):
|
||||
self.event.scenePos = MagicMock(return_value=QtCore.QPointF(15, 25))
|
||||
self.item.rotate_active = True
|
||||
self.item.rotate_orig_degrees = 0
|
||||
self.item.rotate_start_angle = -3
|
||||
self.item.rotate_anchor = QtCore.QPointF(10, 20)
|
||||
self.item.event_anchor = QtCore.QPointF(10, 20)
|
||||
self.item.mouseMoveEvent(self.event)
|
||||
assert self.item.rotation() == -42
|
||||
|
||||
|
|
@ -556,11 +578,10 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase):
|
|||
def test_mouse_release_event_when_scale_action(self):
|
||||
self.event.scenePos = MagicMock(return_value=QtCore.QPointF(20, 90))
|
||||
self.item.scale_active = True
|
||||
self.item.scale_direction = QtCore.QPointF(1, 1)
|
||||
self.item.scale_anchor = QtCore.QPointF(100, 80)
|
||||
self.item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
|
||||
self.item.event_anchor = QtCore.QPointF(100, 80)
|
||||
self.item.event_start = QtCore.QPointF(10, 10)
|
||||
self.item.scale_orig_factor = 1
|
||||
self.item.scale_orig_pos = QtCore.QPointF(0, 0)
|
||||
self.scene.undo_stack = MagicMock(push=MagicMock())
|
||||
|
||||
self.item.mouseReleaseEvent(self.event)
|
||||
|
|
@ -568,11 +589,8 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase):
|
|||
args = self.scene.undo_stack.push.call_args_list[0][0]
|
||||
cmd = args[0]
|
||||
assert cmd.items == [self.item]
|
||||
assert cmd.factor == 1.5
|
||||
assert cmd.item_data == [{
|
||||
'anchor': QtCore.QPointF(100, 80),
|
||||
'orig_factor': 1,
|
||||
'orig_pos': QtCore.QPointF(0, 0)}]
|
||||
assert cmd.factor == approx(1.5, 0.01)
|
||||
assert cmd.anchor == QtCore.QPointF(100, 80)
|
||||
assert cmd.ignore_first_redo is True
|
||||
assert self.item.scale_active is False
|
||||
|
||||
|
|
@ -581,7 +599,7 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase):
|
|||
self.item.rotate_active = True
|
||||
self.item.rotate_orig_degrees = 0
|
||||
self.item.rotate_start_angle = -3
|
||||
self.item.rotate_anchor = QtCore.QPointF(10, 20)
|
||||
self.item.event_anchor = QtCore.QPointF(10, 20)
|
||||
self.scene.undo_stack = MagicMock(push=MagicMock())
|
||||
|
||||
self.item.mouseReleaseEvent(self.event)
|
||||
|
|
|
|||
Loading…
Reference in a new issue