使用Python來(lái)做一個(gè)屏幕錄制工具的操作代碼
一、寫在前面
作為一名測(cè)試,有時(shí)候經(jīng)常會(huì)遇到需要錄屏記錄自己操作,方便后續(xù)開(kāi)發(fā)同學(xué)定位。以前都是用ScreenToGif來(lái)錄屏制作成動(dòng)態(tài)圖,偶爾的機(jī)會(huì)看到python也能實(shí)現(xiàn)。那就趕緊學(xué)習(xí)下。
二、效果展示
三、知識(shí)串講
這次要講的東西可能比較多了,涉及到pyqt5 GUI軟件的制作、QThread多線程的使用、Sikuli庫(kù)的圖形操作、win32庫(kù)的模擬鍵盤操作、cv2庫(kù)的寫視頻文件等。下面我們一點(diǎn)點(diǎn)來(lái)蠶食我這次寫的代碼。
1、GUI界面制作
這次我用的是現(xiàn)成的Pyqt5界面布局類,QVBoxLayout。這個(gè)類可以快速協(xié)助我完成按鈕的垂直分布,而且按鈕添加也更方便。
button1 = QPushButton("自定義錄屏") layout.addWidget(button1)
兩行代碼就完成了按鈕的命名和添加。我之前玩qt時(shí),用的都是qt的UI界面,對(duì)應(yīng)生成的組件代碼也比較復(fù)雜。因此,在開(kāi)發(fā)一些少量按鈕、簡(jiǎn)單布局時(shí)可以用QVBoxLayout類。如果喜歡水平布局,可以用QHBoxLayout類,使用方法是一樣的。
另外,在按鈕點(diǎn)擊關(guān)聯(lián)的功能函數(shù),即work()方法時(shí),如果想帶參數(shù),可以通過(guò)lambda匿名函數(shù)來(lái)實(shí)現(xiàn)。這 也是個(gè)小技巧。
# 不帶參數(shù) button1.clicked.connect(self.work) # 帶參數(shù) button1.clicked.connect(lambda: self.work(1))
2、QThread類的多線程使用
因?yàn)殇浧凉ぞ哂虚_(kāi)始和停止兩個(gè)功能,一開(kāi)始時(shí)我用的是單線程,發(fā)現(xiàn)工具就會(huì)卡死。查了一些資料,發(fā)現(xiàn)針對(duì)這種情況,應(yīng)該要使用多線程來(lái)實(shí)現(xiàn),而QT庫(kù)中本身就有多線程類--QThread。
使用方法是通過(guò)繼承QThread類,重寫run方法來(lái)實(shí)現(xiàn)的。
(但是其實(shí)這種使用方法,QT大神們是不贊成這樣使用的,我會(huì)在第2篇文章中再簡(jiǎn)單說(shuō)明更好的多線程使用方法)
這 里要注意,work()函數(shù)必須是Ui_Mainwindow類方法,因?yàn)槿绻皇穷惙椒ǎ瑫?huì)在運(yùn)行GUI時(shí)導(dǎo)致生命周期直接結(jié)束,導(dǎo)致錄屏代碼沒(méi)見(jiàn)運(yùn)行就報(bào)錯(cuò)退出。
class WorkThread(QThread): def __init__(self, n): super(WorkThread, self).__init__() self.n = n def run(self): XXXXX
3、sikuli庫(kù)圖形識(shí)別
由于這個(gè)庫(kù)的使用方法和介紹,我在之前的博客里已經(jīng)提過(guò) 了。因此只簡(jiǎn)單地呈現(xiàn)下代碼。這段代碼主要是為了自定義錄屏?xí)r,可以獲取選擇范圍的坐標(biāo)值,并傳值給recording函數(shù),從而完成自定義錄屏功能。
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庫(kù)模擬鍵盤操作
其實(shí)這個(gè)庫(kù)不用也是可以的,我為什么要用呢?主要是為了方便用戶在進(jìn)行錄屏?xí)r,能自動(dòng)將工具界面縮小。一切為了用戶嘛!
以下這段代碼 是為了縮小工具窗口,其中91表示左win鍵,40表示方向向下鍵。****即win+向下鍵是可以實(shí)現(xiàn)窗口縮小功能的。****keybd_event(91, 0, 0, 0)表示按下win鍵,
keybd_event(91, 0, win32con.KEYEVENTF_KEYUP, 0)則是松開(kāi)win鍵。
另外,這里為什么要加 上sleep(0.5)?這是因?yàn)樵诎聪聎in鍵后要延遲按方向鍵,不然是 不起作用的。
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、錄屏主代碼
這段代碼其實(shí)網(wǎng)上已經(jīng)有很多類似的代碼,并且我已經(jīng)加了注釋,相信大家應(yīng)該能理解。這里我想注明下的是:如何停止錄屏。
如果大家有去 網(wǎng)上查如何停止錄屏的方法,很多人都會(huì)寫以下代碼:
if cv2.waitKey(1) & 0xFF == ord('q'): break
然后告訴你,按q鍵就會(huì)停止錄屏。但是你會(huì)發(fā)現(xiàn),實(shí)際情況根本停止不了,為什么呢?因?yàn)檫€ 有一句屏幕顯示的代碼:
cv2.imshow('imm', img_bgr) if cv2.waitKey(1) & 0xFF == ord('q'): break
如果你不親自執(zhí)行一次,你以為會(huì)萬(wàn)事大吉,但你錯(cuò)了。這樣寫,會(huì)導(dǎo)致你的電腦屏幕被每一幀畫面給撐暴!因?yàn)橛玫膚hile True,因此每一幀畫面都會(huì)顯示,即1S 25幀畫面會(huì)不停地顯示在你桌面上!
因此,綜上的問(wèn)題,我采用了一種取巧的方法:在錄屏開(kāi)始時(shí)生成一個(gè)標(biāo)記文件,通過(guò)標(biāo)記文件是否被刪除來(lái)判斷是否要停止錄屏功能。
四、示例代碼
1、工具GUI界面代碼:
# coding=utf-8 # @Software : PyCharm #Python學(xué)習(xí)群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) # 添加錄屏相關(guān)按鈕 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('正在錄屏中,請(qǐng)等待……') elif n == 2 : print('已選擇全屏錄屏:') self.text.setPlainText('正在錄屏中,請(qǐng)等待……') else: print('已選擇結(jié)束錄屏:') self.text.setPlainText('錄屏結(jié)束!(點(diǎn)擊關(guān)閉按鈕,可退出程序!)') 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
2、錄屏函數(shù)
# coding=utf-8 # @Software : PyCharm from PIL import ImageGrab import numpy as np import cv2 import os import jpype def Recording(tag=1): # 錄屏開(kāi)始時(shí)創(chuàng)建test.txt,作為結(jié)束錄屏的條件 #Python學(xué)習(xí)群827513319 if not os.path.exists('test.txt'): f = open('test.txt', 'w') f.close() # 根據(jù)tag值判斷自定義錄屏或全錄屏 if tag == 1: r = SelectRegion() record_region = (r.x, r.y, r.w + r.x, r.h + r.y) # 自定義錄屏的范圍(左上坐標(biāo)、右下坐標(biāo)) elif tag == 2: record_region = None image = ImageGrab.grab(record_region) # 獲取指定范圍的屏幕對(duì)象 width, height = image.size fourcc = cv2.VideoWriter_fourcc(*'XVID') video = cv2.VideoWriter('test.avi', fourcc, 25, (width, height)) # 默認(rèn)視頻為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') #停止錄屏的觸發(fā)條件 if __name__ == "__main__": Recording()
五、總結(jié)
至此,基本實(shí)現(xiàn)了錄屏小工具的代碼開(kāi)發(fā)。但是如果你是對(duì)代碼中的相關(guān)庫(kù)不熟悉,或者都沒(méi)下載相關(guān)的庫(kù),那我相信你還會(huì)遇到很多坑。因此,為了方便一些小伙伴能快速把代碼跑起來(lái),我將在下一篇文章中講講我在開(kāi)發(fā)時(shí)遇到的一些坑,方便大家能避免這些問(wèn)題。好了,今天就先到這里!Bye!
以上所述是小編給大家介紹的使用Python來(lái)做一個(gè)屏幕錄制工具的操作代碼,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
python動(dòng)態(tài)文本進(jìn)度條的實(shí)例代碼
這篇文章主要介紹了python動(dòng)態(tài)文本進(jìn)度條的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01一行Python代碼實(shí)現(xiàn)為圖片上版權(quán)
不知道大家會(huì)不會(huì)遇到這樣的情況,自己辛辛苦苦整理的攻略,分享給自己的一些朋友,結(jié)果分享有人堂而皇之地拿著這份攻略圖片去引流,并聲稱是自己整理的,真是豈有此理!本文就來(lái)用Python實(shí)現(xiàn)為圖片上版權(quán),需要的可以參考一下2023-01-0120個(gè)常用Python運(yùn)維庫(kù)和模塊
本篇文章給大家整理了20個(gè)最常用Python運(yùn)維中用到的庫(kù)和模塊,希望我們整理的內(nèi)容對(duì)大家有所幫助。2018-02-02如何利用pytesseract識(shí)別圖片中的數(shù)字
這篇文章主要介紹了如何利用pytesseract識(shí)別圖片中的數(shù)字問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05Numpy中np.max的用法及np.maximum區(qū)別
這篇文章主要介紹了Numpy中np.max的用法及np.maximum區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11