用python制作一款錄屏小工具

天我想學(xué)習(xí)記錄的內(nèi)容是?——如何用python實(shí)現(xiàn)錄屏秋冰。

1

寫在前面

作為一名測(cè)試,有時(shí)候經(jīng)常會(huì)遇到需要錄屏記錄自己操作坚踩,方便后續(xù)開發(fā)同學(xué)定位呈础。以前都是用ScreenToGif來(lái)錄屏制作成動(dòng)態(tài)圖,偶爾的機(jī)會(huì)看到python也能實(shí)現(xiàn)渊胸。那就趕緊學(xué)習(xí)下旬盯。

2

效果展示

?

3

知識(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ù)雜腾降。因此拣度,在開發(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)殇浧凉ぞ哂虚_始和停止兩個(gè)功能涕刚,一開始時(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)致錄屏代碼沒見運(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)則是松開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ì)不停地顯示在你桌面上纱扭!

因此,綜上的問題儡遮,我采用了一種取巧的方法:在錄屏開始時(shí)生成一個(gè)標(biāo)記文件乳蛾,通過(guò)標(biāo)記文件是否被刪除來(lái)判斷是否要停止錄屏功能。

4

示例代碼

1鄙币、工具GUI界面代碼:

# coding=utf-8# @Auther : "鵬哥賊優(yōu)秀"# @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)? ? ? ? # 添加錄屏相關(guān)按鈕? ? ? ? button1 = QPushButton("自定義錄屏")? ? ? ? layout.addWidget(button1)? ? ? ? button2 = QPushButton("全屏錄屏")? ? ? ? layout.addWidget(button2)? ? ? ? button3 = QPushButton("停止錄屏")? ? ? ? layout.addWidget(button3)? ? ? ? self.text = QPlainTextEdit('歡迎使用肃叶!By 鵬哥賊優(yōu)秀')? ? ? ? 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_())

2筛欢、錄屏函數(shù)

# coding=utf-8# @Auther : "鵬哥賊優(yōu)秀"# @Date : 2019/11/27# @Software : PyCharmfrom PIL import ImageGrabimport numpy as npimport cv2import osimport jpypedef Recording(tag=1):? ? # 錄屏開始時(shí)創(chuàng)建test.txt,作為結(jié)束錄屏的條件? ? 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 regiondef StopRecording():? ? os.remove('test.txt') #停止錄屏的觸發(fā)條件if __name__ == "__main__":????Recording()

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末唇聘,一起剝皮案震驚了整個(gè)濱河市版姑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌迟郎,老刑警劉巖剥险,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宪肖,居然都是意外死亡表制,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門控乾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)么介,“玉大人,你說(shuō)我怎么就攤上這事蜕衡∪蓝蹋” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵慨仿,是天一觀的道長(zhǎng)久脯。 經(jīng)常有香客問我,道長(zhǎng)镰吆,這世上最難降的妖魔是什么帘撰? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮鼎姊,結(jié)果婚禮上骡和,老公的妹妹穿的比我還像新娘相赁。我一直安慰自己,他們只是感情好慰于,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布钮科。 她就那樣靜靜地躺著,像睡著了一般婆赠。 火紅的嫁衣襯著肌膚如雪绵脯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天休里,我揣著相機(jī)與錄音蛆挫,去河邊找鬼。 笑死妙黍,一個(gè)胖子當(dāng)著我的面吹牛悴侵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拭嫁,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼可免,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了做粤?” 一聲冷哼從身側(cè)響起浇借,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎怕品,沒想到半個(gè)月后妇垢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肉康,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年闯估,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吼和。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睬愤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纹安,到底是詐尸還是另有隱情,我是刑警寧澤砂豌,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布厢岂,位于F島的核電站,受9級(jí)特大地震影響阳距,放射性物質(zhì)發(fā)生泄漏塔粒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一筐摘、第九天 我趴在偏房一處隱蔽的房頂上張望卒茬。 院中可真熱鬧船老,春花似錦、人聲如沸圃酵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)郭赐。三九已至薪韩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捌锭,已是汗流浹背俘陷。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留观谦,地道東北人拉盾。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像豁状,于是被迫代替她去往敵國(guó)和親捉偏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容