Missing lib directory

This commit is contained in:
Rebecca Breu 2021-03-22 21:48:05 +01:00
parent 3e76f86822
commit b3a322ebd7
14 changed files with 903 additions and 1 deletions

View file

@ -7,7 +7,7 @@ import sys
from PyQt6 import QtCore, QtGui, QtWidgets
from lib.view import BeeGraphicsView
from beeref.view import BeeGraphicsView
logger = logging.getLogger('BeeRef')

0
beeref/__init__.py Normal file
View file

38
beeref/bee_json.py Normal file
View file

@ -0,0 +1,38 @@
import json
from beeref.items import BeePixmapItem
class BeeJSONEncoder(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, 'to_bee_json'):
return obj.to_bee_json()
return json.JSONEncoder.default(self, obj)
def dumps(obj):
return json.dumps(obj, cls=BeeJSONEncoder)
class BeeJSONDecoder(json.JSONDecoder):
bee_classes = [BeePixmapItem]
def __init__(self, *args, **kwargs):
super().__init__(object_hook=self.object_hook, *args, **kwargs)
def get_bee_class(self, cls):
for bee_class in self.bee_classes:
if bee_class.__name__ == cls:
return bee_class
def object_hook(self, obj):
if 'cls' in obj:
bee_class = self.get_bee_class(obj['cls'])
return bee_class.from_bee_json(obj)
return obj
def loads(obj):
return json.loads(obj, cls=BeeJSONDecoder)

24
beeref/gui.py Normal file
View file

@ -0,0 +1,24 @@
import logging
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import Qt
logger = logging.getLogger('BeeRef')
class WelcomeOverlay(QtWidgets.QWidget):
txt = """<p>Paste or drop images here.</p>
<p>Right-click for more options.</p>"""
def __init__(self, parent=None):
super().__init__(parent)
self.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground)
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
label = QtWidgets.QLabel(self)
label.setText(self.txt)
label.setAlignment(Qt.Alignment.AlignVCenter | Qt.Alignment.AlignCenter)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)

109
beeref/items.py Normal file
View file

@ -0,0 +1,109 @@
import base64
import logging
from PyQt6 import QtCore, QtGui, QtWidgets
from beeref.selection import SelectionItem
logger = logging.getLogger('BeeRef')
class BeePixmapItem(QtWidgets.QGraphicsPixmapItem):
def __init__(self, image, filename=None):
super().__init__(QtGui.QPixmap.fromImage(image))
logger.debug(f'Initialized image "{filename}" with dimensions: '
f'{self.width} x {self.height} at index {self.zValue()}')
self.filename = filename
self.scale_factor = 1
self.setFlags(
QtWidgets.QGraphicsItem.GraphicsItemFlags.ItemIsMovable
| QtWidgets.QGraphicsItem.GraphicsItemFlags.ItemIsSelectable)
def setScale(self, factor):
self.scale_factor = factor
logger.debug(f'Setting scale for image "{self.filename}" to {factor}')
super().setScale(factor)
def set_pos_center(self, x, y):
"""Sets the position using the item's center as the origin point."""
self.setPos(x - self.width * self.scale_factor / 2,
y - self.height * self.scale_factor / 2)
@property
def width(self):
return self.pixmap().size().width()
@property
def height(self):
return self.pixmap().size().height()
def pixmap_to_str(self):
barray = QtCore.QByteArray()
buffer = QtCore.QBuffer(barray)
buffer.open(QtCore.QIODevice.OpenMode.WriteOnly)
img = self.pixmap().toImage()
img.save(buffer, 'PNG')
data = base64.b64encode(barray.data())
return data.decode('ascii')
@classmethod
def qimage_from_str(self, data):
img = QtGui.QImage()
img.loadFromData(base64.b64decode(data))
return img
def to_bee_json(self):
return {
'cls': self.__class__.__name__,
'scale': self.scale_factor,
'pixmap': self.pixmap_to_str(),
'pos': [self.pos().x(), self.pos().y()],
'z': self.zValue(),
'filename': self.filename,
}
@classmethod
def from_bee_json(cls, obj):
img = cls.qimage_from_str(obj['pixmap'])
item = cls(img, filename=obj.get('filename'))
if 'scale' in obj:
item.setScale(obj['scale'])
if 'pos' in obj:
item.setPos(*obj['pos'])
if 'z' in obj:
item.setZValue(obj['z'])
return item
def create_selection(self):
SelectionItem(self)
def clear_selection(self):
for child in self.childItems():
logger.debug('Removing child item...')
self.scene().removeItem(child)
logger.debug('Child item removed')
def itemChange(self, change, value):
ret = super().itemChange(change, value)
if change == self.GraphicsItemChange.ItemSelectedChange:
if value:
logger.debug('Item selected')
self.create_selection()
else:
logger.debug('Item deselected')
self.clear_selection()
return ret
def mousePressEvent(self, event):
print('????')
def sceneEvent(self, event):
print('********', event)
return super().sceneEvent(event)

