首页 > 解决方案 > 如何强制屏幕截图大小比例。PyQt5

问题描述

我想从GitHub/harupy/snipping-tool修改 Screen-Snip 代码,使每个 screen-snip 的比例为 3 x 2。(稍后我将保存为 600 x 400 px 的图像)

我不确定如何 self.end动态修改,以便用户以 3 x 2 的比例单击和拖动。鼠标位置将定义x坐标,y坐标将为int(x * 2/3)

关于如何做到这一点的任何建议?我保证我一直在研究这个,我似乎无法“破解代码”只修改y坐标self.end

这是代码:

import sys
import PyQt5
from PyQt5 import QtWidgets, QtCore, QtGui
import tkinter as tk
from PIL import ImageGrab
import numpy as np
import cv2 # package is officially called opencv-python


class MyWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        root = tk.Tk()
        screen_width = root.winfo_screenwidth()
        screen_height = root.winfo_screenheight()
        self.setGeometry(0, 0, screen_width, screen_height)
        self.setWindowTitle(' ')
        self.begin = QtCore.QPoint()
        self.end = QtCore.QPoint()
        self.setWindowOpacity(0.3)
        QtWidgets.QApplication.setOverrideCursor(
            QtGui.QCursor(QtCore.Qt.CrossCursor)
        )
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        print('Capture the screen...')
        self.show()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.setPen(QtGui.QPen(QtGui.QColor('black'), 3))
        qp.setBrush(QtGui.QColor(128, 128, 255, 128))
        qp.drawRect(QtCore.QRect(self.begin, self.end)) ##### This seems like the place I should modify. #########

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = self.begin
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        self.close()

        x1 = min(self.begin.x(), self.end.x())
        y1 = min(self.begin.y(), self.end.y())
        x2 = max(self.begin.x(), self.end.x())
        y2 = max(self.begin.y(), self.end.y())

        img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
        img.save('capture.png')
        img = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)

        cv2.imshow('Captured Image', img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MyWidget()
    window.show()
    app.aboutToQuit.connect(app.deleteLater)
    sys.exit(app.exec_())

标签: pythonpyqt5

解决方案


您不需要“更改 y 坐标”,只需使用正确的参数来创建矩形。初始化 QRect 有多种方法,您使用两个点,另一种(更常见)是使用原点坐标和矩形大小。

一旦知道宽度,就可以计算高度,如果终点的 y 高于起点,则将其设为负数。

请注意,通过这种方式,您可以获得“负”矩形(负宽度,“右”边缘实际上在左侧,高度/底部相同),因此通常最好使用normalized,这也可以让您获得用于屏幕抓取的矩形的正确坐标。

class MyWidget(QtWidgets.QWidget):
    # ...

    def getRect(self):
        # a commodity function that always return a correctly sized
        # rectangle, with normalized coordinates
        width = self.end.x() - self.begin.x()
        height = abs(width * 2 / 3)
        if self.end.y() < self.begin.y():
            height *= -1
        return QtCore.QRect(self.begin.x(), self.begin.y(), 
            width, height).normalized()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.setPen(QtGui.QPen(QtGui.QColor('black'), 3))
        qp.setBrush(QtGui.QColor(128, 128, 255, 128))
        qp.drawRect(self.getRect())

    def mouseReleaseEvent(self, event):
        self.close()

        rect = self.getRect()
        img = ImageGrab.grab(bbox=(
            rect.topLeft().x(), 
            rect.topLeft().y(), 
            rect.bottomRight().x(), 
            rect.bottomRight().y()
        ))

        # ...

我建议您在某些系统(特别是 Linux)中使用延迟的 setGeometry,“最终”几何实际上仅在从窗口管理器正确映射窗口后才应用,特别是如果窗口管理器倾向于应用几何图形第一次显示窗口时它自己的。例如,我有两个屏幕,您的窗口在我的主屏幕上“居中”,使其移动了另一个屏幕的一半宽度。还要考虑只为屏幕大小导入 Tk 没有多大意义,因为 Qt 已经提供了所有必要的工具。

你可以使用类似的东西:

class MyWidget(QtWidgets.QWidget):
    # ...

    def showEvent(self, event):
        if not event.spontaneous():
            # delay the geometry on the "next" cycle of the Qt event loop;
            # this should take care of positioning issues for systems that
            # try to move newly created windows on their own
            QtCore.QTimer.singleShot(0, self.resetPos)

    def resetPos(self):
        rect = QtCore.QRect()
        # create a rectangle that is the sum of the geometries of all available
        # screens; the |= operator acts as `rect = rect.united(screen.geometry())`
        for screen in QtWidgets.QApplication.screens():
            rect |= screen.geometry()
        self.setGeometry(rect)

推荐阅读