【Python成長之路】用python製作一款錄屏小工具(1)

哈嘍大家好,我是鵬哥。

今天我想學習記錄的內容是 —— 如何用python實現錄屏

1

寫在前面

作爲一名測試,有時候經常會遇到需要錄屏記錄自己操作,方便後續開發同學定位。以前都是用ScreenToGif來錄屏製作成動態圖,偶爾的機會看到python也能實現。那就趕緊學習下。

2

效果展示

3

知識串講(敲黑板啦)

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

(1)GUI界面製作 

這次我用的是現成的Pyqt5界面佈局類,QVBoxLayout。這個類可以快速協助我完成按鈕的垂直分佈,而且按鈕添加也更方便。

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

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

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

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

(2)QThread類的多線程使用

因爲錄屏工具有開始和停止兩個功能,一開始時我用的是單線程,發現工具就會卡死。查了一些資料,發現針對這種情況,應該要使用多線程來實現,而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

(3)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

(4)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)

(5)錄屏主代碼

這段代碼其實網上已經有很多類似的代碼,並且我已經加了註釋,相信大家應該能理解。這裏我想註明下的是:如何停止錄屏。

如果大家有去 網上查如何停止錄屏的方法,很多人都會寫以下代碼:​​​​​​​

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

示例代碼

1、工具GUI界面代碼:

# coding=utf-8

# @Auther : "鵬哥賊優秀"# @Date : 2019/11/27# @Software : PyCharm 
import sysfrom PyQt5.QtCore import *from PyQt5.QtWidgets import *import timeimport win32api,win32confrom 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('歡迎使用!By 鵬哥賊優秀')        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_())

2、錄屏函數​​​​​​​

# coding=utf-8# @Auther : "鵬哥賊優秀"# @Date : 2019/11/27# @Software : PyCharm
from PIL import ImageGrabimport numpy as npimport cv2import osimport jpype
def Recording(tag=1):    # 錄屏開始時創建test.txt,作爲結束錄屏的條件    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

總結

至此,基本實現了錄屏小工具的代碼開發。但是如果你是對代碼中的相關庫不熟悉,或者都沒下載相關的庫,那我相信你還會遇到很多坑。因此,爲了方便一些同學能快速把代碼跑起來,我將在第2篇文章中講講我在開發時遇到的一些坑,方便大家能避免這些問題。好了,今天就先到這裏!Bye!

 

~~~下課鈴~~~

 

 

點擊下方詩句,可以留言互動喔  

 

是誰來自山川湖海,卻囿與晝夜廚房與愛。

 

掃描二維碼

    與鵬哥一起

學python吧!

發佈了38 篇原創文章 · 獲贊 17 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章