凌的博客

您现在的位置是: 首页 > 学无止境 > python > 

python

PyQt5 屏幕截图

2019-09-17 python 1714
from PyQt5.QtWidgets import QApplication,QWidget
from PyQt5.QtCore import Qt,QPoint,QDateTime,QRectF,QRect
from PyQt5.QtGui import QPainter,QPalette,QPixmap,QColor,QBrush,QFont

import sys


def toRectF(rect):
    return QRectF(
        rect.x(),
        rect.y(),
        rect.width(),
        rect.height()
    )


def toRect(rectF):
    return QRect(
        rectF.x(),
        rectF.y(),
        rectF.width(),
        rectF.height()
    )


def normalizeRect(rect):
    x = rect.x()
    y = rect.y()
    w = rect.width()
    h = rect.height()
    if w < 0:
        x = x + w
        w = -w
    if h < 0:
        y = y + h
        h = -h
    return QRectF(x, y, w, h)


class WScreenshot(QWidget):
    u"""
    截图
    """

    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)

        self.saveDir = u'D:/'
        self.saveName = u'W截图-{}'.format(QDateTime().currentDateTime().toString("yyyyMMdd-hh-mm-ss"))
        self.saveFormat = 'JPG'
        self.picQuality = 100

        self.setWindowTitle(u'截图窗体')
        self.showFullScreen()  # 全屏显示
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        # 屏幕 和 屏幕信息
        self.screen = QApplication.primaryScreen().grabWindow(QApplication.desktop().winId())
        self.screenSize = self.screen.rect().size()
        self.screenRect = toRectF(self.screen.rect())
        # -点
        self.screenTopLeft = self.screenRect.topLeft()
        self.screenBottomLeft = self.screenRect.bottomLeft()
        self.screenTopRight = self.screenRect.topRight()
        self.screenBottomRight = self.screenRect.bottomRight()
        # -上下左右限
        self.screenLeft = self.screenRect.left()
        self.screenRight = self.screenRect.right()
        self.screenTop = self.screenRect.top()
        self.screenBottom = self.screenRect.bottom()

        # A:start(x,y)        D:(x+w,y)
        #     -----------------
        #     |               |
        #     |               |
        #     -----------------
        # B:(x,y+h)           C:end(x+w,y+h)

        # 设置 self.screen 为窗口背景
        palette = QPalette()
        palette.setBrush(QPalette.Background, QBrush(self.screen))
        self.setPalette(palette)

        # 调节器层
        self.adjustment_original = QPixmap(self.screenSize)  # 初始调节器
        self.adjustment_original.fill(QColor(0, 0, 0, 64))
        self.adjustment = QPixmap()  # 调节器
        # self.adjustment = self.adjustment_original.copy()  # 调节器

        # 画布层
        self.canvas_original = QPixmap(self.screenSize)  # 初始画布
        self.canvas_original.fill(Qt.transparent)
        self.canvas_saved = self.canvas_original.copy()  # 保存已经画好的图案
        self.canvas = QPixmap()  # 画布

        # self.canvas = self.canvas_original.copy()  # 画布
        # self.canvas_saved = self.canvas.copy()
        # 输出
        self.output = QPixmap()

        # 当前功能状态
        self.isMasking = False
        self.hasMask = False
        self.isMoving = False
        self.isAdjusting = False
        self.isDrawing = False
        self.hasPattern = False
        self.mousePos = ''
        self.isShifting = False

        # 蒙版 和 蒙版信息
        self.maskRect = QRectF()
        self.maskRect_backup = QRectF()

        # 以下 16 个变量随self.maskRect变化而变化
        self.maskTopLeft = QPoint()
        self.maskBottomLeft = QPoint()
        self.maskTopRight = QPoint()
        self.maskBottomRight = QPoint()
        self.maskTopMid = QPoint()
        self.maskBottomMid = QPoint()
        self.maskLeftMid = QPoint()
        self.maskRightMid = QPoint()

        self.rectTopLeft = QRectF()
        self.rectBottomLeft = QRectF()
        self.rectTopRight = QRectF()
        self.rectBottomRight = QRectF()
        self.rectTop = QRectF()
        self.rectBottom = QRectF()
        self.rectLeft = QRectF()
        self.rectRight = QRectF()

        self.adjustmentLineWidth = 2
        self.adjustmentWhiteDotRadius = 6
        self.adjustmentBlueDotRadius = 4
        self.blue = QColor(30, 120, 255)
        self.setCursor(Qt.CrossCursor)  # 设置鼠标样式 十字

        self.setMouseTracking(True)

        # 鼠标事件点
        self.start = QPoint()
        self.end = QPoint()

        # self.test()

    def test(self):
        self.hasMask = True
        self.isMasking = True
        self.maskRect = QRectF(100, 100, 600, 800)
        self.updateMaskInfo()
        self.update()

    def toMask(self):
        rect = QRectF(self.start, self.end)

        if self.isShifting:
            x = rect.x()
            y = rect.y()
            w = rect.width()
            h = rect.height()
            absW = abs(w)
            absH = abs(h)
            wIsLonger = True if absW > absH else False
            if w > 0:
                if h > 0:
                    end = QPoint(x + absW, y + absW) if wIsLonger else QPoint(x + absH, y + absH)
                else:
                    end = QPoint(x + absW, y - absW) if wIsLonger else QPoint(x + absH, y - absH)
            else:
                if h > 0:
                    end = QPoint(x - absW, y + absW) if wIsLonger else QPoint(x - absH, y + absH)
                else:
                    end = QPoint(x - absW, y - absW) if wIsLonger else QPoint(x - absH, y - absH)

            rect = QRectF(self.start, end)

        self.maskRect = QRectF(
            rect.x() + min(rect.width(), 0),
            rect.y() + min(rect.height(), 0),
            abs(rect.width()),
            abs(rect.height())
        )

        # 修正超出屏幕、碰撞
        if self.isShifting:
            self.fixCollision()

        self.updateMaskInfo()
        self.update()

    # 修复碰撞。针对 isShifting 的情况
    def fixCollision(self):
        vector = self.end - self.start
        vX = vector.x()
        vY = vector.y()
        resStart = self.maskRect.topLeft()
        resEnd = self.maskRect.bottomRight()
        mLeft = self.maskRect.left()
        mRight = self.maskRect.right()
        mTop = self.maskRect.top()
        mBottom = self.maskRect.bottom()
        # w < h
        if self.maskRect.left() <= self.screenLeft:
            newW = mRight - self.screenLeft
            if vY > 0:
                resStart = QPoint(self.screenLeft, mTop)
                resEnd = resStart + QPoint(newW, newW)
            else:
                resStart = resEnd + QPoint(-newW, -newW)
        elif self.maskRect.right() >= self.screenRight:
            newW = self.screenRight - mLeft
            if vY > 0:
                resEnd = resStart + QPoint(newW, newW)
            else:
                resEnd = QPoint(self.screenRight, mBottom)
                resStart = resEnd + QPoint(-newW, -newW)
        # w > h
        elif self.maskRect.top() <= self.screenTop:
            newW = mBottom - self.screenTop
            if vX > 0:
                resStart = QPoint(mLeft, self.screenTop)
                resEnd = resStart + QPoint(newW, newW)
            else:
                resStart = resEnd + QPoint(-newW, -newW)
        elif self.maskRect.bottom() >= self.screenBottom:
            newW = self.screenBottom - mTop
            if vX > 0:
                resEnd = resStart + QPoint(newW, newW)
            else:
                resEnd = QPoint(mRight, self.screenBottom)
                resStart = resEnd + QPoint(-newW, -newW)
        self.maskRect = QRectF(resStart, resEnd)

    def toAdjust(self):

        mRect = self.maskRect_backup
        mStart = mRect.topLeft()
        mStartX = mStart.x()
        mStartY = mStart.y()
        mEnd = mRect.bottomRight()
        mEndX = mEnd.x()
        mEndY = mEnd.y()
        resStart = mStart
        resEnd = mEnd
        if not self.isShifting:

            if self.mousePos == 'TL':
                resStart = self.end
            elif self.mousePos == 'BL':
                resStart = QPoint(self.end.x(), mStartY)
                resEnd = QPoint(mEndX, self.end.y())
            elif self.mousePos == 'TR':
                resStart = QPoint(mStartX, self.end.y())
                resEnd = QPoint(self.end.x(), mEndY)
            elif self.mousePos == 'BR':
                resEnd = self.end
            elif self.mousePos == 'T':
                resStart = QPoint(mStartX, self.end.y())
            elif self.mousePos == 'B':
                resEnd = QPoint(mEndX, self.end.y())
            elif self.mousePos == 'L':
                resStart = QPoint(self.end.x(), mStartY)
            elif self.mousePos == 'R':
                resEnd = QPoint(self.end.x(), mEndY)
        else:
            print(self.mousePos)
            if self.mousePos == 'T':
                resStart = QPoint(mStartX, self.end.y())
                newW = mEndY - self.end.y()
                resEnd = resStart + QPoint(newW, newW)
            elif self.mousePos == 'B':
                newW = self.end.y() - mStartY
                resEnd = resStart + QPoint(newW, newW)
            elif self.mousePos == 'L':
                resStart = QPoint(self.end.x(), mStartY)
                newW = mEndX - self.end.x()
                resEnd = resStart + QPoint(newW, newW)
            elif self.mousePos == 'R':
                newW = self.end.x() - mStartX
                resEnd = resStart + QPoint(newW, newW)
            elif self.mousePos == 'TL':
                newW = mEndX - self.end.x()
                newH = mEndY - self.end.y()
                newW = newW if newW > newH else newH
                resStart = resEnd + QPoint(-newW, -newW)
            elif self.mousePos == 'BR':
                newW = self.end.x() - mStartX
                newH = self.end.y() - mStartY
                newW = newW if newW > newH else newH
                resEnd = resStart + QPoint(newW, newW)
            elif self.mousePos == 'TR':
                newW = self.end.x() - mStartX
                newH = mEndY - self.end.y()
                newW = newW if newW > newH else newH
                resStart = mRect.bottomLeft()
                resEnd = resStart + QPoint(newW, -newW)
            elif self.mousePos == 'BL':
                newW = mEndX - self.end.x()
                newH = self.end.y() - mStartY
                newW = newW if newW > newH else newH
                resStart = mRect.topRight()
                resEnd = resStart + QPoint(-newW, newW)

        self.maskRect = normalizeRect(QRectF(resStart, resEnd))

        self.fixCollision()

        self.updateMaskInfo()
        self.update()

    def toMove(self):
        mStart = self.maskRect_backup.topLeft()
        mStartX = mStart.x()
        mStartY = mStart.y()
        mEnd = self.maskRect_backup.bottomRight()
        mEndX = mEnd.x()
        mEndY = mEnd.y()
        mWidth = self.maskRect_backup.width()
        mHeight = self.maskRect_backup.height()
        mWHPoint = QPoint(mWidth, mHeight)
        vector = self.end - self.start
        vX = vector.x()
        vY = vector.y()

        resStart = mStart + vector
        resStartX = resStart.x()
        resStartY = resStart.y()
        resEnd = mEnd + vector
        resEndX = resEnd.x()
        resEndY = resEnd.y()

        if resStartX <= self.screenLeft and resStartY <= self.screenTop:
            resStart = self.screenTopLeft
            resEnd = resStart + mWHPoint
        elif resEndX >= self.screenRight and resEndY >= self.screenBottom:
            resEnd = self.screenBottomRight
            resStart = resEnd - mWHPoint
        elif resStartX <= self.screenLeft and resEndY >= self.screenBottom:
            resStart = QPoint(self.screenLeft, self.screenBottom - mHeight)
            resEnd = resStart + mWHPoint
        elif resEndX >= self.screenRight and resStartY <= self.screenTop:
            resStart = QPoint(self.screenRight - mWidth, self.screenTop)
            resEnd = resStart + mWHPoint
        elif resStartX <= self.screenLeft:
            resStart = QPoint(self.screenLeft, mStartY + vY)
            resEnd = resStart + mWHPoint
        elif resStartY <= self.screenTop:
            resStart = QPoint(mStartX + vX, self.screenTop)
            resEnd = resStart + mWHPoint
        elif resEndX >= self.screenRight:
            resEnd = QPoint(self.screenRight, mEndY + vY)
            resStart = resEnd - mWHPoint
        elif resEndY >= self.screenBottom:
            resEnd = QPoint(mEndX + vX, self.screenBottom)
            resStart = resEnd - mWHPoint
        self.maskRect = normalizeRect(QRectF(resStart, resEnd))
        self.updateMaskInfo()
        self.update()

    def mousePressEvent(self, QMouseEvent):
        if QMouseEvent.button() == Qt.LeftButton:
            self.start = QMouseEvent.pos()
            self.end = self.start
            if self.hasMask:
                self.maskRect_backup = self.maskRect
                if self.mousePos == 'mask':
                    self.isMoving = True
                else:
                    self.isAdjusting = True
            else:
                self.isMasking = True

        if QMouseEvent.button() == Qt.RightButton:
            if self.isMasking or self.hasMask:
                self.isMasking = False
                self.hasMask = False
                self.maskRect = QRectF(0, 0, 0, 0)
                self.updateMaskInfo()
                self.update()
            else:
                self.close()

    def mouseReleaseEvent(self, QMouseEvent):
        if QMouseEvent.button() == Qt.LeftButton:
            self.isMasking = False
            self.isMoving = False
            self.isAdjusting = False
            self.isDrawing = False

    def mouseDoubleClickEvent(self, QMouseEvent):
        if QMouseEvent.button() == Qt.LeftButton:
            if self.mousePos == 'mask':
                self.save()
                self.close()

    def mouseMoveEvent(self, QMouseEvent):
        pos = QMouseEvent.pos()
        self.end = pos

        if self.isMasking:
            self.hasMask = True
            self.toMask()
        elif self.isMoving:
            self.toMove()
        elif self.isAdjusting:
            self.toAdjust()

        # 设置鼠标样式
        if self.isDrawing:
            pass
        else:
            if self.hasMask:
                if self.isMoving:
                    self.setCursor(Qt.SizeAllCursor)
                if self.isAdjusting:
                    pass
                else:
                    self.setMouseShape(pos)
            else:
                self.mousePos = ''
                self.setCursor(Qt.CrossCursor)  # 设置鼠标样式 十字

    def setMouseShape(self, pos):
        # 设置鼠标样式
        if self.rectTopLeft.contains(pos):
            self.setCursor(Qt.SizeFDiagCursor)
            self.mousePos = 'TL'
        elif self.rectBottomLeft.contains(pos):
            self.setCursor(Qt.SizeBDiagCursor)
            self.mousePos = 'BL'
        elif self.rectBottomRight.contains(pos):
            self.setCursor(Qt.SizeFDiagCursor)
            self.mousePos = 'BR'
        elif self.rectTopRight.contains(pos):
            self.setCursor(Qt.SizeBDiagCursor)
            self.mousePos = 'TR'
        elif self.rectLeft.contains(pos):
            self.setCursor(Qt.SizeHorCursor)
            self.mousePos = 'L'
        elif self.rectTop.contains(pos):
            self.setCursor(Qt.SizeVerCursor)
            self.mousePos = 'T'
        elif self.rectBottom.contains(pos):
            self.setCursor(Qt.SizeVerCursor)
            self.mousePos = 'B'
        elif self.rectRight.contains(pos):
            self.setCursor(Qt.SizeHorCursor)
            self.mousePos = 'R'
        elif self.maskRect.contains(pos):
            self.setCursor(Qt.SizeAllCursor)
            self.mousePos = 'mask'

    def updateMaskInfo(self):
        # 蒙版点
        self.maskTopLeft = self.maskRect.topLeft()
        self.maskBottomLeft = self.maskRect.bottomLeft()
        self.maskTopRight = self.maskRect.topRight()
        self.maskBottomRight = self.maskRect.bottomRight()
        self.maskTopMid = (self.maskTopLeft + self.maskTopRight) / 2
        self.maskBottomMid = (self.maskBottomLeft + self.maskBottomRight) / 2
        self.maskLeftMid = (self.maskTopLeft + self.maskBottomLeft) / 2
        self.maskRightMid = (self.maskTopRight + self.maskBottomRight) / 2
        # 除蒙版区外的 8 个区域
        self.rectTopLeft = QRectF(self.screenTopLeft, self.maskTopLeft)
        self.rectBottomLeft = QRectF(self.screenBottomLeft, self.maskBottomLeft)
        self.rectTopRight = QRectF(self.screenTopRight, self.maskTopRight)
        self.rectBottomRight = QRectF(self.screenBottomRight, self.maskBottomRight)
        self.rectTop = QRectF(QPoint(self.maskRect.left(), self.screenTop), self.maskTopRight)
        self.rectBottom = QRectF(self.maskBottomLeft, QPoint(self.maskRect.right(), self.screenBottom))
        self.rectLeft = QRectF(QPoint(self.screenLeft, self.maskRect.top()), self.maskBottomLeft)
        self.rectRight = QRectF(self.maskTopRight, QPoint(self.screenRight, self.maskRect.bottom()))

    def paintEvent(self, QPaintEvent):

        painter = QPainter()

        # 开始在 画布层 上绘画。如果正在绘画,绘制图案, 否则不绘制
        if self.isDrawing:
            if self.hasPattern:
                self.canvas = self.canvas_saved.copy()
            else:
                self.canvas = self.canvas_original.copy()
            painter.begin(self.canvas)
            self.paintCanvas(painter)
            painter.end()
            # 把 画布层 绘画到窗口上
            painter.begin(self)
            painter.drawPixmap(self.screenRect, self.canvas)
            painter.end()

        # 开始在 空白调节器层 上绘画。如果有蒙版,绘制调节器形状, 否则不绘制
        else:
            self.adjustment = self.adjustment_original.copy()
            painter.begin(self.adjustment)
            self.paintAdjustment(painter)
            painter.end()
            # 把 调节器层 绘画到窗口上
            painter.begin(self)
            painter.drawPixmap(toRect(self.screenRect), self.adjustment)
            painter.end()

    def paintAdjustment(self, painter):
        if self.hasMask:
            painter.setRenderHint(QPainter.Antialiasing, True)  # 反走样
            painter.setPen(Qt.NoPen)
            # 在蒙版区绘制屏幕背景
            painter.setBrush(QBrush(self.screen))
            painter.drawRect(self.maskRect)
            # 绘制线框
            lineWidth = self.adjustmentLineWidth
            painter.setBrush(self.blue)
            painter.drawRect(
                QRectF(
                    self.maskTopLeft + QPoint(-lineWidth, -lineWidth),
                    self.maskTopRight + QPoint(lineWidth, 0))
            )
            painter.drawRect(
                QRectF(
                    self.maskBottomLeft + QPoint(-lineWidth, 0),
                    self.maskBottomRight + QPoint(lineWidth, lineWidth)
                )
            )
            painter.drawRect(
                QRectF(
                    self.maskTopLeft + QPoint(-lineWidth, -lineWidth),
                    self.maskBottomLeft + QPoint(0, lineWidth)
                )
            )
            painter.drawRect(
                QRectF(
                    self.maskTopRight + QPoint(0, -lineWidth),
                    self.maskBottomRight + QPoint(lineWidth, lineWidth)
                )
            )
            if self.maskRect.width() > 150 and self.maskRect.height() > 150:
                # 绘制点
                points = [
                    self.maskTopLeft, self.maskTopRight, self.maskBottomLeft, self.maskBottomRight,
                    self.maskLeftMid, self.maskRightMid, self.maskTopMid, self.maskBottomMid
                ]
                # -白点
                whiteDotRadiusPoint = QPoint(self.adjustmentWhiteDotRadius, self.adjustmentWhiteDotRadius)
                painter.setBrush(Qt.white)
                for point in points:
                    painter.drawEllipse(QRectF(point - whiteDotRadiusPoint, point + whiteDotRadiusPoint))
                # -蓝点
                blueDotRadius = QPoint(self.adjustmentBlueDotRadius, self.adjustmentBlueDotRadius)
                painter.setBrush(self.blue)
                for point in points:
                    painter.drawEllipse(QRectF(point - blueDotRadius, point + blueDotRadius))

            # 绘制尺寸
            maskSize = (abs(int(self.maskRect.width())), abs(int(self.maskRect.height())))
            painter.setFont(QFont('Monaco', 7, QFont.Bold))
            painter.setPen(Qt.transparent)  # 透明获得字体Rect
            textRect = painter.drawText(
                QRectF(self.maskTopLeft.x() + 10, self.maskTopLeft.y() - 25, 100, 20),
                Qt.AlignLeft | Qt.AlignVCenter,
                '{} x {}'.format(*maskSize)
            )
            painter.setBrush(QColor(0, 0, 0, 128))  # 黑底
            padding = 5
            painter.drawRect(
                QRectF(
                    textRect.x() - padding,
                    textRect.y() - padding * 0.4,
                    textRect.width() + padding * 2,
                    textRect.height() + padding * 0.4
                )
            )
            painter.setPen(Qt.white)
            painter.drawText(
                textRect,
                Qt.AlignLeft | Qt.AlignVCenter,
                '{} x {}'.format(*maskSize)
            )
            painter.setPen(Qt.NoPen)

    def paintCanvas(self, painter):
        pass

    def keyPressEvent(self, QKeyEvent):
        if QKeyEvent.key() == Qt.Key_Escape:
            self.close()
        if QKeyEvent.key() == Qt.Key_Return or QKeyEvent.key() == Qt.Key_Enter:  # 大键盘、小键盘回车
            if self.hasMask:
                self.save()
            self.close()
        if QKeyEvent.modifiers() & Qt.ShiftModifier:
            self.isShifting = True

    def keyReleaseEvent(self, QKeyEvent):
        if QKeyEvent.key() == Qt.Key_Shift:
            self.isShifting = False

    def save(self):
        self.output = self.screen.copy()
        if self.hasPattern:
            painter = QPainter(self.output)
            painter.drawPixmap(self.canvas)
        self.output = self.output.copy(toRect(self.maskRect))

        self.output.save(
            u'{saveDir}/{saveName}.{format}'.format(
                saveDir=self.saveDir.rstrip('/'),
                saveName=self.saveName,
                format=self.saveFormat.lower()
            ),
            self.saveFormat,
            self.picQuality
        )

if __name__ == "__main__":
    app = QApplication.instance() or QApplication(sys.argv)
    win = WScreenshot()
    win.show()
    app.exec_()

2.png

文章评论

0条评论