mirror of
https://github.com/rbreu/beeref.git
synced 2026-03-11 08:54:28 +00:00
Add rotate cursor
This commit is contained in:
parent
1ff957bca4
commit
8204d3141f
9 changed files with 200 additions and 10 deletions
|
|
@ -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)
|
||||
|
|
|
|||
48
beeref/assets.py
Normal file
48
beeref/assets.py
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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])
|
||||
BIN
beeref/assets/cursor_rotate.png
Normal file
BIN
beeref/assets/cursor_rotate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2 KiB |
111
beeref/assets/cursor_rotate.svg
Normal file
111
beeref/assets/cursor_rotate.svg
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="43.466164mm"
|
||||
height="43.466164mm"
|
||||
viewBox="0 0 43.466164 43.466164"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="cursor_rotate_2.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
inkscape:export-filename="/home/rbreu/code/python/beeref/git/beeref/assets/cursor_rotate_2.png"
|
||||
inkscape:export-xdpi="19.870001"
|
||||
inkscape:export-ydpi="19.870001">
|
||||
<defs
|
||||
id="defs2">
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1107"
|
||||
x="-0.026028319"
|
||||
width="1.0520566"
|
||||
y="-0.022264951"
|
||||
height="1.0445299">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.3499727"
|
||||
id="feGaussianBlur1109" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#8affc7"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4.1593846"
|
||||
inkscape:cx="79.730822"
|
||||
inkscape:cy="62.010665"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1311"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="55"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-45.073624,-71.54261)">
|
||||
<path
|
||||
style="display:inline;opacity:0.27999998;fill:#060000;fill-opacity:1;stroke:#141212;stroke-width:2.26152635;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1107)"
|
||||
d="m 67.706833,81.380683 0.01655,-6.509726 -14.216038,11.446056 14.128554,9.936714 0.0371,-6.139976 c 4.096961,0.008 7.48908,3.132026 7.4158,6.983515 -0.06361,3.343294 -3.168469,6.638494 -6.97212,6.633644 -3.803652,-0.005 -7.30065,-4.457607 -6.273868,-7.248175 l -8.267827,-5.990456 c -5.40715,13.579601 6.14067,22.168771 14.232955,22.102821 8.19885,-0.0668 16.472123,-6.26946 16.642924,-15.237257 C 84.626131,88.1555 77.590725,80.975864 67.706831,81.380683 Z"
|
||||
id="path895-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccszccssc"
|
||||
inkscape:label="shadow"
|
||||
transform="matrix(1.0214918,0,0,1.0214918,-2.1351019,-1.5473716)" />
|
||||
<path
|
||||
style="display:inline;fill:#060000;fill-opacity:1;stroke:#f6f0f0;stroke-width:2.3101306;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 66.035745,79.802895 0.01691,-6.649632 -14.521566,11.692052 14.432202,10.150272 0.0379,-6.271935 c 4.185012,0.0082 7.650034,3.199339 7.575179,7.133603 -0.06498,3.415148 -3.236565,6.781175 -7.121963,6.776215 -3.885399,-0.005 -7.457554,-4.553411 -6.408705,-7.403953 l -8.445517,-6.119202 c -5.523359,13.871445 6.272644,22.645215 14.538846,22.577855 8.375058,-0.0682 16.826137,-6.40421 17.00061,-15.564738 C 83.318677,86.723315 76.132066,79.389376 66.03575,79.802895 Z"
|
||||
id="path895"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccszccssc"
|
||||
inkscape:label="arrow" />
|
||||
<g
|
||||
id="g1090"
|
||||
inkscape:label="guides"
|
||||
style="display:none">
|
||||
<ellipse
|
||||
ry="14.478881"
|
||||
rx="14.928679"
|
||||
cy="95.189369"
|
||||
cx="67.291611"
|
||||
id="path1071"
|
||||
style="display:inline;opacity:1;fill:none;fill-opacity:1;stroke:#ff0000;stroke-width:0.19997676;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<circle
|
||||
r="7.8203492"
|
||||
cy="95.24012"
|
||||
cx="67.361153"
|
||||
id="path1071-1"
|
||||
style="display:inline;opacity:1;fill:none;fill-opacity:1;stroke:#ff0000;stroke-width:0.13681354;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
14
tests/test_assets.py
Normal file
14
tests/test_assets.py
Normal file
|
|
@ -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)
|
||||
8
tests/test_config.py
Normal file
8
tests/test_config.py
Normal file
|
|
@ -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
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
Loading…
Reference in a new issue