diff -r 3fc8dfeb6ebe -r b99e7fd55fd3 src/eric7/Snapshot/SnapshotDefaultGrabber.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/Snapshot/SnapshotDefaultGrabber.py Thu Jul 07 11:23:56 2022 +0200 @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a grabber object for non-Wayland desktops. +""" + +from PyQt6.QtCore import pyqtSignal, Qt, QObject, QTimer, QEvent +from PyQt6.QtGui import QPixmap, QCursor, QGuiApplication +from PyQt6.QtWidgets import QWidget + +from .SnapshotModes import SnapshotModes + +import Globals + + +class SnapshotDefaultGrabber(QObject): + """ + Class implementing a grabber object for non-Wayland desktops. + + @signal grabbed(QPixmap) emitted after the grab operation is finished + """ + grabbed = pyqtSignal(QPixmap) + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent object + @type QObject + """ + super().__init__(parent) + + self.__grabber = None + self.__grabberWidget = QWidget( + None, Qt.WindowType.X11BypassWindowManagerHint) + self.__grabberWidget.move(-10000, -10000) + self.__grabberWidget.installEventFilter(self) + + from .SnapshotTimer import SnapshotTimer + self.__grabTimer = SnapshotTimer() + self.__grabTimer.timeout.connect(self.__grabTimerTimeout) + + def supportedModes(self): + """ + Public method to get the supported screenshot modes. + + @return tuple of supported screenshot modes + @rtype tuple of SnapshotModes + """ + return ( + SnapshotModes.FULLSCREEN, + SnapshotModes.SELECTEDSCREEN, + SnapshotModes.RECTANGLE, + SnapshotModes.FREEHAND, + SnapshotModes.ELLIPSE, + ) + + def grab(self, mode, delay=0, captureCursor=False, + captureDecorations=False): + """ + Public method to perform a grab operation potentially after a delay. + + @param mode screenshot mode + @type ScreenshotModes + @param delay delay in seconds + @type int + @param captureCursor flag indicating to include the mouse cursor + (not used) + @type bool + @param captureDecorations flag indicating to include the window + decorations (not used) + @type bool + """ + self.__mode = mode + if delay: + self.__grabTimer.start(delay) + else: + QTimer.singleShot(200, self.__startUndelayedGrab) + + def __grabTimerTimeout(self): + """ + Private slot to perform a delayed grab operation. + """ + if self.__mode == SnapshotModes.RECTANGLE: + self.__grabRectangle() + elif self.__mode == SnapshotModes.ELLIPSE: + self.__grabEllipse() + elif self.__mode == SnapshotModes.FREEHAND: + self.__grabFreehand() + else: + self.__performGrab(self.__mode) + + def __startUndelayedGrab(self): + """ + Private slot to perform an undelayed grab operation. + """ + if self.__mode == SnapshotModes.RECTANGLE: + self.__grabRectangle() + elif self.__mode == SnapshotModes.ELLIPSE: + self.__grabEllipse() + elif self.__mode == SnapshotModes.FREEHAND: + self.__grabFreehand() + else: + if Globals.isMacPlatform(): + self.__performGrab(self.__mode) + else: + self.__grabberWidget.show() + self.__grabberWidget.grabMouse(Qt.CursorShape.CrossCursor) + + def __grabRectangle(self): + """ + Private method to grab a rectangular screen region. + """ + from .SnapshotRegionGrabber import SnapshotRegionGrabber + self.__grabber = SnapshotRegionGrabber( + mode=SnapshotRegionGrabber.Rectangle) + self.__grabber.grabbed.connect(self.__captured) + + def __grabEllipse(self): + """ + Private method to grab an elliptical screen region. + """ + from .SnapshotRegionGrabber import SnapshotRegionGrabber + self.__grabber = SnapshotRegionGrabber( + mode=SnapshotRegionGrabber.Ellipse) + self.__grabber.grabbed.connect(self.__captured) + + def __grabFreehand(self): + """ + Private method to grab a non-rectangular screen region. + """ + from .SnapshotFreehandGrabber import SnapshotFreehandGrabber + self.__grabber = SnapshotFreehandGrabber() + self.__grabber.grabbed.connect(self.__captured) + + def __performGrab(self, mode): + """ + Private method to perform a screen grab other than a selected region. + + @param mode screenshot mode + @type SnapshotModes + """ + self.__grabberWidget.releaseMouse() + self.__grabberWidget.hide() + self.__grabTimer.stop() + + if mode == SnapshotModes.FULLSCREEN: + screen = QGuiApplication.screens()[0] + vgeom = screen.availableVirtualGeometry() + snapshot = screen.grabWindow( + 0, vgeom.x(), vgeom.y(), vgeom.width(), vgeom.height()) + elif mode == SnapshotModes.SELECTEDSCREEN: + screen = QGuiApplication.screenAt(QCursor.pos()) + sgeom = screen.geometry() + if Globals.isMacPlatform(): + # macOS variant + snapshot = screen.grabWindow( + 0, sgeom.x(), sgeom.y(), sgeom.width(), sgeom.height() + ) + else: + # Linux variant + # Windows variant + snapshot = screen.grabWindow( + 0, 0, 0, sgeom.width(), sgeom.height()) + else: + snapshot = QPixmap() + + self.grabbed.emit(snapshot) + + def __captured(self, pixmap): + """ + Private slot to show a preview of the snapshot. + + @param pixmap pixmap of the snapshot (QPixmap) + """ + self.__grabber.close() + snapshot = QPixmap(pixmap) + + self.__grabber.grabbed.disconnect(self.__captured) + self.__grabber = None + + self.grabbed.emit(snapshot) + + def eventFilter(self, obj, evt): + """ + Public method to handle event for other objects. + + @param obj reference to the object (QObject) + @param evt reference to the event (QEvent) + @return flag indicating that the event should be filtered out (boolean) + """ + if ( + obj == self.__grabberWidget and + evt.type() == QEvent.Type.MouseButtonPress + ): + if QWidget.mouseGrabber() != self.__grabberWidget: + return False + if evt.button() == Qt.MouseButton.LeftButton: + self.__performGrab(self.__mode) + + return False