71
beeref/scene.py Normal file
View file

@ -0,0 +1,71 @@
import logging
import math
from PyQt6 import QtCore, QtWidgets
from PyQt6.QtCore import Qt
logger = logging.getLogger('BeeRef')
class BeeGraphicsScene(QtWidgets.QGraphicsScene):
def delete_selected_items(self):
for item in self.selectedItems():
logger.debug('Deleting item...')
self.removeItem(item)
def normalize_width_or_height(self, mode):
values = [getattr(i, mode) for i in self.selectedItems()]
if not values:
return
avg = sum(values) / len(values)
logger.debug(f'Calculated average {mode} {avg}')
for item in self.selectedItems():
factor = avg / getattr(item, mode)
item.setScale(factor)
def normalize_height(self):
return self.normalize_width_or_height('height')
def normalize_width(self):
return self.normalize_width_or_height('width')
def normalize_size(self):
sizes = [i.width * i.height for i in self.selectedItems()]
if not sizes:
return
avg = sum(sizes) / len(sizes)
logger.debug(f'Calculated average size {avg}')
for item in self.selectedItems():
factor = math.sqrt(avg / item.width / item.height)
item.setScale(factor)
def has_selection(self):
"""Checks whether there are currently items selected."""
return bool(self.selectedItems())
def has_single_selection(self):
"""Checks whether there's currently exactly one item selected."""
return len(self.selectedItems()) == 1
def mousePressEvent(self, event):
if event.button() == Qt.MouseButtons.RightButton:
# Right-click invokes the context menu on the
# GraphicsView. We don't need it here.
return
super().mousePressEvent(event)
def items_for_export(self):
"""Returns the items that are to be exported."""
# self.items() holds items in reverse order of addition
return list(filter(lambda i: hasattr(i, 'to_bee_json'),
reversed(self.items())))

92
beeref/selection.py Normal file
View file

@ -0,0 +1,92 @@
import logging
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import Qt
logger = logging.getLogger('BeeRef')
class SelectionItem(QtWidgets.QGraphicsItem):
color = QtGui.QColor(116, 234, 231, 255)
handle_size = 30 # scaling handles
resize_size = 50 # area for scaling hover events
margin = 200 # margin for bounding box etc
debug = False
def __init__(self, item):
super().__init__(parent=item)
self.single_select_mode = False
bounds = self.parentItem().boundingRect()
pos = bounds.bottomRight()
self.bottom_right_scale_bounds = QtCore.QRectF(
pos.x() - self.resize_size/2,
pos.y() - self.resize_size/2,
self.resize_size,
self.resize_size)
def boundingRect(self):
bounds = self.parentItem().boundingRect()
return QtCore.QRectF(
bounds.topLeft().x() - self.margin,
bounds.topLeft().y() - self.margin,
bounds.bottomRight().x() + self.margin * 2,
bounds.bottomRight().y() + self.margin * 2)
def shape(self):
path = QtGui.QPainterPath()
path.addRect(self.boundingRect())
return path
def draw_debug_rect(self, painter, rect):
pen = QtGui.QPen(QtGui.QColor('red'))
pen.setWidth(3)
painter.setPen(pen)
painter.drawRect(rect)
def paint(self, painter, option, widget):
pen = QtGui.QPen(self.color)
pen.setWidth(10)
painter.setPen(pen)
# Draw the main selection rectangle
bounds = self.parentItem().boundingRect()
painter.drawRect(bounds)
self.single_select_mode = self.parentItem()\
.scene().has_single_selection()
self.setAcceptHoverEvents(self.single_select_mode)
self.setAcceptDrops(self.single_select_mode)
# If it's a single selection, draw the handles:
if self.single_select_mode:
self.setFlags(
QtWidgets.QGraphicsItem.GraphicsItemFlags.ItemIsSelectable)
pos = bounds.bottomRight()
painter.fillRect(pos.x() - self.handle_size/2,
pos.y() - self.handle_size/2,
self.handle_size,
self.handle_size,
self.color)
if self.debug:
self.draw_debug_rect(painter, self.boundingRect())
self.draw_debug_rect(painter, self.bottom_right_scale_bounds)
def hoverMoveEvent(self, event):
if not self.single_select_mode:
return
# In bottomright scale area?
if self.bottom_right_scale_bounds.contains(event.pos()):
self.setCursor(Qt.CursorShape.SizeFDiagCursor)
else:
self.setCursor(Qt.CursorShape.ArrowCursor)
def dragEnterEvent(self, event):
print('OOOOOOOOOOOOOOOdrag enter')

