用Python來作一個屏幕錄製工具

1、寫在前面

做爲一名測試,有時候常常會遇到須要錄屏記錄本身操做,方便後續開發同窗定位。之前都是用ScreenToGif來錄屏製做成動態圖,偶爾的機會看到python也能實現。那就趕忙學習下。java

2、效果展現

3、知識串講

此次要講的東西可能比較多了,涉及到pyqt5 GUI軟件的製做、QThread多線程的使用、Sikuli庫的圖形操做、win32庫的模擬鍵盤操做、cv2庫的寫視頻文件等。下面咱們一點點來蠶食我此次寫的代碼。python

一、GUI界面製做api

此次我用的是現成的Pyqt5界面佈局類,QVBoxLayout。這個類能夠快速協助我完成按鈕的垂直分佈,並且按鈕添加也更方便。多線程

button1 = QPushButton("自定義錄屏")
layout.addWidget(button1)

 

兩行代碼就完成了按鈕的命名和添加。我以前玩qt時,用的都是qt的UI界面,對應生成的組件代碼也比較複雜。所以,在開發一些少許按鈕、簡單佈局時能夠用QVBoxLayout類。若是喜歡水平佈局,能夠用QHBoxLayout類,使用方法是同樣的。app

另外,在按鈕點擊關聯的功能函數,即work()方法時,若是想帶參數,能夠經過lambda匿名函數來實現。這 也是個小技巧。jvm

# 不帶參數
button1.clicked.connect(self.work)
# 帶參數
button1.clicked.connect(lambda: self.work(1))

 

二、QThread類的多線程使用ide

由於錄屏工具備開始和中止兩個功能,一開始時我用的是單線程,發現工具就會卡死。查了一些資料,發現針對這種狀況,應該要使用多線程來實現,而QT庫中自己就有多線程類--QThread。函數

使用方法是經過繼承QThread類,重寫run方法來實現的。工具

(可是其實這種使用方法,QT大神們是不同意這樣使用的,我會在第2篇文章中再簡單說明更好的多線程使用方法)佈局

這 裏要注意,work()函數必須是Ui_Mainwindow類方法,由於若是不是類方法,會在運行GUI時致使生命週期直接結束,致使錄屏代碼沒見運行就報錯退出。

class WorkThread(QThread):
    def __init__(self, n):
        super(WorkThread, self).__init__()
        self.n = n

    def run(self):
        XXXXX

 

三、sikuli庫圖形識別

因爲這個庫的使用方法和介紹,我在以前的博客裏已經提過 了。所以只簡單地呈現下代碼。這段代碼主要是爲了自定義錄屏時,能夠獲取選擇範圍的座標值,並傳值給recording函數,從而完成自定義錄屏功能。

def SelectRegion():
    jvmPath = jpype.get_default_jvm_path()
    jpype.startJVM(jvmPath, '-ea', '-Djava.class.path=F:\\sikuli\\1\\sikulixapi.jar') #加載jar包路徑
    Screen = jpype.JClass('org.sikuli.script.Screen')
    myscreen = Screen()
    region = myscreen.selectRegion() # 自定義獲取屏幕範圍
    return region

 

四、win32庫模擬鍵盤操做

其實這個庫不用也是能夠的,我爲何要用呢?主要是爲了方便用戶在進行錄屏時,能自動將工具界面縮小。一切爲了用戶嘛!

如下這段代碼 是爲了縮小工具窗口,其中91表示左win鍵,40表示方向向下鍵。****即win+向下鍵是能夠實現窗口縮小功能的。****keybd_event(91, 0, 0, 0)表示按下win鍵,

keybd_event(91, 0, win32con.KEYEVENTF_KEYUP, 0)則是鬆開win鍵。

另外,這裏爲何要加 上sleep(0.5)?這是由於在按下win鍵後要延遲按方向鍵,否則是 不起做用的。

def Minimize_Window():
    win32api.keybd_event(91, 0, 0, 0)
    time.sleep(0.5)
    win32api.keybd_event(40, 0, 0, 0)
    time.sleep(0.5)
    win32api.keybd_event(91, 0, win32con.KEYEVENTF_KEYUP, 0)
    win32api.keybd_event(40, 0, win32con.KEYEVENTF_KEYUP, 0)

 

五、錄屏主代碼

這段代碼其實網上已經有不少相似的代碼,而且我已經加了註釋,相信你們應該能理解。這裏我想註明下的是:如何中止錄屏。

若是你們有去 網上查如何中止錄屏的方法,不少人都會寫如下代碼:

if cv2.waitKey(1) & 0xFF == ord('q'):
    break

 

而後告訴你,按q鍵就會中止錄屏。可是你會發現,實際狀況根本中止不了,爲何呢?由於還 有一句屏幕顯示的代碼:

cv2.imshow('imm', img_bgr)
if cv2.waitKey(1) & 0xFF == ord('q'):
    break

 

