使用Python来做一个屏幕录制工具的操作代码


Posted in Python onJanuary 18, 2020

一、写在前面

作为一名测试,有时候经常会遇到需要录屏记录自己操作,方便后续开发同学定位。以前都是用ScreenToGif来录屏制作成动态图,偶尔的机会看到python也能实现。那就赶紧学习下。

二、效果展示

使用Python来做一个屏幕录制工具的操作代码

三、知识串讲

这次要讲的东西可能比较多了,涉及到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帧画面会不停地显示在你桌面上!

因此,综上的问题,我采用了一种取巧的方法:在录屏开始时生成一个标记文件,通过标记文件是否被删除来判断是否要停止录屏功能。

四、示例代码

1、工具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

2、录屏函数

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

五、总结

至此,基本实现了录屏小工具的代码开发。但是如果你是对代码中的相关库不熟悉,或者都没下载相关的库,那我相信你还会遇到很多坑。因此,为了方便一些小伙伴能快速把代码跑起来,我将在下一篇文章中讲讲我在开发时遇到的一些坑,方便大家能避免这些问题。好了,今天就先到这里!Bye!

以上所述是小编给大家介绍的使用Python来做一个屏幕录制工具的操作代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
Python实现基本线性数据结构
Aug 22 Python
Python实现爬取需要登录的网站完整示例
Aug 19 Python
python中判断文件编码的chardet(实例讲解)
Dec 21 Python
Python列表推导式、字典推导式与集合推导式用法实例分析
Feb 07 Python
python筛选出两个文件中重复行的方法
May 31 Python
对matplotlib改变colorbar位置和方向的方法详解
Dec 13 Python
python模块之subprocess模块级方法的使用
Mar 26 Python
python保存log日志,实现用log日志画图
Dec 24 Python
python 在threading中如何处理主进程和子线程的关系
Apr 25 Python
Django admin管理工具TabularInline类用法详解
May 14 Python
python实现文件分片上传的接口自动化
Nov 19 Python
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
May 04 Python
pytorch 状态字典:state_dict使用详解
Jan 17 #Python
Python标准库itertools的使用方法
Jan 17 #Python
Python实现投影法分割图像示例(二)
Jan 17 #Python
Python常用库大全及简要说明
Jan 17 #Python
Python Sphinx使用实例及问题解决
Jan 17 #Python
通过实例了解Python str()和repr()的区别
Jan 17 #Python
python无序链表删除重复项的方法
Jan 17 #Python
You might like
phpmyadmin操作流程
2006/10/09 PHP
php压缩多个CSS为一个css的代码并缓存
2011/04/21 PHP
php入门学习知识点七 PHP函数的基本应用
2011/07/14 PHP
php操作redis缓存方法分享
2015/06/03 PHP
php实现的二分查找算法示例
2017/06/20 PHP
Mootools 1.2教程(2) DOM选择器
2009/09/14 Javascript
写了10年的Javascript也未必全了解的连续赋值运算
2011/03/25 Javascript
javascript读写XML实现广告轮换(兼容IE、FF)
2013/08/09 Javascript
javascript操作css属性
2013/12/30 Javascript
js showModalDialog 弹出对话框的简单实例(子窗体)
2014/01/07 Javascript
整理JavaScript对DOM中各种类型的元素的常用操作
2016/05/05 Javascript
JavaScript编写检测用户所使用的浏览器的代码示例
2016/05/05 Javascript
AngularJS  $on、$emit和$broadcast的使用
2016/09/05 Javascript
vuex学习之Actions的用法详解
2017/08/29 Javascript
vue实现循环切换动画
2018/10/17 Javascript
微信小程序-form表单提交代码实例
2019/04/29 Javascript
vue深度监听(监听对象和数组的改变)与立即执行监听实例
2020/09/04 Javascript
解决vue下载后台传过来的乱码流的问题
2020/12/05 Vue.js
Vue+Bootstrap实现简易学生管理系统
2021/02/09 Vue.js
python写xml文件的操作实例
2014/10/05 Python
python获得文件创建时间和修改时间的方法
2015/06/30 Python
python简单实现操作Mysql数据库
2018/01/29 Python
PyCharm安装Markdown插件的两种方法
2019/06/24 Python
python 实现turtle画图并导出图片格式的文件
2019/12/07 Python
Keras 使用 Lambda层详解
2020/06/10 Python
Html5实现二维码扫描并解析
2016/01/20 HTML / CSS
上海微创软件面试题
2012/06/14 面试题
高中打架检讨书
2014/02/13 职场文书
出纳员的岗位职责
2014/02/22 职场文书
建房协议书
2014/04/11 职场文书
党的群众路线对照检查材料
2014/09/22 职场文书
走群众路线剖析材料
2014/10/09 职场文书
2016年世界人口日宣传活动总结
2016/04/05 职场文书
受欢迎的自荐信,就这么写!
2019/04/19 职场文书
解决Mysql中的innoDB幻读问题
2022/04/29 MySQL
mysql字段为NULL索引是否会失效实例详解
2022/05/30 MySQL