0
beeref/tests/__init__.py Normal file
View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

14
beeref/tests/base.py Normal file
View file

@ -0,0 +1,14 @@
from unittest import TestCase
from PyQt6 import QtWidgets
class BeeTestCase(TestCase):
@classmethod
def setUpClass(cls):
cls.app = QtWidgets.QApplication([])
@classmethod
def tearDownClass(cls):
cls.app.quit()

View file

@ -0,0 +1,30 @@
import os.path
from PyQt6 import QtGui
from beeref import bee_json
from beeref.items import BeePixmapItem
from beeref.scene import BeeGraphicsScene
from beeref.tests.base import BeeTestCase
class BeeJsonTestCase(BeeTestCase):
def test_dumps_loads(self):
filename = os.path.join('lib', 'tests', 'assets', 'test3x3.png')
item = BeePixmapItem(QtGui.QImage(filename), filename)
item.setScale(2)
item.setPos(100, 200)
item.setZValue(3)
dump = bee_json.dumps({'items': [item]})
obj = bee_json.loads(dump)
assert len(obj['items']) == 1
obj_item = obj['items'][0]
assert obj_item.scale_factor == 2
assert obj_item.zValue() == 3
assert obj_item.pos().x() == 100
assert obj_item.pos().y() == 200
assert obj_item.width == item.width
assert obj_item.height == item.height
assert obj_item.filename == filename

View file

@ -0,0 +1,80 @@
import os.path
from PyQt6 import QtGui, QtWidgets
from beeref.items import BeePixmapItem
from beeref.tests.base import BeeTestCase
class BeePixmapItemTestCase(BeeTestCase):
def test_init(self):
filename = os.path.join('beeref', 'tests', 'assets', 'test3x3.png')
item = BeePixmapItem(QtGui.QImage(filename), filename)
assert item.width == 3
assert item.height == 3
assert item.scale_factor == 1
assert item.flags() == (
QtWidgets.QGraphicsItem.GraphicsItemFlags.ItemIsMovable
| QtWidgets.QGraphicsItem.GraphicsItemFlags.ItemIsSelectable)
assert item.filename == filename
class BeePixmapItemToBeeJsonTestCase(BeeTestCase):
def test_basic(self):
item = BeePixmapItem(QtGui.QImage(), 'bee.png')
assert item.to_bee_json() == {
'cls': 'BeePixmapItem',
'scale': 1,
'pos': [0.0, 0.0],
'z': 0.0,
'pixmap': '',
'filename': 'bee.png',
}
def test_scale(self):
item = BeePixmapItem(QtGui.QImage())
item.setScale(2)
beejson = item.to_bee_json()
assert beejson['scale'] == 2
def test_position(self):
item = BeePixmapItem(QtGui.QImage())
item.setPos(100, 200)
beejson = item.to_bee_json()
assert beejson['pos'] == [100, 200]
def test_z_value(self):
item = BeePixmapItem(QtGui.QImage())
item.setZValue(3)
beejson = item.to_bee_json()
assert beejson['z'] == 3.0
def test_pixmap(self):
filename = os.path.join('beeref', 'tests', 'assets', 'test3x3.png')
item = BeePixmapItem(QtGui.QImage(filename))
beejson = item.to_bee_json()
assert len(beejson['pixmap']) > 0
class BeePixmapItemFromBeeJsonTestCase(BeeTestCase):
def test_basic(self):
bee_json = {
'cls': 'BeePixmapItem',
'scale': 2,
'pos': [100.0, 200.0],
'z': 3.0,
'pixmap': '',
'filename': 'bee.png',
}
item = BeePixmapItem.from_bee_json(bee_json)
assert isinstance(item, BeePixmapItem)
assert item.scale_factor == 2
assert item.pos().x() == 100.0
assert item.pos().y() == 200.0
assert item.zValue() == 3.0
assert item.filename == 'bee.png'

