From 8204d3141fb8a03869a0709ec0bde8f88ebaa595 Mon Sep 17 00:00:00 2001 From: Rebecca Breu Date: Mon, 5 Apr 2021 14:45:18 +0200 Subject: [PATCH] Add rotate cursor --- beeref/__main__.py | 6 +- beeref/assets.py | 48 ++++++++++++++ beeref/assets/cursor_rotate.png | Bin 0 -> 2023 bytes beeref/assets/cursor_rotate.svg | 111 ++++++++++++++++++++++++++++++++ beeref/config.py | 11 ++-- beeref/selection.py | 3 +- tests/test_assets.py | 14 ++++ tests/test_config.py | 8 +++ tests/test_selection.py | 9 +++ 9 files changed, 200 insertions(+), 10 deletions(-) create mode 100644 beeref/assets.py create mode 100644 beeref/assets/cursor_rotate.png create mode 100644 beeref/assets/cursor_rotate.svg create mode 100644 tests/test_assets.py create mode 100644 tests/test_config.py diff --git a/beeref/__main__.py b/beeref/__main__.py index ad1d328..dfb6a29 100755 --- a/beeref/__main__.py +++ b/beeref/__main__.py @@ -22,6 +22,7 @@ import sys from PyQt6 import QtCore, QtGui, QtWidgets +from beeref.assets import BeeAssets from beeref.config import CommandlineArgs from beeref.view import BeeGraphicsView @@ -33,10 +34,7 @@ class BeeRefMainWindow(QtWidgets.QWidget): def __init__(self, app, filename=None): super().__init__() self.setWindowTitle('BeeRef') - root = os.path.dirname(__file__) - logo = os.path.join(root, 'assets', 'logo.png') - logger.debug(f'Loading icon {logo}') - self.setWindowIcon(QtGui.QIcon(logo)) + self.setWindowIcon(BeeAssets().logo) layout = QtWidgets.QVBoxLayout() layout.setContentsMargins(QtCore.QMargins(0, 0, 0, 0)) self.setLayout(layout) diff --git a/beeref/assets.py b/beeref/assets.py new file mode 100644 index 0000000..745f3ee --- /dev/null +++ b/beeref/assets.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +# This file is part of BeeRef. +# +# BeeRef is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BeeRef is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BeeRef. If not, see . + +import logging +import os.path + +from PyQt6 import QtCore, QtGui, QtWidgets +from PyQt6.QtCore import Qt + + +logger = logging.getLogger('BeeRef') + + +class BeeAssets: + _instance = None + PATH = os.path.join( os.path.dirname(__file__), 'assets') + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super().__new__(cls, *args, **kwargs) + cls._instance.on_new() + return cls._instance + + def on_new(self): + PATH = os.path.join(os.path.dirname(__file__), 'assets') + logger.debug(f'Assets path: {self.PATH}') + + self.logo = QtGui.QIcon(os.path.join(self.PATH, 'logo.png')) + self.cursor_rotate = self.cursor_from_image('cursor_rotate', (6, 10)) + + def cursor_from_image(self, filename, hotspot): + img = QtGui.QImage(os.path.join(self.PATH, filename)) + return QtGui.QCursor( + QtGui.QPixmap.fromImage(img), hotspot[0], hotspot[1]) diff --git a/beeref/assets/cursor_rotate.png b/beeref/assets/cursor_rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..b2d4e7e12a132158539970bec1c7983a3c384881 GIT binary patch literal 2023 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 FV1kfqv!eh2 literal 0 HcmV?d00001 diff --git a/beeref/assets/cursor_rotate.svg b/beeref/assets/cursor_rotate.svg new file mode 100644 index 0000000..06fc6d3 --- /dev/null +++ b/beeref/assets/cursor_rotate.svg @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/beeref/config.py b/beeref/config.py index 5cb6a11..11cd717 100644 --- a/beeref/config.py +++ b/beeref/config.py @@ -35,7 +35,7 @@ parser.add_argument( help='draw debug shapes for bounding rects and interactable areas') -class CommandlineArgs(): +class CommandlineArgs: """Wrapper around argument parsing. Checking for unknown arugments is configurable so that it can be @@ -52,10 +52,11 @@ class CommandlineArgs(): return cls._instance def __init__(self, with_check=False): - if with_check: - self._args = parser.parse_args() - else: - self._args = parser.parse_known_args()[0] + if not hasattr(self, '_args'): + if with_check: + self._args = parser.parse_args() + else: + self._args = parser.parse_known_args()[0] def __getattribute__(self, name): if name == '_args': diff --git a/beeref/selection.py b/beeref/selection.py index 2bf2ef2..ea49190 100644 --- a/beeref/selection.py +++ b/beeref/selection.py @@ -21,6 +21,7 @@ from PyQt6 import QtCore, QtGui, QtWidgets from PyQt6.QtCore import Qt from PyQt6.QtWidgets import QGraphicsItem +from beeref.assets import BeeAssets from beeref import commands from beeref.config import CommandlineArgs from beeref import utils @@ -204,7 +205,7 @@ class SelectableMixin(BaseItemMixin): self.setCursor(Qt.CursorShape.SizeBDiagCursor) return if self.get_rotate_bounds(corner).contains(event.pos()): - self.setCursor(Qt.CursorShape.ForbiddenCursor) + self.setCursor(BeeAssets().cursor_rotate) return self.setCursor(Qt.CursorShape.ArrowCursor) diff --git a/tests/test_assets.py b/tests/test_assets.py new file mode 100644 index 0000000..98b21f6 --- /dev/null +++ b/tests/test_assets.py @@ -0,0 +1,14 @@ +from PyQt6 import QtGui + +from beeref.assets import BeeAssets +from .base import BeeTestCase + + +class GetRectFromPointsTestCase(BeeTestCase): + + def test_singleton(self): + assert BeeAssets() is BeeAssets() + assert BeeAssets().logo is BeeAssets().logo + + def test_has_logo(self): + assert isinstance(BeeAssets().logo, QtGui.QIcon) diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..56cb80a --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,8 @@ +from unittest.mock import patch + +from beeref.config import CommandlineArgs + + +def test_singleton(): + assert CommandlineArgs() is CommandlineArgs() + assert CommandlineArgs()._args is CommandlineArgs()._args diff --git a/tests/test_selection.py b/tests/test_selection.py index f36e3e7..72aa3f9 100644 --- a/tests/test_selection.py +++ b/tests/test_selection.py @@ -3,6 +3,7 @@ from unittest.mock import patch, MagicMock, PropertyMock from PyQt6 import QtCore, QtGui from PyQt6.QtCore import Qt +from beeref.assets import BeeAssets from beeref.items import BeePixmapItem from beeref.scene import BeeGraphicsScene from beeref.selection import MultiSelectItem, RubberbandItem @@ -406,6 +407,14 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase): self.item.setCursor.assert_called_once_with( Qt.CursorShape.SizeBDiagCursor) + def test_hover_move_event_rotate(self): + self.item.setSelected(True) + self.item.SELECT_RESIZE_SIZE = 10 + self.item.SELECT_ROTATE_SIZE = 10 + self.event.pos = MagicMock(return_value=QtCore.QPointF(110, 90)) + self.item.hoverMoveEvent(self.event) + self.item.setCursor.assert_called_once_with(BeeAssets().cursor_rotate) + def test_hover_move_event_not_in_handles(self): self.item.setSelected(True) self.event.pos = MagicMock(return_value=QtCore.QPointF(50, 50))