beeref/tests/selection/test_selectable_mixin.py

1087 lines
41 KiB
Python

import math
from pytest import approx, mark
from unittest.mock import patch, MagicMock
from PyQt6 import QtCore, QtGui
from PyQt6.QtCore import Qt
from beeref.assets import BeeAssets
from beeref import commands
from beeref.items import BeePixmapItem
def test_init_selectable(view):
item = BeePixmapItem(QtGui.QImage())
assert item.viewport_scale == 1
assert item.active_mode is None
def test_on_view_scale_change(view, item):
with patch('beeref.items.BeePixmapItem.prepareGeometryChange') as m:
item.on_view_scale_change()
m.assert_called_once()
def test_fixed_length_for_viewport_when_default_scale(view, item):
view.scene.addItem(item)
assert item.fixed_length_for_viewport(100) == 100
assert item._view_scale == 1
def test_fixed_length_for_viewport_when_viewport_scaled(view, item):
view.scene.addItem(item)
view.scale(2, 2)
assert item.fixed_length_for_viewport(100) == 50
assert item._view_scale == 2
def test_fixed_length_for_viewport_when_item_scaled(view, item):
view.scene.addItem(item)
item.setScale(5)
assert item.fixed_length_for_viewport(100) == 20
assert item._view_scale == 1
def test_fixed_length_for_viewport_when_no_scene(view, item):
item._view_scale = 0.5
assert item.fixed_length_for_viewport(100) == 200
def test_resize_size_when_scaled(view, item):
view.scene.addItem(item)
view.scale(2, 2)
item.setScale(2)
item.SELECT_RESIZE_SIZE = 100
assert item.select_resize_size == 25
def test_rotate_size_when_scaled(view, item):
view.scene.addItem(item)
view.scale(2, 2)
item.setScale(2)
item.SELECT_ROTATE_SIZE = 100
assert item.select_rotate_size == 25
def test_select_handle_free_center(view, item):
view.scene.addItem(item)
view.scale(0.5, 0.5)
item.SELECT_FREE_CENTER = 10
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
assert item.select_handle_free_center() == QtCore.QRectF(
40, 30, 20, 20)
def test_draw_debug_shape_rect(view, item):
view.scene.addItem(item)
painter = MagicMock(
combinedTransform=MagicMock(
return_value=MagicMock(
m11=MagicMock(return_value=0.5))))
item.draw_debug_shape(
painter,
QtCore.QRectF(5, 6, 20, 30),
255, 0, 0)
painter.fillRect.assert_called_once()
painter.fillPath.assert_not_called()
def test_draw_debug_shape_path(view, item):
view.scene.addItem(item)
painter = MagicMock(
combinedTransform=MagicMock(
return_value=MagicMock(
m11=MagicMock(return_value=0.5))))
path = QtGui.QPainterPath()
path.addRect(QtCore.QRectF(5, 6, 20, 30))
item.draw_debug_shape(
painter,
path,
0, 255, 0)
painter.fillPath.assert_called_once()
painter.fillRect.assert_not_called()
@patch('beeref.items.BeePixmapItem.draw_debug_shape')
def test_paint_when_not_selected(debug_mock, view, item):
view.scene.addItem(item)
painter = MagicMock(
combinedTransform=MagicMock(
return_value=MagicMock(
m11=MagicMock(return_value=0.5))))
item.setSelected(False)
item.paint(painter, None, None)
painter.drawPixmap.assert_called_once()
painter.drawRect.assert_not_called()
painter.drawPoint.assert_not_called()
debug_mock.assert_not_called()
def test_paint_when_selected_single_selection(view, item):
view.scene.addItem(item)
painter = MagicMock(
combinedTransform=MagicMock(
return_value=MagicMock(
m11=MagicMock(return_value=0.5))))
item.setSelected(True)
item.paint(painter, None, None)
painter.drawPixmap.assert_called_once()
painter.drawRect.assert_called_once()
assert painter.drawPoint.call_count == 4
def test_paint_when_selected_multi_selection(view, item):
view.scene.addItem(item)
item2 = BeePixmapItem(QtGui.QImage())
item2.setSelected(True)
view.scene.addItem(item2)
painter = MagicMock(
combinedTransform=MagicMock(
return_value=MagicMock(
m11=MagicMock(return_value=0.5))))
item.setSelected(True)
item.paint(painter, None, None)
painter.drawPixmap.assert_called_once()
painter.drawRect.assert_called_once()
painter.drawPoint.assert_not_called()
def test_paint_when_debug_shapes(view):
with patch('beeref.selection.commandline_args') as args_mock:
with patch('beeref.items.BeePixmapItem.draw_debug_shape') as m:
args_mock.debug_shapes = True
args_mock.debug_boundingrects = False
args_mock.debug_handles = False
item = BeePixmapItem(QtGui.QImage())
painter = MagicMock(
combinedTransform=MagicMock(
return_value=MagicMock(
m11=MagicMock(return_value=0.5))))
item.paint(painter, None, None)
m.assert_called_once()
def test_paint_when_debug_boundingrects(view):
with patch('beeref.selection.commandline_args') as args_mock:
with patch('beeref.items.BeePixmapItem.draw_debug_shape') as m:
args_mock.debug_shapes = False
args_mock.debug_boundingrects = True
args_mock.debug_handles = False
item = BeePixmapItem(QtGui.QImage())
painter = MagicMock(
combinedTransform=MagicMock(
return_value=MagicMock(
m11=MagicMock(return_value=0.5))))
item.paint(painter, None, None)
m.assert_called_once()
def test_paint_when_debug_handles(view):
with patch('beeref.selection.commandline_args') as args_mock:
with patch('beeref.items.BeePixmapItem.draw_debug_shape') as m:
args_mock.debug_shapes = False
args_mock.debug_boundingrects = False
args_mock.debug_handles = True
item = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item)
item.setSelected(True)
painter = MagicMock(
combinedTransform=MagicMock(
return_value=MagicMock(
m11=MagicMock(return_value=0.5))))
item.paint(painter, None, None)
m.assert_called()
def test_corners(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
assert len(item.corners) == 4
assert QtCore.QPointF(0, 0) in item.corners
assert QtCore.QPointF(100, 0) in item.corners
assert QtCore.QPointF(0, 80) in item.corners
assert QtCore.QPointF(100, 80) in item.corners
def test_corners_scene_coords_translated(view, item):
item.setPos(5, 5)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
corners = item.corners_scene_coords
assert len(corners) == 4
assert QtCore.QPointF(5, 5) in corners
assert QtCore.QPointF(105, 5) in corners
assert QtCore.QPointF(5, 85) in corners
assert QtCore.QPointF(105, 85) in corners
def test_corners_scene_coords_scaled(view, item):
item.setScale(2)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
corners = item.corners_scene_coords
assert len(corners) == 4
assert QtCore.QPointF(0, 0) in corners
assert QtCore.QPointF(200, 0) in corners
assert QtCore.QPointF(0, 160) in corners
assert QtCore.QPointF(200, 160) in corners
def test_get_scale_bounds(view, item):
item.SELECT_RESIZE_SIZE = 10
rect = item.get_scale_bounds(
QtCore.QPointF(100, 80)).boundingRect()
assert rect.topLeft().x() == 95
assert rect.topLeft().y() == 75
assert rect.bottomRight().x() == 105
assert rect.bottomRight().y() == 85
def test_get_scale_bounds_with_margin(view, item):
item.SELECT_RESIZE_SIZE = 10
rect = item.get_scale_bounds(
QtCore.QPointF(100, 80), margin=1).boundingRect()
assert rect.topLeft().x() == 94
assert rect.topLeft().y() == 74
assert rect.bottomRight().x() == 106
assert rect.bottomRight().y() == 86
def test_rotate_bounds_bottomright(view, item):
item.SELECT_RESIZE_SIZE = 10
item.SELECT_ROTATE_SIZE = 10
path = item.get_rotate_bounds(QtCore.QPointF(100, 80))
assert path.boundingRect().topLeft().x() == 95
assert path.boundingRect().topLeft().y() == 75
assert path.boundingRect().bottomRight().x() == 115
assert path.boundingRect().bottomRight().y() == 95
assert path.contains(QtCore.QPointF(104, 84)) is False
def test_rotate_bounds_topleft(view, item):
item.SELECT_RESIZE_SIZE = 10
item.SELECT_ROTATE_SIZE = 10
path = item.get_rotate_bounds(QtCore.QPointF(0, 0))
assert path.boundingRect().topLeft().x() == -15
assert path.boundingRect().topLeft().y() == -15
assert path.boundingRect().bottomRight().x() == 5
assert path.boundingRect().bottomRight().y() == 5
assert path.contains(QtCore.QPointF(-4, -4)) is False
def test_get_flip_bounds(view, item):
item.SELECT_RESIZE_SIZE = 10
item.SELECT_ROTATE_SIZE = 10
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
edges = item.get_flip_bounds()
assert edges[0]['rect'].topLeft() == QtCore.QPointF(5, -5)
assert edges[0]['rect'].bottomRight() == QtCore.QPointF(95, 5)
assert edges[0]['flip_v'] is True
assert edges[1]['rect'].topLeft() == QtCore.QPointF(5, 75)
assert edges[1]['rect'].bottomRight() == QtCore.QPointF(95, 85)
assert edges[1]['flip_v'] is True
assert edges[2]['rect'].topLeft() == QtCore.QPointF(-5, 5)
assert edges[2]['rect'].bottomRight() == QtCore.QPointF(5, 75)
assert edges[2]['flip_v'] is False
assert edges[3]['rect'].topLeft() == QtCore.QPointF(95, 5)
assert edges[3]['rect'].bottomRight() == QtCore.QPointF(105, 75)
assert edges[3]['flip_v'] is False
def test_get_flip_bounds_cropped_item(view, item):
item.SELECT_RESIZE_SIZE = 10
item.SELECT_ROTATE_SIZE = 10
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
edges = item.get_flip_bounds()
assert edges[0]['rect'].topLeft() == QtCore.QPointF(10, 0)
assert edges[0]['rect'].bottomRight() == QtCore.QPointF(100, 10)
assert edges[0]['flip_v'] is True
assert edges[1]['rect'].topLeft() == QtCore.QPointF(10, 80)
assert edges[1]['rect'].bottomRight() == QtCore.QPointF(100, 90)
assert edges[1]['flip_v'] is True
assert edges[2]['rect'].topLeft() == QtCore.QPointF(0, 10)
assert edges[2]['rect'].bottomRight() == QtCore.QPointF(10, 80)
assert edges[2]['flip_v'] is False
assert edges[3]['rect'].topLeft() == QtCore.QPointF(100, 10)
assert edges[3]['rect'].bottomRight() == QtCore.QPointF(110, 80)
assert edges[3]['flip_v'] is False
def test_bounding_rect_when_not_selected(view, item):
item.setSelected(False)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
rect = 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(view, item):
item.SELECT_RESIZE_SIZE = 10
item.SELECT_ROTATE_SIZE = 10
item.setSelected(True)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
rect = 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(view, item):
view.scene.addItem(item)
item.setSelected(False)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
shape = 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_single(view, item):
view.scene.addItem(item)
item.SELECT_RESIZE_SIZE = 10
item.SELECT_ROTATE_SIZE = 10
item.setSelected(True)
path = QtGui.QPainterPath()
path.addRect(QtCore.QRectF(0, 0, 100, 80))
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
shape = item.shape().boundingRect()
assert shape.topLeft().x() == -15
assert shape.topLeft().y() == -15
assert shape.bottomRight().x() == 115
assert shape.bottomRight().y() == 95
def test_shape_when_selected_multi(view, item):
view.scene.addItem(item)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.setSelected(True)
item.SELECT_RESIZE_SIZE = 10
item.SELECT_ROTATE_SIZE = 10
item.setSelected(True)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
shape = 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_get_scale_factor_bottomright(view, item):
item.event_start = QtCore.QPointF(10, 10)
item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
item.scale_orig_factor = 1
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(20, 90)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
assert item.get_scale_factor(event) == approx(1.5, 0.01)
def test_get_scale_factor_bottomright_cropped_item(view, item):
item.event_start = QtCore.QPointF(15, 15)
item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
item.scale_orig_factor = 1
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(25, 95)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
assert item.get_scale_factor(event) == approx(1.5, 0.01)
def test_get_scale_factor_topleft(view, item):
item.event_start = QtCore.QPointF(10, 10)
item.event_direction = QtCore.QPointF(-1, -1) / math.sqrt(2)
item.scale_orig_factor = 0.5
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(-10, -60)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
assert item.get_scale_factor(event) == approx(2, 0.01)
def test_get_scale_factor_topleft_cropped_item(view, item):
item.event_start = QtCore.QPointF(15, 15)
item.event_direction = QtCore.QPointF(-1, -1) / math.sqrt(2)
item.scale_orig_factor = 0.5
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(-5, -55)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
assert item.get_scale_factor(event) == approx(2, 0.01)
def test_get_scale_anchor_topleft(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
anchor = item.get_scale_anchor(QtCore.QPointF(0, 0))
assert anchor.x() == 100
assert anchor.y() == 80
def test_get_scale_anchor_topleft_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
anchor = item.get_scale_anchor(QtCore.QPointF(5, 5))
assert anchor.x() == 105
assert anchor.y() == 85
def test_get_scale_anchor_bottomright(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
anchor = item.get_scale_anchor(QtCore.QPointF(100, 80))
assert anchor.x() == 0
assert anchor.y() == 0
def test_get_scale_anchor_bottomright_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
anchor = item.get_scale_anchor(QtCore.QPointF(105, 85))
assert anchor.x() == 5
assert anchor.y() == 5
def test_get_scale_anchor_topright(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
anchor = item.get_scale_anchor(QtCore.QPointF(100, 0))
assert anchor.x() == 0
assert anchor.y() == 80
def test_get_scale_anchor_topright_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
anchor = item.get_scale_anchor(QtCore.QPointF(105, 5))
assert anchor.x() == 5
assert anchor.y() == 85
def test_get_scale_anchor_bottomleft(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
anchor = item.get_scale_anchor(QtCore.QPointF(0, 80))
assert anchor.x() == 100
assert anchor.y() == 0
def test_get_scale_anchor_bottomleft_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
anchor = item.get_scale_anchor(QtCore.QPointF(5, 85))
assert anchor.x() == 105
assert anchor.y() == 5
def test_get_corner_direction_topleft(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
assert item.get_corner_direction(
QtCore.QPointF(0, 0)) == QtCore.QPointF(-1, -1)
def test_get_corner_direction_topleft_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
assert item.get_corner_direction(
QtCore.QPointF(5, 5)) == QtCore.QPointF(-1, -1)
def test_get_corner_direction_bottomright(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
assert item.get_corner_direction(
QtCore.QPointF(100, 80)) == QtCore.QPointF(1, 1)
def test_get_corner_direction_bottomright_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
assert item.get_corner_direction(
QtCore.QPointF(105, 85)) == QtCore.QPointF(1, 1)
def test_get_corner_direction_topright(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
assert item.get_corner_direction(
QtCore.QPointF(100, 0)) == QtCore.QPointF(1, -1)
def test_get_corner_direction_topright_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
assert item.get_corner_direction(
QtCore.QPointF(105, 5)) == QtCore.QPointF(1, -1)
def test_get_corner_direction_bottomleft(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
assert item.get_corner_direction(
QtCore.QPointF(0, 80)) == QtCore.QPointF(-1, 1)
def test_get_corner_direction_bottomleft_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
assert item.get_corner_direction(
QtCore.QPointF(5, 85)) == QtCore.QPointF(-1, 1)
def test_get_direction_from_center_topleft(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
direction = item.get_direction_from_center(QtCore.QPointF(0, -10))
assert direction == approx(QtCore.QPointF(-1, -1) / math.sqrt(2))
def test_get_direction_from_center_topleft_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
direction = item.get_direction_from_center(QtCore.QPointF(5, -5))
assert direction == approx(QtCore.QPointF(-1, -1) / math.sqrt(2))
def test_get_direction_from_center_bottomright(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
direction = item.get_direction_from_center(QtCore.QPointF(100, 90))
assert direction == approx(QtCore.QPointF(1, 1) / math.sqrt(2))
def test_get_direction_from_center_bottomright_cropped_item(view, item):
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(5, 5, 100, 80)):
direction = item.get_direction_from_center(QtCore.QPointF(105, 95))
assert direction == approx(QtCore.QPointF(1, 1) / math.sqrt(2))
def test_get_direction_from_center_bottomright_when_rotated_180(view, item):
item.setRotation(180, QtCore.QPointF(50, 40))
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
direction = item.get_direction_from_center(QtCore.QPointF(100, 90))
assert direction == approx(QtCore.QPointF(1, 1) / math.sqrt(2))
def test_get_rotate_angle(view, item):
item.event_anchor = QtCore.QPointF(10, 20)
assert item.get_rotate_angle(QtCore.QPointF(15, 25)) == -45
def test_get_rotate_delta(view, item):
item.event_anchor = QtCore.QPointF(10, 20)
item.rotate_start_angle = -3
assert item.get_rotate_delta(QtCore.QPointF(15, 25)) == -42
def test_get_rotate_delta_snaps(view, item):
item.event_anchor = QtCore.QPointF(10, 20)
item.rotate_start_angle = -3
item.rotate_orig_degrees = 5
assert item.get_rotate_delta(QtCore.QPointF(15, 25), snap=True) == -35
def test_edge_flips_v_when_item_horizontal(view, item):
item.setRotation(20)
assert item.get_edge_flips_v({'flip_v': True}) is True
def test_edge_flips_v_when_item_horizontal_upside_down(view, item):
item.setRotation(200)
assert item.get_edge_flips_v({'flip_v': True}) is True
def test_edge_flips_v_when_item_vertical_cw(view, item):
item.setRotation(80)
assert item.get_edge_flips_v({'flip_v': True}) is False
def test_edge_flips_v_when_item_vertical_ccw(view, item):
item.setRotation(120)
assert item.get_edge_flips_v({'flip_v': True}) is False
def test_hover_move_event_no_selection(view, item):
view.scene.addItem(item)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(0, 0)
item.set_cursor = MagicMock()
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
item.set_cursor.assert_not_called()
def test_hover_move_event_small_item_inside_handle_free_center(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(10, 10)
item.unset_cursor = MagicMock()
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 20, 20)):
item.hoverMoveEvent(event)
item.unset_cursor.assert_called_once_with()
@mark.parametrize('pos,flipped,rotation, expected',
[((0, 0), False, 0, 'SizeFDiagCursor'),
((100, 80), False, 0, 'SizeFDiagCursor'),
((100, 0), False, 0, 'SizeBDiagCursor'),
((0, 0), False, 90, 'SizeBDiagCursor'),
((0, 0), False, 45, 'SizeVerCursor'),
((0, 0), False, 135, 'SizeHorCursor'),
((0, 80), False, 45, 'SizeHorCursor'),
((0, 80), False, 90, 'SizeFDiagCursor'),
((0, 80), False, 135, 'SizeVerCursor'),
((0, 80), True, 0, 'SizeFDiagCursor'),
((100, 0), True, 0, 'SizeFDiagCursor'),
((100, 80), True, 0, 'SizeBDiagCursor'),
((0, 80), True, 90, 'SizeBDiagCursor'),
((0, 80), True, 45, 'SizeHorCursor'),
((0, 0), True, 135, 'SizeHorCursor'),
((0, 0), True, 45, 'SizeVerCursor'),
((0, 0), True, 90, 'SizeFDiagCursor'),
((0, 0), True, 135, 'SizeHorCursor')])
def test_hover_move_event_scale(
pos, flipped, rotation, expected, view, item):
view.scene.addItem(item)
item.setSelected(True)
if flipped:
item.do_flip()
item.setRotation(rotation)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(*pos)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == getattr(Qt.CursorShape, expected)
def test_hover_move_event_scale_bottomright_very_wide_item(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(1000, 100)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 1000, 100)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == Qt.CursorShape.SizeFDiagCursor
def test_hover_move_event_rotate(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(115, 95)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == BeeAssets().cursor_rotate
def test_hover_flip_event_top_edge(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(50, 0)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == BeeAssets().cursor_flip_v
def test_hover_flip_event_bottom_edge(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(50, 80)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == BeeAssets().cursor_flip_v
def test_hover_flip_event_left_edge(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(0, 50)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == BeeAssets().cursor_flip_h
def test_hover_flip_event_right_edge(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(100, 50)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == BeeAssets().cursor_flip_h
def test_hover_flip_event_top_edge_rotated_90(view, item):
view.scene.addItem(item)
item.setSelected(True)
item.setRotation(90)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(50, 0)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == BeeAssets().cursor_flip_h
def test_hover_flip_event_left_edge_when_rotated_90(view, item):
view.scene.addItem(item)
event = MagicMock()
item.setSelected(True)
item.setRotation(90)
event.pos.return_value = QtCore.QPointF(0, 50)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == BeeAssets().cursor_flip_v
def test_hover_move_event_not_in_handles(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(50, 50)
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 1000, 800)):
item.hoverMoveEvent(event)
assert view.viewport().cursor() == Qt.CursorShape.ArrowCursor
def test_hover_leave_event(view, item):
view.scene.addItem(item)
event = MagicMock()
item.setSelected(True)
item.unset_cursor = MagicMock()
item.hoverLeaveEvent(event)
item.unset_cursor.assert_called_once_with()
def test_mouse_press_event_just_selected(view, item):
view.scene.addItem(item)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(0, 0)
event.button.return_value = Qt.MouseButton.LeftButton
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mousePressEvent') as m:
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.mousePressEvent(event)
event.accept.assert_not_called()
m.assert_called_once_with(event)
def test_mouse_press_event_small_item_inside_handle_free_center(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(10, 10)
event.button.return_value = Qt.MouseButton.LeftButton
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mousePressEvent') as m:
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 20, 20)):
item.mousePressEvent(event)
event.accept.assert_not_called()
m.assert_called_once_with(event)
def test_mouse_press_event_topleft_scale(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(2, 2)
event.scenePos.return_value = QtCore.QPointF(-1, -1)
event.button.return_value = Qt.MouseButton.LeftButton
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.mousePressEvent(event)
assert item.active_mode == item.SCALE_MODE
assert item.event_start == QtCore.QPointF(-1, -1)
assert item.event_direction.x() < 0
assert item.event_direction.y() < 0
assert item.scale_orig_factor == 1
event.accept.assert_called_once_with()
def test_mouse_press_event_bottomright_scale(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(99, 79)
event.scenePos.return_value = QtCore.QPointF(101, 81)
event.button.return_value = Qt.MouseButton.LeftButton
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.mousePressEvent(event)
assert item.active_mode == item.SCALE_MODE
assert item.event_start == QtCore.QPointF(101, 81)
assert item.event_direction.x() > 0
assert item.event_direction.y() > 0
assert item.scale_orig_factor == 1
event.accept.assert_called_once_with()
def test_mouse_press_event_rotate(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(111, 91)
event.scenePos.return_value = QtCore.QPointF(66, 99)
event.button.return_value = Qt.MouseButton.LeftButton
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mousePressEvent'):
item.mousePressEvent(event)
assert item.active_mode == item.ROTATE_MODE
assert item.event_anchor == QtCore.QPointF(50, 40)
assert item.rotate_orig_degrees == 0
event.accept.assert_called_once_with()
def test_mouse_press_event_flip(view, item):
view.scene.addItem(item)
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(0, 40)
event.button.return_value = Qt.MouseButton.LeftButton
view.scene.undo_stack = MagicMock(push=MagicMock())
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mousePressEvent'):
item.mousePressEvent(event)
args = view.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
isinstance(cmd, commands.FlipItems)
assert cmd.items == [item]
assert cmd.anchor == QtCore.QPointF(50, 40)
assert cmd.vertical is False
assert item.active_mode == item.FLIP_MODE
event.accept.assert_called_once_with()
def test_mouse_press_event_not_in_handles(view, item):
view.scene.addItem(item)
view.reset_previous_transform = MagicMock()
item.setSelected(True)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(50, 40)
event.button.return_value = Qt.MouseButton.LeftButton
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mousePressEvent') as m:
item.mousePressEvent(event)
m.assert_called_once_with(event)
assert item.active_mode is None
event.accept.assert_not_called()
def test_mouse_move_event_when_no_action_reset_prev_transform(view, item):
view.scene.addItem(item)
view.reset_previous_transform = MagicMock()
event = MagicMock()
item.event_start = QtCore.QPointF(10, 10)
event.scenePos.return_value = QtCore.QPointF(50, 40)
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mouseMoveEvent') as m:
item.mouseMoveEvent(event)
m.assert_called_once_with(event)
view.reset_previous_transform.assert_called_once()
event.accept.assert_not_called()
def test_mouse_move_event_when_no_action_doesnt_reset_prev_transf(view, item):
view.scene.addItem(item)
view.reset_previous_transform = MagicMock()
event = MagicMock()
item.event_start = QtCore.QPointF(10, 10)
event.scenePos.return_value = QtCore.QPointF(11, 11)
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mouseMoveEvent') as m:
item.mouseMoveEvent(event)
m.assert_called_once_with(event)
view.reset_previous_transform.assert_not_called()
event.accept.assert_not_called()
def test_mouse_move_event_when_scale_action(view, item):
view.scene.addItem(item)
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(20, 90)
item.active_mode = item.SCALE_MODE
item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
item.event_anchor = QtCore.QPointF(100, 80)
item.event_start = QtCore.QPointF(10, 10)
item.scale_orig_factor = 1
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mouseMoveEvent') as m:
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.mouseMoveEvent(event)
m.assert_not_called()
assert item.scale() == approx(1.5, 0.01)
event.accept.assert_called_once_with()
def test_mouse_move_event_when_rotate_action(view, item):
view.scene.addItem(item)
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(15, 25)
item.event_start = QtCore.QPointF(10, 10)
item.active_mode = item.ROTATE_MODE
item.rotate_orig_degrees = 0
item.rotate_start_angle = -3
item.event_anchor = QtCore.QPointF(10, 20)
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mouseMoveEvent') as m:
item.mouseMoveEvent(event)
m.assert_not_called()
assert item.rotation() == 318
event.accept.assert_called_once_with()
def test_mouse_move_event_when_flip_action(view, item):
view.scene.addItem(item)
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(15, 25)
item.event_start = QtCore.QPointF(10, 10)
item.active_mode = item.FLIP_MODE
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem.mouseMoveEvent') as m:
item.mouseMoveEvent(event)
m.assert_not_called()
event.accept.assert_called_once_with()
def test_mouse_release_event_when_no_action(view, item):
view.scene.addItem(item)
event = MagicMock()
item.active_mode = item.FLIP_MODE
event.pos.return_value = QtCore.QPointF(-100, -100)
with patch('PyQt6.QtWidgets.QGraphicsPixmapItem'
'.mouseReleaseEvent') as m:
item.mouseReleaseEvent(event)
m.assert_called_once_with(event)
item.active_mode is None
event.accept.assert_not_called()
def test_mouse_release_event_when_scale_action(view, item):
view.scene.addItem(item)
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(20, 90)
item.active_mode = item.SCALE_MODE
item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
item.event_anchor = QtCore.QPointF(100, 80)
item.event_start = QtCore.QPointF(10, 10)
item.scale_orig_factor = 1
view.scene.undo_stack = MagicMock(push=MagicMock())
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.mouseReleaseEvent(event)
view.scene.undo_stack.push.assert_called_once()
args = view.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
isinstance(cmd, commands.ScaleItemsBy)
assert cmd.items == [item]
assert cmd.factor == approx(1.5, 0.01)
assert cmd.anchor == QtCore.QPointF(100, 80)
assert cmd.ignore_first_redo is True
assert item.active_mode is None
event.accept.assert_called_once_with()
def test_mouse_release_event_when_scale_action_zero(view, item):
view.scene.addItem(item)
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(20, 90)
item.active_mode = item.SCALE_MODE
item.event_direction = QtCore.QPointF(1, 1) / math.sqrt(2)
item.event_anchor = QtCore.QPointF(100, 80)
item.event_start = QtCore.QPointF(20, 90)
item.scale_orig_factor = 1
view.scene.undo_stack = MagicMock(push=MagicMock())
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.mouseReleaseEvent(event)
view.scene.undo_stack.push.assert_not_called()
assert item.active_mode is None
event.accept.assert_called_once_with()
def test_mouse_release_event_when_rotate_action(view, item):
view.scene.addItem(item)
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(15, 25)
item.active_mode = item.ROTATE_MODE
item.rotate_orig_degrees = 0
item.rotate_start_angle = -3
item.event_anchor = QtCore.QPointF(10, 20)
view.scene.undo_stack = MagicMock(push=MagicMock())
item.mouseReleaseEvent(event)
view.scene.undo_stack.push.assert_called_once()
args = view.scene.undo_stack.push.call_args_list[0][0]
cmd = args[0]
isinstance(cmd, commands.RotateItemsBy)
assert cmd.items == [item]
assert cmd.delta == -42
assert cmd.anchor == QtCore.QPointF(10, 20)
assert cmd.ignore_first_redo is True
assert item.active_mode is None
event.accept.assert_called_once_with()
def test_mouse_release_event_when_rotate_action_zero(view, item):
view.scene.addItem(item)
event = MagicMock()
event.scenePos.return_value = QtCore.QPointF(15, 25)
item.active_mode = item.ROTATE_MODE
item.rotate_orig_degrees = 0
item.rotate_start_angle = -45
item.event_anchor = QtCore.QPointF(10, 20)
view.scene.undo_stack = MagicMock(push=MagicMock())
item.mouseReleaseEvent(event)
view.scene.undo_stack.push.assert_not_called()
assert item.active_mode is None
event.accept.assert_called_once_with()
def test_mouse_release_event_when_flip_action(view, item):
view.scene.addItem(item)
event = MagicMock()
event.pos.return_value = QtCore.QPointF(0, 40)
item.active_mode = item.FLIP_MODE
view.scene.undo_stack = MagicMock(push=MagicMock())
with patch.object(item, 'bounding_rect_unselected',
return_value=QtCore.QRectF(0, 0, 100, 80)):
item.mouseReleaseEvent(event)
view.scene.undo_stack.push.assert_not_called()
assert item.active_mode is None
event.accept.assert_called_once_with()