diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/Snapshot/SnapshotFreehandGrabber.py --- a/src/eric7/Snapshot/SnapshotFreehandGrabber.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Snapshot/SnapshotFreehandGrabber.py Wed Jul 13 14:55:47 2022 +0200 @@ -9,8 +9,17 @@ from PyQt6.QtCore import pyqtSignal, Qt, QRect, QPoint, QTimer, QLocale from PyQt6.QtGui import ( - QPixmap, QColor, QRegion, QPainter, QPalette, QPolygon, QPen, QBrush, - QPaintEngine, QGuiApplication, QCursor + QPixmap, + QColor, + QRegion, + QPainter, + QPalette, + QPolygon, + QPen, + QBrush, + QPaintEngine, + QGuiApplication, + QCursor, ) from PyQt6.QtWidgets import QWidget, QToolTip @@ -20,7 +29,7 @@ def drawPolygon(painter, polygon, outline, fill=None): """ Module function to draw a polygon with the given parameters. - + @param painter reference to the painter to be used (QPainter) @param polygon polygon to be drawn (QPolygon) @param outline color of the outline (QColor) @@ -28,9 +37,14 @@ """ clip = QRegion(polygon) clip -= QRegion(polygon) - pen = QPen(outline, 1, Qt.PenStyle.SolidLine, Qt.PenCapStyle.SquareCap, - Qt.PenJoinStyle.BevelJoin) - + pen = QPen( + outline, + 1, + Qt.PenStyle.SolidLine, + Qt.PenCapStyle.SquareCap, + Qt.PenJoinStyle.BevelJoin, + ) + painter.save() painter.setClipRegion(clip) painter.setPen(pen) @@ -45,23 +59,24 @@ class SnapshotFreehandGrabber(QWidget): """ Class implementing a grabber widget for a freehand snapshot region. - + @signal grabbed(QPixmap) emitted after the region was grabbed """ + grabbed = pyqtSignal(QPixmap) - + def __init__(self): """ Constructor """ super().__init__( None, - Qt.WindowType.X11BypassWindowManagerHint | - Qt.WindowType.WindowStaysOnTopHint | - Qt.WindowType.FramelessWindowHint | - Qt.WindowType.Tool + Qt.WindowType.X11BypassWindowManagerHint + | Qt.WindowType.WindowStaysOnTopHint + | Qt.WindowType.FramelessWindowHint + | Qt.WindowType.Tool, ) - + self.__selection = QPolygon() self.__mouseDown = False self.__newSelection = False @@ -71,19 +86,20 @@ self.__dragStartPoint = QPoint() self.__selectionBeforeDrag = QPolygon() self.__locale = QLocale() - + self.__helpTextRect = QRect() self.__helpText = self.tr( "Select a region using the mouse. To take the snapshot," - " press the Enter key or double click. Press Esc to quit.") - + " press the Enter key or double click. Press Esc to quit." + ) + self.__pixmap = QPixmap() self.__pBefore = QPoint() - + self.setMouseTracking(True) - + QTimer.singleShot(200, self.__initialize) - + def __initialize(self): """ Private slot to initialize the rest of the widget. @@ -93,14 +109,16 @@ screen = QGuiApplication.screenAt(QCursor.pos()) geom = screen.geometry() self.__pixmap = screen.grabWindow( - 0, geom.x(), geom.y(), geom.width(), geom.height()) + 0, geom.x(), geom.y(), geom.width(), geom.height() + ) else: # Linux variant # Windows variant screen = QGuiApplication.screens()[0] geom = screen.availableVirtualGeometry() self.__pixmap = screen.grabWindow( - 0, geom.x(), geom.y(), geom.width(), geom.height()) + 0, geom.x(), geom.y(), geom.width(), geom.height() + ) self.resize(self.__pixmap.size()) self.move(geom.x(), geom.y()) self.setCursor(Qt.CursorShape.CrossCursor) @@ -109,41 +127,47 @@ self.grabMouse() self.grabKeyboard() self.activateWindow() - + def paintEvent(self, evt): """ Protected method handling paint events. - + @param evt paint event (QPaintEvent) """ - if self.__grabbing: # grabWindow() should just get the background + if self.__grabbing: # grabWindow() should just get the background return - + painter = QPainter(self) pal = QPalette(QToolTip.palette()) font = QToolTip.font() - - handleColor = pal.color(QPalette.ColorGroup.Active, - QPalette.ColorRole.Highlight) + + handleColor = pal.color( + QPalette.ColorGroup.Active, QPalette.ColorRole.Highlight + ) handleColor.setAlpha(160) overlayColor = QColor(0, 0, 0, 160) - textColor = pal.color(QPalette.ColorGroup.Active, - QPalette.ColorRole.Text) - textBackgroundColor = pal.color(QPalette.ColorGroup.Active, - QPalette.ColorRole.Base) + textColor = pal.color(QPalette.ColorGroup.Active, QPalette.ColorRole.Text) + textBackgroundColor = pal.color( + QPalette.ColorGroup.Active, QPalette.ColorRole.Base + ) painter.drawPixmap(0, 0, self.__pixmap) painter.setFont(font) - + pol = QPolygon(self.__selection) if not self.__selection.boundingRect().isNull(): # Draw outline around selection. # Important: the 1px-wide outline is *also* part of the # captured free-region - pen = QPen(handleColor, 1, Qt.PenStyle.SolidLine, - Qt.PenCapStyle.SquareCap, Qt.PenJoinStyle.BevelJoin) + pen = QPen( + handleColor, + 1, + Qt.PenStyle.SolidLine, + Qt.PenCapStyle.SquareCap, + Qt.PenJoinStyle.BevelJoin, + ) painter.setPen(pen) painter.drawPolygon(pol) - + # Draw the grey area around the selection. grey = QRegion(self.rect()) grey -= QRegion(pol) @@ -153,23 +177,26 @@ painter.drawRect(self.rect()) painter.setClipRect(self.rect()) drawPolygon(painter, pol, handleColor) - + if self.__showHelp: painter.setPen(textColor) painter.setBrush(textBackgroundColor) self.__helpTextRect = painter.boundingRect( self.rect().adjusted(2, 2, -2, -2), - Qt.TextFlag.TextWordWrap, self.__helpText).translated(0, 0) + Qt.TextFlag.TextWordWrap, + self.__helpText, + ).translated(0, 0) self.__helpTextRect.adjust(-2, -2, 4, 2) - drawPolygon(painter, self.__helpTextRect, textColor, - textBackgroundColor) + drawPolygon(painter, self.__helpTextRect, textColor, textBackgroundColor) painter.drawText( self.__helpTextRect.adjusted(3, 3, -3, -3), - Qt.TextFlag.TextWordWrap, self.__helpText) - + Qt.TextFlag.TextWordWrap, + self.__helpText, + ) + if self.__selection.isEmpty(): return - + # The grabbed region is everything which is covered by the drawn # rectangles (border included). This means that there is no 0px # selection, since a 0px wide rectangle will always be drawn as a line. @@ -178,90 +205,86 @@ self.__locale.toString(boundingRect.x()), self.__locale.toString(boundingRect.y()), self.__locale.toString(boundingRect.width()), - self.__locale.toString(boundingRect.height()) + self.__locale.toString(boundingRect.height()), ) - textRect = painter.boundingRect(self.rect(), - Qt.AlignmentFlag.AlignLeft, txt) + textRect = painter.boundingRect(self.rect(), Qt.AlignmentFlag.AlignLeft, txt) boundingRect = textRect.adjusted(-4, 0, 0, 0) - + polBoundingRect = pol.boundingRect() if ( - (textRect.width() < - polBoundingRect.width() - 2 * self.__handleSize) and - (textRect.height() < - polBoundingRect.height() - 2 * self.__handleSize) and - polBoundingRect.width() > 100 and - polBoundingRect.height() > 100 + (textRect.width() < polBoundingRect.width() - 2 * self.__handleSize) + and (textRect.height() < polBoundingRect.height() - 2 * self.__handleSize) + and polBoundingRect.width() > 100 + and polBoundingRect.height() > 100 ): # center, unsuitable for small selections boundingRect.moveCenter(polBoundingRect.center()) textRect.moveCenter(polBoundingRect.center()) elif ( - polBoundingRect.y() - 3 > textRect.height() and - polBoundingRect.x() + textRect.width() < self.rect().width() + polBoundingRect.y() - 3 > textRect.height() + and polBoundingRect.x() + textRect.width() < self.rect().width() ): # on top, left aligned boundingRect.moveBottomLeft( - QPoint(polBoundingRect.x(), polBoundingRect.y() - 3)) + QPoint(polBoundingRect.x(), polBoundingRect.y() - 3) + ) textRect.moveBottomLeft( - QPoint(polBoundingRect.x() + 2, polBoundingRect.y() - 3)) + QPoint(polBoundingRect.x() + 2, polBoundingRect.y() - 3) + ) elif polBoundingRect.x() - 3 > textRect.width(): # left, top aligned boundingRect.moveTopRight( - QPoint(polBoundingRect.x() - 3, polBoundingRect.y())) - textRect.moveTopRight( - QPoint(polBoundingRect.x() - 5, polBoundingRect.y())) + QPoint(polBoundingRect.x() - 3, polBoundingRect.y()) + ) + textRect.moveTopRight(QPoint(polBoundingRect.x() - 5, polBoundingRect.y())) elif ( - (polBoundingRect.bottom() + 3 + textRect.height() < - self.rect().bottom()) and - polBoundingRect.right() > textRect.width() - ): + polBoundingRect.bottom() + 3 + textRect.height() < self.rect().bottom() + ) and polBoundingRect.right() > textRect.width(): # at bottom, right aligned boundingRect.moveTopRight( - QPoint(polBoundingRect.right(), polBoundingRect.bottom() + 3)) + QPoint(polBoundingRect.right(), polBoundingRect.bottom() + 3) + ) textRect.moveTopRight( - QPoint(polBoundingRect.right() - 2, - polBoundingRect.bottom() + 3)) - elif ( - polBoundingRect.right() + textRect.width() + 3 < - self.rect().width() - ): + QPoint(polBoundingRect.right() - 2, polBoundingRect.bottom() + 3) + ) + elif polBoundingRect.right() + textRect.width() + 3 < self.rect().width(): # right, bottom aligned boundingRect.moveBottomLeft( - QPoint(polBoundingRect.right() + 3, polBoundingRect.bottom())) + QPoint(polBoundingRect.right() + 3, polBoundingRect.bottom()) + ) textRect.moveBottomLeft( - QPoint(polBoundingRect.right() + 5, polBoundingRect.bottom())) - + QPoint(polBoundingRect.right() + 5, polBoundingRect.bottom()) + ) + # If the above didn't catch it, you are running on a very # tiny screen... drawPolygon(painter, boundingRect, textColor, textBackgroundColor) painter.drawText(textRect, Qt.AlignmentFlag.AlignHCenter, txt) - + if ( - (polBoundingRect.height() > self.__handleSize * 2 and - polBoundingRect.width() > self.__handleSize * 2) or - not self.__mouseDown - ): + polBoundingRect.height() > self.__handleSize * 2 + and polBoundingRect.width() > self.__handleSize * 2 + ) or not self.__mouseDown: painter.setBrush(Qt.GlobalColor.transparent) painter.setClipRegion(QRegion(pol)) painter.drawPolygon(QPolygon(self.rect())) - + def mousePressEvent(self, evt): """ Protected method to handle mouse button presses. - + @param evt mouse press event (QMouseEvent) """ self.__pBefore = evt.position().toPoint() - - self.__showHelp = not self.__helpTextRect.contains( - evt.position().toPoint()) + + self.__showHelp = not self.__helpTextRect.contains(evt.position().toPoint()) if evt.button() == Qt.MouseButton.LeftButton: self.__mouseDown = True self.__dragStartPoint = evt.position().toPoint() self.__selectionBeforeDrag = QPolygon(self.__selection) - if not self.__selection.containsPoint(evt.position().toPoint(), - Qt.FillRule.WindingFill): + if not self.__selection.containsPoint( + evt.position().toPoint(), Qt.FillRule.WindingFill + ): self.__newSelection = True self.__selection = QPolygon() else: @@ -271,19 +294,18 @@ self.__selection = QPolygon() self.setCursor(Qt.CursorShape.CrossCursor) self.update() - + def mouseMoveEvent(self, evt): """ Protected method to handle mouse movements. - + @param evt mouse move event (QMouseEvent) """ - shouldShowHelp = not self.__helpTextRect.contains( - evt.position().toPoint()) + shouldShowHelp = not self.__helpTextRect.contains(evt.position().toPoint()) if shouldShowHelp != self.__showHelp: self.__showHelp = shouldShowHelp self.update() - + if self.__mouseDown: if self.__newSelection: p = evt.position().toPoint() @@ -294,43 +316,45 @@ self.__pBefore = evt.position().toPoint() # save position for next iteration self.__selection.translate(p) - + self.update() else: if self.__selection.boundingRect().isEmpty(): return - - if self.__selection.containsPoint(evt.position().toPoint(), - Qt.FillRule.WindingFill): + + if self.__selection.containsPoint( + evt.position().toPoint(), Qt.FillRule.WindingFill + ): self.setCursor(Qt.CursorShape.OpenHandCursor) else: self.setCursor(Qt.CursorShape.CrossCursor) - + def mouseReleaseEvent(self, evt): """ Protected method to handle mouse button releases. - + @param evt mouse release event (QMouseEvent) """ self.__mouseDown = False self.__newSelection = False - if self.__selection.containsPoint(evt.position().toPoint(), - Qt.FillRule.WindingFill): + if self.__selection.containsPoint( + evt.position().toPoint(), Qt.FillRule.WindingFill + ): self.setCursor(Qt.CursorShape.OpenHandCursor) self.update() - + def mouseDoubleClickEvent(self, evt): """ Protected method to handle mouse double clicks. - + @param evt mouse double click event (QMouseEvent) """ self.__grabRegion() - + def keyPressEvent(self, evt): """ Protected method to handle key presses. - + @param evt key press event (QKeyEvent) """ if evt.key() == Qt.Key.Key_Escape: @@ -339,7 +363,7 @@ self.__grabRegion() else: evt.ignore() - + def __grabRegion(self): """ Private method to grab the selected region (i.e. do the snapshot). @@ -347,34 +371,31 @@ pol = QPolygon(self.__selection) if not pol.isEmpty(): self.__grabbing = True - + xOffset = self.__pixmap.rect().x() - pol.boundingRect().x() yOffset = self.__pixmap.rect().y() - pol.boundingRect().y() translatedPol = pol.translated(xOffset, yOffset) - + pixmap2 = QPixmap(pol.boundingRect().size()) pixmap2.fill(Qt.GlobalColor.transparent) - + pt = QPainter() pt.begin(pixmap2) - if pt.paintEngine().hasFeature( - QPaintEngine.PaintEngineFeature.PorterDuff - ): + if pt.paintEngine().hasFeature(QPaintEngine.PaintEngineFeature.PorterDuff): pt.setRenderHints( - QPainter.RenderHint.Antialiasing | - QPainter.RenderHint.SmoothPixmapTransform, - True) + QPainter.RenderHint.Antialiasing + | QPainter.RenderHint.SmoothPixmapTransform, + True, + ) pt.setBrush(Qt.GlobalColor.black) pt.setPen(QPen(QBrush(Qt.GlobalColor.black), 0.5)) pt.drawPolygon(translatedPol) - pt.setCompositionMode( - QPainter.CompositionMode.CompositionMode_SourceIn) + pt.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceIn) else: pt.setClipRegion(QRegion(translatedPol)) - pt.setCompositionMode( - QPainter.CompositionMode.CompositionMode_Source) - + pt.setCompositionMode(QPainter.CompositionMode.CompositionMode_Source) + pt.drawPixmap(pixmap2.rect(), self.__pixmap, pol.boundingRect()) pt.end() - + self.grabbed.emit(pixmap2)