View file

@ -0,0 +1,47 @@
import math
from unittest.mock import patch, MagicMock
from PyQt6 import QtGui
from beeref.items import BeePixmapItem
from beeref.scene import BeeGraphicsScene
from beeref.tests.base import BeeTestCase
class BeeGraphicsSceneNormalizeTestCase(BeeTestCase):
def test_normalize_height(self):
item1 = MagicMock(height=100)
item2 = MagicMock(height=200)
scene = BeeGraphicsScene()
with patch.object(scene, 'selectedItems',
return_value=[item1, item2]):
scene.normalize_height()
item1.setScale.assert_called_once_with(1.5)
item2.setScale.assert_called_once_with(0.75)
def test_normalize_width(self):
item1 = MagicMock(width=100)
item2 = MagicMock(width=200)
scene = BeeGraphicsScene()
with patch.object(scene, 'selectedItems',
return_value=[item1, item2]):
scene.normalize_width()
item1.setScale.assert_called_once_with(1.5)
item2.setScale.assert_called_once_with(0.75)
def test_normalize_size(self):
item1 = MagicMock(width=100, height=200)
item2 = MagicMock(width=400, height=100)
scene = BeeGraphicsScene()
with patch.object(scene, 'selectedItems',
return_value=[item1, item2]):
scene.normalize_size()
item1.setScale.assert_called_once_with(math.sqrt(1.5))
item2.setScale.assert_called_once_with(math.sqrt(0.75))

397
beeref/view.py Normal file
View file