若是你不親自執行一次,你覺得會萬事大吉,但你錯了。這樣寫,會致使你的電腦屏幕被每一幀畫面給撐暴!由於用的while True,所以每一幀畫面都會顯示,即1S 25幀畫面會不停地顯示在你桌面上!

所以,綜上的問題,我採用了一種取巧的方法:在錄屏開始時生成一個標記文件,經過標記文件是否被刪除來判斷是否要中止錄屏功能。

4、示例代碼

一、工具GUI界面代碼:

# coding=utf-8
# @Software : PyCharm
#Python學習羣827513319



import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import time
import win32api,win32con
from recording import *

class WorkThread(QThread):
    def __init__(self, n):
        super(WorkThread, self).__init__()
        self.n = n

    def run(self):
        if self.n == 1:
            Minimize_Window()
            Recording(1)
        elif self.n == 2:
            Minimize_Window()
            Recording(2)
        else:
            StopRecording()

def Minimize_Window():
    win32api.keybd_event(91, 0, 0, 0)
    time.sleep(0.5)
    win32api.keybd_event(40, 0, 0, 0)
    time.sleep(0.5)
    win32api.keybd_event(91, 0, win32con.KEYEVENTF_KEYUP, 0)
    win32api.keybd_event(40, 0, win32con.KEYEVENTF_KEYUP, 0)

class Ui_Mainwindow():
    def setupUi(self, top):
        # 垂直佈局類QVBoxLayout
        layout = QVBoxLayout(top)
        # 添加錄屏相關按鈕
        button1 = QPushButton("自定義錄屏")
        layout.addWidget(button1)
        button2 = QPushButton("全屏錄屏")
        layout.addWidget(button2)
        button3 = QPushButton("中止錄屏")
        layout.addWidget(button3)
        self.text = QPlainTextEdit('歡迎使用!')
        layout.addWidget(self.text)
        button1.clicked.connect(lambda: self.work(1))
        button2.clicked.connect(lambda: self.work(2))
        button3.clicked.connect(lambda: self.work(3))

    def work(self, n):
        if n == 1 :
            print('已選擇自定義錄屏:')
            self.text.setPlainText('正在錄屏中,請等待……')
        elif n == 2 :
            print('已選擇全屏錄屏:')
            self.text.setPlainText('正在錄屏中,請等待……')
        else:
            print('已選擇結束錄屏:')
            self.text.setPlainText('錄屏結束!(點擊關閉按鈕,可退出程序!)')
        self.workThread = WorkThread(n)
        self.workThread.start()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    top = QWidget()
    top.setWindowTitle('錄屏小工具')
    top.resize(300, 170)
    ui = Ui_Mainwindow()
    ui.setupUi(top)
    top.show()
    sys.exit(app.exec_())# coding=utf-8

 

二、錄屏函數

# coding=utf-8
# @Software : PyCharm

from PIL import ImageGrab
import numpy as np
import cv2
import os
import jpype

def Recording(tag=1):
    # 錄屏開始時建立test.txt,做爲結束錄屏的條件
    #Python學習羣827513319
    if not os.path.exists('test.txt'):
        f = open('test.txt', 'w')
        f.close()
    # 根據tag值判斷自定義錄屏或全錄屏
    if tag == 1:
        r = SelectRegion()
        record_region = (r.x, r.y, r.w + r.x, r.h + r.y) # 自定義錄屏的範圍(左上座標、右下座標)
    elif tag == 2:
        record_region = None
    image = ImageGrab.grab(record_region)  # 獲取指定範圍的屏幕對象
    width, height = image.size
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    video = cv2.VideoWriter('test.avi', fourcc, 25, (width, height)) # 默認視頻爲25幀
    while True:
        captureImage = ImageGrab.grab(record_region)  # 抓取指定範圍的屏幕
        frame = cv2.cvtColor(np.array(captureImage), cv2.COLOR_RGB2BGR)
        video.write(frame) # 將每幀畫面寫視頻文件
        # 中止錄屏的條件:test.txt被刪除
        if not os.path.exists('test.txt'):
            break
    video.release()
    cv2.destroyAllWindows()

def SelectRegion():
    jvmPath = jpype.get_default_jvm_path()
    jpype.startJVM(jvmPath, '-ea', '-Djava.class.path=F:\\sikuli\\1\\sikulixapi.jar') #加載jar包路徑
    Screen = jpype.JClass('org.sikuli.script.Screen')
    myscreen = Screen()
    region = myscreen.selectRegion() # 自定義獲取屏幕範圍
    return region

def StopRecording():
    os.remove('test.txt') #中止錄屏的觸發條件

if __name__ == "__main__":
    Recording()

 

5、總結

至此,基本實現了錄屏小工具的代碼開發。可是若是你是對代碼中的相關庫不熟悉,或者都沒下載相關的庫,那我相信你還會遇到不少坑。所以,爲了方便一些小夥伴能快速把代碼跑起來,我將在下一篇文章中講講我在開發時遇到的一些坑,方便你們能避免這些問題。好了,今天就先到這裏!Bye!

相關文章
相關標籤/搜索