Skip to content

Instantly share code, notes, and snippets.

@ssokolow

ssokolow/1_screenshot.png

Last active Sep 10, 2020
Embed
What would you like to do?
Simple PyQt5 application to inspect drag-and-drop or clipboard data
#!/usr/bin/env python3
"""A simple test program to easily inspect what a drag-and-drop
or paste contains."""
__authors__ = "Stephan Sokolow (deitarion/SSokolow)"
__license__ = "MIT"
import signal, sys
from pprint import pprint
try:
# pylint: disable=no-name-in-module
from PyQt5.QtCore import ( # type: ignore
Qt, QCommandLineParser, QCommandLineOption, pyqtSlot)
from PyQt5.QtGui import ( # type: ignore
QClipboard, QCursor, QIcon, QKeySequence)
from PyQt5.QtWidgets import ( # type: ignore
QAction, QApplication, QLabel, QMenu)
except ImportError:
raise # Little hack to scope the disable=no-name-in-module
# Make this default to 60 and configurable by command-line argument
DATA_TRUNCATE_TO = 60
class TestWindow(QLabel):
"""Simple test target for drag-and-drop with a Paste context menu"""
def __init__(self, trunc_len=DATA_TRUNCATE_TO, *args, **kwargs):
super(TestWindow, self).__init__(*args, **kwargs)
self.setAcceptDrops(True)
self.setWindowTitle("QMimeData Test Tool")
self.setMargin(10)
#self.resize(200, 200)
self.trunc_len = trunc_len
# Bind a Paste handler to the platform-standard hotkey
self.a_paste = QAction("&Paste", self)
self.a_paste.setShortcuts(QKeySequence.Paste)
self.a_paste.setShortcutContext(Qt.ApplicationShortcut)
self.a_paste.triggered.connect(self.cb_paste)
self.addAction(self.a_paste)
# Quick hack for instructions and a roughly square window sized to fit
self.setText("<b>QMimeData Test Tool</b><br><br>"
"<b>Data Sources:</b><br>"
"&nbsp;• DnD: Drop something here<br>"
"&nbsp;• Clipboard: Press Ctrl+V or Right-click<br>"
"&nbsp;• Selection: Middle-click<br><br>"
"<b>Results:</b><br><br>"
"Offered data types will appear here.<br>"
"See <code>stdout</code> for contents<br>")
@pyqtSlot()
def cb_paste(self):
"""Handler for the paste action"""
clipboard = QApplication.instance().clipboard()
self.handle_mimedata(clipboard.mimeData())
def contextMenuEvent(self, _event):
"""Declare a context menu to allow mouse-only paste
(And to make the Paste keyboard shortcut more discoverable)
"""
cmenu = QMenu(self)
cmenu.addAction(self.a_paste)
cmenu.exec(QCursor.pos())
def dragEnterEvent(self, event): # pylint: disable=no-self-use
"""Required by Qt's drag-and-drop API"""
event.acceptProposedAction()
def dragMoveEvent(self, event): # pylint: disable=no-self-use
"""Required by Qt's drag-and-drop API"""
event.acceptProposedAction()
def dropEvent(self, event):
"""Actually handle dropped data"""
self.handle_mimedata(event.mimeData())
def handle_mimedata(self, mimeData):
"""Common code for paste and drag-and-drop"""
self.setAlignment(Qt.AlignTop)
self.setText("<b>Offered Types:</b><br><br>" +
'<br>'.join(mimeData.formats()))
results = {}
print('\nCOOKED:')
if mimeData.hasColor():
results['colorData'] = mimeData.colorData()
if mimeData.hasHtml():
results['html'] = mimeData.html()
if mimeData.hasImage():
results['imageData'] = mimeData.imageData()
if mimeData.hasText():
results['text'] = mimeData.text()
if mimeData.hasUrls():
results['urls'] = mimeData.urls()
pprint(results)
results = {}
print('\nRAW:')
for mimetype in mimeData.formats():
data = mimeData.data(mimetype)
if self.trunc_len and len(data) > self.trunc_len:
# pylint: disable=redefined-variable-type
if isinstance(data, bytes):
tmpl = b"{}... (+{} bytes)"
else:
tmpl = "{}... (+{} bytes)"
data = tmpl.format(data[:self.trunc_len],
len(data) - self.trunc_len)
results[mimetype] = data
pprint(results)
def mousePressEvent(self, event):
"""Handler to support querying the SELECTION on supporting desktops"""
if not event.buttons() & Qt.MiddleButton:
return
clipboard = QApplication.instance().clipboard()
if not clipboard.supportsSelection():
return
self.handle_mimedata(clipboard.mimeData(QClipboard.Selection))
# Allow killing via Ctrl+C
signal.signal(signal.SIGINT, signal.SIG_DFL)
if __name__ == '__main__':
app = QApplication(sys.argv)
truncate_opt = QCommandLineOption("truncate-to", app.translate(
"main", "Truncate pasted data to <len> characters. (default: {})"
).format(DATA_TRUNCATE_TO), "len", str(DATA_TRUNCATE_TO))
parser = QCommandLineParser()
parser.setApplicationDescription(__doc__.split('--snip--')[0].strip())
parser.addHelpOption()
parser.addOption(truncate_opt)
parser.process(app)
# Have a decent default window icon on X11/Wayland desktops
app.setWindowIcon(QIcon.fromTheme('edit-paste'))
win = TestWindow(int(parser.value(truncate_opt)))
win.show()
app.exec_()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.