@ -0,0 +1,397 @@
import logging
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import Qt
from beeref import bee_json
from beeref.gui import WelcomeOverlay
from beeref.items import BeePixmapItem
from beeref.scene import BeeGraphicsScene
logger = logging.getLogger('BeeRef')
class BeeGraphicsView(QtWidgets.QGraphicsView):
def __init__(self, app, parent=None, filename=None):
super().__init__(parent)
self.app = app
self.scene = BeeGraphicsScene()
self.filename = None
# TBD: make scrollbar configurable
self.setHorizontalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
self.setAcceptDrops(True)
# TBD: fix zoom anchor - why does this not work?
# self.setTransformationAnchor(
# QtWidgets.QGraphicsView.ViewportAnchor.AnchorUnderMouse)
self.pan_active = False
self.scene.changed.connect(self.on_scene_changed)
self.scene.selectionChanged.connect(self.on_selection_changed)
self.setScene(self.scene)
# Context menu and actions
self.setContextMenuPolicy(
Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self.on_context_menu)
self.context_menu = QtWidgets.QMenu(self)
self.build_actions()
self.welcome_overlay = WelcomeOverlay(self)
# Load file given via command line
if filename:
self.open_from_file(filename)
def on_scene_changed(self, region):
if not self.scene.items():
logger.info('No items in scene')
self.welcome_overlay.show()
else:
self.welcome_overlay.hide()
self.recalc_scene_rect()
def build_actions(self):
self.actions_active_when_selection = []
def add_to_menu(menu, actions):
for action in actions:
qaction = QtGui.QAction(action['text'], self)
if 'shortcuts' in action:
qaction.setShortcuts(action['shortcuts'])
qaction.triggered.connect(action['callback'])
self.addAction(qaction)
menu.addAction(qaction)
if 'group' in action:
action['group'].append(qaction)
qaction.setEnabled(action.get('enabled', True))
# File menu
actions = [
{
'text': '&Open',
'shortcuts': ['Ctrl+O'],
'callback': self.on_action_open,
},
{
'text': '&Save',
'shortcuts': ['Ctrl+S'],
'callback': self.on_action_save,
},
{
'text': 'Save &As...',
'shortcuts': ['Ctrl+Shift+S'],
'callback': self.on_action_save_as,
},
{
'text': '&Quit...',
'shortcuts': ['Ctrl+Q'],
'callback': self.on_action_quit,
},
]
add_to_menu(self.context_menu.addMenu('&File'), actions)
# Add menu
actions = [
{
'text': 'Images from &files...',
'shortcuts': ['Ctrl+I'],
'callback': self.on_action_load_images,
},
{
'text': 'Image from &clipboard...',
'shortcuts': ['Ctrl+V'],
'callback': self.on_action_paste_images,
},
]
add_to_menu(self.context_menu.addMenu('&Add'), actions)
# Edit menu
actions = [
{
'text': '&Delete',
'shortcuts': ['Del'],
'callback': self.on_action_delete_images,
'group': self.actions_active_when_selection,
'enabled': False,
},
]
items_menu = self.context_menu.addMenu('&Items')
add_to_menu(items_menu, actions)
actions = [
{
'text': '&Height',
'shortcuts': ['Shift+H'],
'callback': self.on_action_normalize_height,
'group': self.actions_active_when_selection,
'enabled': False,
},
{
'text': '&Width',
'shortcuts': ['Shift+W'],
'callback': self.on_action_normalize_width,
'group': self.actions_active_when_selection,
'enabled': False,
},
{
'text': '&Size',
'shortcuts': ['Shift+S'],
'callback': self.on_action_normalize_size,
'group': self.actions_active_when_selection,
'enabled': False,
},
]
add_to_menu(items_menu.addMenu('&Normalize'), actions)
def on_context_menu(self, point):
self.context_menu.exec(self.mapToGlobal(point))
def get_supported_image_formats(self, cls):
formats = map(lambda f: f'*.{f.data().decode()}',
cls.supportedImageFormats())
return ' '.join(formats)
def get_view_center(self):
return QtCore.QPoint(self.size().width() / 2,
self.size().height() / 2)
def on_action_delete_images(self):
self.scene.delete_selected_items()
def on_action_normalize_height(self):
self.scene.normalize_height()
def on_action_normalize_width(self):
self.scene.normalize_width()
def on_action_normalize_size(self):
self.scene.normalize_size()
def add_image(self, img, pos, filename=None):
item = BeePixmapItem(img, filename)
item.set_pos_center(pos.x(), pos.y())
item.setSelected(True)
self.scene.addItem(item)
def save_to_file(self, filename):
logging.info(f'Saving to file {filename}')
with open(filename, 'w') as f:
f.write(bee_json.dumps({'items': self.scene.items_for_export()}))
self.filename = filename
def open_from_file(self, filename):
logging.info(f'Opening file {filename}')
with open(filename, 'r') as f:
items = bee_json.loads(f.read())['items']
self.scene.clear()
for item in items:
self.scene.addItem(item)
self.filename = filename
def on_action_open(self):
filename, f = QtWidgets.QFileDialog.getOpenFileName(
parent=self,
caption='Open file',
filter='BeeRef File (*.bee)')
if filename:
self.open_from_file(filename)
def on_action_save_as(self):
filename, f = QtWidgets.QFileDialog.getSaveFileName(
parent=self,
caption='Save file',
filter='BeeRef File (*.bee)')
if filename:
if not filename.endswith('.bee'):
filename = f'{filename}.bee'
self.save_to_file(filename)
def on_action_save(self):
if not self.filename:
self.on_action_save_as()
else:
self.save_to_file(self.filename)
def on_action_quit(self):
logger.info('User quit. Exiting...')
self.app.quit()
def on_action_load_images(self):
formats = self.get_supported_image_formats(QtGui.QImageReader)
filenames, f = QtWidgets.QFileDialog.getOpenFileNames(
parent=self,
caption='Select one ore more images to open',
filter=f'Images ({formats})')
if filenames:
self.scene.clearSelection()
pos = self.mapToScene(self.get_view_center())
errors = []
for filename in filenames:
logger.info(f'Loading image from file {filename}')
img = QtGui.QImage(filename)
if img.isNull():
errors.append(filename)
continue
self.add_image(img, pos, filename)
pos.setX(pos.x() + 50)
pos.setY(pos.y() + 50)
if errors:
errornames = [
f'<li>{fn}</li>' for fn in errors]
errornames = '<ul>%s</ul>' % '\n'.join(errornames)
msg = ('{errors} image(s) out of {total} '
'could not be opened:'.format(
errors=len(errors), total=len(filenames)))
QtWidgets.QMessageBox.warning(
self,
'Problem loading images',
msg + errornames)
def on_action_paste_images(self):
logging.info('Pasting image from clipboard...')
clipboard = QtWidgets.QApplication.clipboard()
img = clipboard.image()
if img.isNull():
logging.info('No image data in clipboard')
else:
self.scene.clearSelection()
pos = self.mapToScene(
self.mapFromGlobal(self.cursor().pos()))
self.add_image(img, pos)
def on_selection_changed(self):
for action in self.actions_active_when_selection:
action.setEnabled(self.scene.has_selection())
self.viewport().repaint()
def recalc_scene_rect(self):
"""Resize the scene rectangle so that it is always one view width
wider than all items' bounding box at each side and one view
width higher on top and bottom. This gives the impression of
an infinite canvas."""
logger.debug('Recalculating scene rectangle...')
try:
topleft = self.mapFromScene(
self.scene.itemsBoundingRect().topLeft())
topleft = self.mapToScene(QtCore.QPoint(
topleft.x() - self.size().width(),
topleft.y() - self.size().height()))
bottomright = self.mapFromScene(
self.scene.itemsBoundingRect().bottomRight())
bottomright = self.mapToScene(QtCore.QPoint(
bottomright.x() + self.size().width(),
bottomright.y() + self.size().height()))
self.setSceneRect(QtCore.QRectF(topleft, bottomright))
except OverflowError:
logger.info('Maximum scene size reached')
def get_zoom_size(self, func):
"""Calculates the size of the current items' bounding box
in the view's coordinates.
This helps ensure that we never zoom out too much (scene
becomes so tiny that they become invisible) or zoom in too
much (causing overflow errors).
`func` is a function witch takes the width and height as
arguments and turns it into a number, for ex. `min` or `max`.
"""
topleft = self.mapFromScene(
self.scene.itemsBoundingRect().topLeft())
bottomright = self.mapFromScene(
self.scene.itemsBoundingRect().bottomRight())
return func(bottomright.x() - topleft.x(),
bottomright.y() - topleft.y())
def scale(self, *args, **kwargs):
super().scale(*args, **kwargs)
self.recalc_scene_rect()
def wheelEvent(self, event):
factor = 1.2
if event.angleDelta().y() > 0:
if self.get_zoom_size(max) < 10000000:
self.scale(factor, factor)
else:
logging.debug('Maximum zoom size reached')
else:
if self.get_zoom_size(min) > 50:
self.scale(1/factor, 1/factor)
else:
logging.debug('Minimum zoom size reached')
event.accept()
def mouseMoveEvent(self, event):
if self.pan_active:
point = event.position()
hscroll = self.horizontalScrollBar()
hscroll.setValue(hscroll.value() + self.pan_start.x() - point.x())
vscroll = self.verticalScrollBar()
vscroll.setValue(vscroll.value() + self.pan_start.y() - point.y())
self.pan_start = point
event.accept()
return
super().mouseMoveEvent(event)
def mousePressEvent(self, event):
if (event.button() == Qt.MouseButtons.MiddleButton
or (event.button() == Qt.MouseButtons.LeftButton
and event.modifiers() == Qt.KeyboardModifiers.AltModifier)):
self.pan_active = True
self.pan_start = event.position()
self.setCursor(Qt.CursorShape.ClosedHandCursor)
event.accept()
return
super().mousePressEvent(event)
def mouseReleaseEvent(self, event):
if self.pan_active:
self.setCursor(Qt.CursorShape.ArrowCursor)
self.pan_active = False
event.accept()
return
super().mouseReleaseEvent(event)
def resizeEvent(self, event):
super().resizeEvent(event)
self.recalc_scene_rect()
self.welcome_overlay.resize(self.size())
def dragEnterEvent(self, event):
logging.debug('Received drag enter event')
# tbd: always empty???
print(event.mimeData().formats())
print(dir(event))
if event.mimeData().hasImage():
event.acceptProposedAction()
else:
logging.info('Attempted drop not an image')
def dragMoveEvent(self, event):
event.acceptProposedAction()
def dropEvent(self, event):
logging.info('Handling file drop...')
print(event.mimeData().formats())
# tbd