Created
June 13, 2020 17:17
-
-
Save MaurizioB/30763f4e0ce7cdf055cc2d43c6e2ceb5 to your computer and use it in GitHub Desktop.
Click based screen grab workaround for systems not supporting Qt mouseGrab()
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from PyQt5 import QtCore, QtGui, QtWidgets | |
class FakeCursor(QtWidgets.QWidget): | |
def __init__(self): | |
super().__init__() | |
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.ToolTip) | |
self.setFixedSize(16, 16) | |
path = QtGui.QPainterPath() | |
path.moveTo(0, 8) | |
path.lineTo(6, 8) | |
path.moveTo(8, 0) | |
path.lineTo(8, 6) | |
path.moveTo(10, 8) | |
path.lineTo(16, 8) | |
path.moveTo(8, 10) | |
path.lineTo(8, 16) | |
stroker = QtGui.QPainterPathStroker() | |
stroker.setCapStyle(QtCore.Qt.FlatCap) | |
stroker.setWidth(2) | |
final = stroker.createStroke(path) | |
region = QtGui.QRegion() | |
for path in final.toFillPolygons(QtGui.QTransform()): | |
region |= QtGui.QRegion(path.toPolygon()) | |
self.setMask(region) | |
def closeEvent(self, event): | |
event.ignore() | |
def paintEvent(self, event): | |
qp = QtGui.QPainter(self) | |
qp.fillRect(self.rect(), QtCore.Qt.black) | |
class GrabTest(QtWidgets.QWidget): | |
def __init__(self): | |
super().__init__() | |
layout = QtWidgets.QVBoxLayout(self) | |
self.grabButton = QtWidgets.QPushButton('Grab') | |
layout.addWidget(self.grabButton) | |
self.grabButton.installEventFilter(self) | |
self.grabButton.setCheckable(True) | |
self.grabButton.toggled.connect(self.startGrab) | |
self.label = QtWidgets.QLabel('Waiting') | |
layout.addWidget(self.label) | |
self.mousePos = None | |
self.fakeCursor = FakeCursor() | |
def startGrab(self, state): | |
if state: | |
# store the cursor position | |
self.mousePos = QtGui.QCursor.pos() | |
self.grabButton.setMouseTracking(True) | |
self.grabButton.grabMouse() | |
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.BlankCursor)) | |
self.fakeCursor.show() | |
self.fakeCursor.move(self.mousePos - self.fakeCursor.rect().center()) | |
self.label.setText('Grabbing') | |
else: | |
self.grabButton.setMouseTracking(False) | |
self.grabButton.releaseMouse() | |
self.grabButton.blockSignals(True) | |
self.grabButton.setChecked(False) | |
self.grabButton.blockSignals(False) | |
QtGui.QCursor.setPos(self.fakeCursor.pos() + self.fakeCursor.rect().center()) | |
self.mousePos = None | |
QtWidgets.QApplication.restoreOverrideCursor() | |
self.fakeCursor.hide() | |
def getPixel(self): | |
desktopId = QtWidgets.QApplication.desktop().winId() | |
screenshot = QtWidgets.QApplication.primaryScreen().grabWindow(desktopId).toImage() | |
pos = self.fakeCursor.pos() + self.fakeCursor.rect().center() | |
pixel = screenshot.pixelColor(pos) | |
self.label.setText('Click on {}x{}\nRGB: {}'.format(pos.x(), pos.y(), pixel.name())) | |
def eventFilter(self, source, event): | |
if source == self.grabButton: | |
if event.type() == QtCore.QEvent.MouseButtonPress and self.grabButton.isChecked(): | |
self.getPixel() | |
elif event.type() == QtCore.QEvent.MouseButtonRelease and event.button() == QtCore.Qt.LeftButton: | |
self.grabButton.toggle() | |
return True | |
elif event.type() == QtCore.QEvent.MouseMove and self.mousePos is not None: | |
# get the difference with the previous mouse position | |
delta = event.globalPos() - self.mousePos | |
screenRect = QtCore.QRect() | |
for screen in QtWidgets.QApplication.screens(): | |
screenRect |= screen.geometry() | |
# set the (possible) geometry using the delta | |
geo = self.fakeCursor.geometry().translated(delta) | |
# ensure that the fake cursor doesn't go outside screen margins | |
center = geo.center() | |
diff = self.fakeCursor.width() // 2 | |
if center.x() > screenRect.right(): | |
geo.moveRight(screenRect.right() + diff) | |
elif center.x() < screenRect.left(): | |
geo.moveLeft(screenRect.left() - diff) | |
if center.y() > screenRect.bottom(): | |
geo.moveBottom(screenRect.bottom() + diff) | |
elif center.y() < screenRect.top(): | |
geo.moveTop(screenRect.top() - diff) | |
# actually move the fake cursor | |
self.fakeCursor.move(geo.topLeft()) | |
# reset the cursor position back to the initial | |
QtGui.QCursor.setPos(self.mousePos) | |
return super().eventFilter(source, event) | |
if __name__ == '__main__': | |
import sys | |
app = QtWidgets.QApplication(sys.argv) | |
w = GrabTest() | |
w.show() | |
sys.exit(app.exec_()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment