PyQT制作自己的录屏软件(完整版全屏录制+自由选择区域录制)

594 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情

之前的博客中制作了一款录屏软件,但是只能录制全屏,然而市面上许多录屏软件还能选择区域进行录制,感觉两者差不多,难度相差也不大,于是更新一下之前的代码,让这款录屏软件既可以全屏录制,也可以自由选择区域进行录制。

首先,界面相比于之前肯定要做一下调整,调整后的界面如下。

image.png

界面文件如下

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'screenCapture.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(231, 210)
        self.horizontalLayoutWidget = QtWidgets.QWidget(Form)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 211, 81))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.pushButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        self.pushButton_2 = QtWidgets.QPushButton(self.horizontalLayoutWidget)
        self.pushButton_2.setObjectName("pushButton_2")
        self.verticalLayout.addWidget(self.pushButton_2)
        self.label = QtWidgets.QLabel(self.horizontalLayoutWidget)
        self.label.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.horizontalLayout.addLayout(self.verticalLayout)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.pushButton_3 = QtWidgets.QPushButton(self.horizontalLayoutWidget)
        self.pushButton_3.setObjectName("pushButton_3")
        self.verticalLayout_2.addWidget(self.pushButton_3)
        self.pushButton_4 = QtWidgets.QPushButton(self.horizontalLayoutWidget)
        self.pushButton_4.setObjectName("pushButton_4")
        self.verticalLayout_2.addWidget(self.pushButton_4)
        self.label_3 = QtWidgets.QLabel(self.horizontalLayoutWidget)
        self.label_3.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.label_3.setAlignment(QtCore.Qt.AlignCenter)
        self.label_3.setObjectName("label_3")
        self.verticalLayout_2.addWidget(self.label_3)
        self.horizontalLayout.addLayout(self.verticalLayout_2)
        self.verticalLayoutWidget_3 = QtWidgets.QWidget(Form)
        self.verticalLayoutWidget_3.setGeometry(QtCore.QRect(10, 90, 211, 91))
        self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3)
        self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.label_2 = QtWidgets.QLabel(self.verticalLayoutWidget_3)
        self.label_2.setText("")
        self.label_2.setObjectName("label_2")
        self.verticalLayout_3.addWidget(self.label_2)

        self.retranslateUi(Form)
        self.pushButton.clicked.connect(Form.startCapture) # type: ignore
        self.pushButton_2.clicked.connect(Form.stopCapture) # type: ignore
        self.pushButton_3.clicked.connect(Form.startROICapture) # type: ignore
        self.pushButton_4.clicked.connect(Form.stopROICapture) # type: ignore
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.pushButton.setText(_translate("Form", "全屏录制"))
        self.pushButton_2.setText(_translate("Form", "结束录制"))
        self.label.setText(_translate("Form", "录制时自动隐藏窗口"))
        self.pushButton_3.setText(_translate("Form", "选择区域录制"))
        self.pushButton_4.setText(_translate("Form", "结束区域录制"))
        self.label_3.setText(_translate("Form", "有问题评论区留言"))

界面简洁,本身功能就比较单一,没必要花里胡哨

然后是主函数代码

from screenCapture import Ui_Form
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
import cv2
from PIL import ImageGrab
import numpy as np
import time
from datetime import datetime
global img
global point1, point2


