使用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 25 Python
利用Python实现命令行版的火车票查看器
Aug 05 Python
python数据类型_元组、字典常用操作方法(介绍)
May 30 Python
手把手教你如何安装Pycharm(详细图文教程)
Nov 28 Python
Python微信操控itchat的方法
May 31 Python
python word转pdf代码实例
Aug 16 Python
python清空命令行方式
Jan 13 Python
python脚本实现mp4中的音频提取并保存在原目录
Feb 27 Python
IntelliJ 中配置 Anaconda的过程图解
Jun 01 Python
浅谈keras 的抽象后端(from keras import backend as K)
Jun 16 Python
撤回我也能看到!教你用Python制作微信防撤回脚本
Jun 11 Python
Selenium浏览器自动化如何上传文件
Apr 06 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
php中使用Akismet防止垃圾评论的代码
2011/06/10 PHP
php获取数组元素中头一个数组元素值的实现方法
2014/12/20 PHP
javascript下操作css的float属性的特殊写法
2007/08/22 Javascript
setTimeout 不断吐食CPU的问题分析
2009/04/01 Javascript
JavaScript随机排序(随即出牌)
2010/09/17 Javascript
jquery选择器的选择使用及性能介绍
2013/01/16 Javascript
关于scrollLeft,scrollTop的浏览器兼容性测试
2013/03/19 Javascript
JS 添加千分位与去掉千分位的示例
2013/07/11 Javascript
JS:window.onload的使用介绍
2013/11/13 Javascript
javascript中call apply 的应用场景
2015/04/16 Javascript
Vue.js实现实例搜索应用功能详细代码
2017/08/24 Javascript
angular.js实现购物车功能
2017/10/23 Javascript
实战node静态文件服务器的示例代码
2018/03/08 Javascript
Vue入门之animate过渡动画效果
2018/04/08 Javascript
详解vuex持久化插件解决浏览器刷新数据消失问题
2019/04/15 Javascript
详解Vue前端生产环境发布配置实战篇
2019/05/07 Javascript
React实现轮播效果
2020/08/25 Javascript
[01:46]DOTA2上海特锦赛小组赛英文解说KotlGuy采访
2016/02/27 DOTA
Tensorflow的可视化工具Tensorboard的初步使用详解
2018/02/11 Python
python读取LMDB中图像的方法
2018/07/02 Python
Python如何爬取实时变化的WebSocket数据的方法
2019/03/09 Python
详解Python二维数组与三维数组切片的方法
2019/07/18 Python
详解Python中的路径问题
2020/09/02 Python
美国家用和厨房电器销售网站:Appliances Connection
2020/01/24 全球购物
乌克兰数字设备、配件和智能技术的连锁商店:KTC
2020/08/18 全球购物
世界上最大的艺术社区:SAA
2020/12/30 全球购物
Linux机考试题
2015/10/16 面试题
广告学专业推荐信范文
2013/11/23 职场文书
办理退休介绍信
2014/01/09 职场文书
高中政治教学反思
2014/01/18 职场文书
初中英语课后反思
2014/04/25 职场文书
医院竞聘演讲稿
2014/05/16 职场文书
新闻编辑专业自荐信
2014/07/02 职场文书
2015年房地产个人工作总结
2015/05/26 职场文书
黑白记忆观后感
2015/06/18 职场文书
使用 Apache Dubbo 实现远程通信(微服务架构)
2022/02/12 Servers