使用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正则分组的应用
Nov 10 Python
Django学习笔记之Class-Based-View
Feb 15 Python
Python中字符串格式化str.format的详细介绍
Feb 17 Python
Python 编码Basic Auth使用方法简单实例
May 25 Python
Python3计算三角形的面积代码
Dec 18 Python
python reverse反转部分数组的实例
Dec 13 Python
python获取交互式ssh shell的方法
Feb 14 Python
python的内存管理和垃圾回收机制详解
May 18 Python
python全局变量引用与修改过程解析
Jan 07 Python
python爬取代理IP并进行有效的IP测试实现
Oct 09 Python
Python扫描端口的实现
Jan 25 Python
Django使用channels + websocket打造在线聊天室
May 20 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
怎样在UNIX系统下安装MySQL
2006/10/09 PHP
如何突破PHP程序员的技术瓶颈分析
2011/07/17 PHP
PHP+Mysql+jQuery实现发布微博程序 jQuery篇
2011/10/08 PHP
php中如何使对象可以像数组一样进行foreach循环
2013/08/09 PHP
PHP中Memcache操作类及用法实例
2014/12/12 PHP
php 处理png图片白色背景色改为透明色的实例代码
2018/12/10 PHP
一个js写的日历(代码部分网摘)
2009/09/20 Javascript
js获取dom的高度和宽度(可见区域及部分等等)
2013/06/13 Javascript
javascript每日必学之多态
2016/02/23 Javascript
JS遍历页面所有对象属性及实现方法
2016/08/01 Javascript
利用Js+Css实现折纸动态导航效果实例源码
2017/01/25 Javascript
使用canvas及js简单生成验证码方法
2017/04/02 Javascript
使用Require.js封装原生js轮播图的实现代码
2017/06/15 Javascript
vue+vuex+axios+echarts画一个动态更新的中国地图的方法
2017/12/19 Javascript
vue动态路由配置及路由传参的方式
2018/05/23 Javascript
Vue keepAlive 数据缓存工具实现返回上一个页面浏览的位置
2019/05/10 Javascript
JavaScript基础之this和箭头函数详析
2019/09/05 Javascript
webpack打包html里面img后src为“[object Module]”问题
2019/12/22 Javascript
详解微信小程序(Taro)手动埋点和自动埋点的实现
2021/03/02 Javascript
python实现通过代理服务器访问远程url的方法
2015/04/29 Python
[原创]python爬虫(入门教程、视频教程)
2018/01/08 Python
Python argparse模块应用实例解析
2019/11/15 Python
python 协程 gevent原理与用法分析
2019/11/22 Python
Spring实战之使用util:命名空间简化配置操作示例
2019/12/09 Python
Python爬虫防封ip的一些技巧
2020/08/06 Python
使用canvas压缩图片大小的方法示例
2019/08/02 HTML / CSS
美国最受欢迎的度假目的地优惠套餐:BookVIP
2018/09/27 全球购物
高二美术教学反思
2014/01/14 职场文书
施工安全标语
2014/06/07 职场文书
委托证明模板
2014/09/16 职场文书
四风问题个人对照检查剖析材料
2014/09/27 职场文书
2014年超市员工工作总结
2014/11/18 职场文书
2016年学校综治宣传月活动总结
2016/03/16 职场文书
如果用一句诗总结你的上半年,你会用哪句呢?
2019/07/16 职场文书
nginx网站服务如何配置防盗链(推荐)
2021/03/31 Servers
Python find()、rfind()方法及作用
2022/12/24 Python