class MainWindow(QMainWindow, Ui_Form):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.setupUi(self)
        self.MyTimer = QTimer()
        self.MyTimer1 = QTimer()
        self.name = None
        self.ROIname = None

    def startCapture(self):
        self.countSave = 0
        self.fps = 13
        self.flag = False
        self.count = 1
        self.name = datetime.now().strftime('%Y-%m-%d %H-%M-%S')
        self.screen = ImageGrab.grab()
        self.width, self.high = self.screen.size
        self.fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
        self.video = cv2.VideoWriter('%s.avi' % self.name, self.fourcc, self.fps, (self.width, self.high))
        print('开始录制!')
        self.label_2.setText("录制中!")
        self.start_time = time.time()
        self.showMinimized()
        self.MyTimer.start(30)
        self.MyTimer.timeout.connect(self.video_record)

    def video_record(self):  # 录入视频
        if self.flag:
            print("录制结束!")
            self.final_time = time.time()
            self.video.release()
        if self.count == 1:
            self.label_2.setText("录制中.")
        elif self.count == 2:
            self.label_2.setText("录制中..")
        else:
            self.label_2.setText("录制中...")
        self.count += 1
        self.count = self.count % 4
        self.im = ImageGrab.grab()
        frame = cv2.cvtColor(np.array(self.im), cv2.COLOR_RGB2BGR)
        self.video.write(frame)

    def stopCapture(self):
        if self.MyTimer.isActive():
            self.MyTimer.stop()
        self.label_2.setText("录制结束\n录制文件名:{}".format(self.name))
        if self.name is None:
            self.label_2.setText("视频还未录制!")
        else:
            self.flag = True

    def startROICapture(self):
        curScreen = ImageGrab.grab()  # 获取屏幕对象
        point1, point2 = select_roi(curScreen)
        self.showMinimized()
        self.min_x = min(point1[0], point2[0])
        self.min_y = min(point1[1], point2[1])
        self.max_x = max(point1[0], point2[0])
        self.max_y = max(point1[1], point2[1])
        height, width = self.max_y - self.min_y, self.max_x - self.min_x
        print(height, width)
        fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
        self.ROIname = datetime.now().strftime('%Y-%m-%d %H-%M-%S') + ".avi"
        self.video1 = cv2.VideoWriter(self.ROIname, fourcc, 13, (width, height))
        self.label_2.setText("录制中...")
        self.MyTimer1.start(30)
        self.MyTimer1.timeout.connect(self.ROIRect)

    def ROIRect(self):
        captureImage = ImageGrab.grab()  # 抓取屏幕
        frame = cv2.cvtColor(np.array(captureImage), cv2.COLOR_RGB2BGR)
        frame = frame[self.min_y:self.max_y, self.min_x:self.max_x, :]
        self.video1.write(frame)

    def stopROICapture(self):
        if self.MyTimer1.isActive():
            self.MyTimer1.stop()
        self.label_2.setText("录制结束\n录制文件名:{}".format(self.ROIname))
        if self.ROIname is None:
            self.label_2.setText("视频还未录制!")


# 鼠标事件
def on_mouse(event, x, y, flags, key):
    global img, point1, point2
    img2 = img.copy()
    if event == cv2.EVENT_LBUTTONDOWN:  # 左键点击
        point1 = (x, y)
        cv2.circle(img2, point1, 10, (0, 255, 0), thickness=2)
        cv2.imshow('image', img2)
    elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON):  # 按住左键拖曳
        cv2.rectangle(img2, point1, (x, y), (255, 0, 0), thickness=2)
        cv2.imshow('image', img2)
    elif event == cv2.EVENT_LBUTTONUP:  # 左键释放
        point2 = (x, y)
        cv2.rectangle(img2, point1, point2, (0, 0, 255), thickness=2)
        cv2.imshow('image', img2)


# 选择ROI区域
def select_roi(frame):
    global img, point1, point2
    img = cv2.cvtColor(np.array(frame), cv2.COLOR_RGB2BGR)
    winname = 'image'
    cv2.namedWindow(winname, cv2.WINDOW_NORMAL)
    cv2.setWindowProperty(winname, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
    cv2.setMouseCallback(winname, on_mouse)
    cv2.imshow(winname, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    return point1, point2


if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

最后的结果跟录制全屏的结果一样,都是录制结束后输出录制文件名称,结果如下

image.png

image.png

最后就是使用下面命令打包成exe文件了,可以随时使用

pyinstaller -F -w 文件名