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 0000000..b2d4e7e Binary files /dev/null and b/beeref/assets/cursor_rotate.png differ 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))