Making a screen recording tool with Python

Keywords: Python Lambda Qt Pycharm

1, Write in front

As a test, sometimes it is often necessary to record their own operation on the screen, so as to facilitate the follow-up development and positioning of students. In the past, screen to GIF was used to record the screen and make dynamic pictures. Occasionally, python can also be realized. Then hurry to learn.

2, Effect display

3, Knowledge collusion

There may be more to talk about this time, involving the production of pyqt5 GUI software, the use of QThread multithreading, the graphic operation of Sikuli library, the analog keyboard operation of win32 library, the writing of video files of cv2 library, etc. Now let's nibble at the code I wrote this time.

1. GUI interface making

This time I used the ready-made Pyqt5 interface layout class, QVBoxLayout. This class can help me quickly complete the vertical distribution of buttons, and it is more convenient to add buttons.

button1 = QPushButton("Custom recording")
layout.addWidget(button1)

 

Two lines of code completes the naming and adding of the button. When I used to play qt, I used the UI of qt, and the generated component code was also complex. Therefore, the QVBoxLayout class can be used when developing a few buttons and simple layouts. If you like horizontal layout, you can use the QHBoxLayout class. The method is the same.

In addition, when clicking the function associated with the button, i.e. the work() method, if you want to take parameters, you can implement it through the lambda anonymous function. It's also a trick.

# Without parameters
button1.clicked.connect(self.work)
# Band parameters
button1.clicked.connect(lambda: self.work(1))

 

2. Multithreading of QThread class

Because the screen recorder has two functions: start and stop. At the beginning, I used a single thread and found that the tool would be stuck. After checking some data, it is found that in this case, multithreading should be used, and QT library itself has multithreading class - QThread.

The method is implemented by inheriting the QThread class and overriding the run method.

(but in fact, QT gods do not approve of this method. I will explain a better way to use multithreading in the second article.)

Note here that the work() function must be a UI ﹣ MainWindow class method, because if it is not a class method, it will directly end the life cycle when the GUI is running, resulting in the error exit when the screen recording code is not running.

class WorkThread(QThread):
    def __init__(self, n):
        super(WorkThread, self).__init__()
        self.n = n

    def run(self):
        XXXXX

 

3. Figure recognition of sikuli Library

Because of the usage and introduction of this library, I have mentioned it in my previous blog. So just simply render the code. This code is mainly used to obtain the coordinate value of the selected range and pass the value to the recording function when the screen recording is customized, so as to complete the customized screen recording function.

def SelectRegion():
    jvmPath = jpype.get_default_jvm_path()
    jpype.startJVM(jvmPath, '-ea', '-Djava.class.path=F:\\sikuli\\1\\sikulixapi.jar') #Load jar Packet path
    Screen = jpype.JClass('org.sikuli.script.Screen')
    myscreen = Screen()
    region = myscreen.selectRegion() # Custom get screen range
    return region

 

4. win32 library simulating keyboard operation

In fact, it's OK to use this library. Why should I use it? It is mainly for the convenience of the user to automatically reduce the tool interface when recording the screen. All for users!

The following code is to narrow the tool window, where 91 represents the left win key and 40 represents the direction down key. ****That is to say, win + down key can reduce the window. ****Keybd [event (91, 0, 0, 0) means to press win key,

Keybd? Event (91, 0, win32con. Keyeventf? Keyup, 0) is to release the win key.

In addition, why do we add sleep(0.5)? This is because you need to delay pressing the direction key after pressing the win key, otherwise it will not work.

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. Recording main code

In fact, there are many similar codes on the Internet, and I have added comments. I believe you can understand them. Here I'd like to note how to stop recording.

If you go to the Internet to find out how to stop screen recording, many people will write the following code:

if cv2.waitKey(1) & 0xFF == ord('q'):
    break

 

Then I'll tell you that pressing the q key will stop the recording. But you will find that the actual situation can not stop. Why? Because there is another code on the screen:

cv2.imshow('imm', img_bgr)
if cv2.waitKey(1) & 0xFF == ord('q'):
    break

 

If you don't do it yourself, you think everything will be OK, but you are wrong. If you write like this, your computer screen will be blown up by every frame! Because of the while True, every frame will be displayed, that is, 1S 25 frame will be displayed on your desktop constantly!

Therefore, to sum up the problems, I used a shortcut method: generate a tag file at the beginning of screen recording, and judge whether to stop the screen recording function by whether the tag file is deleted.

4, Sample code

1. Tool GUI interface code:

# coding=utf-8
# @Software : PyCharm
#Python Learning group 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):
        # Vertical layout class QVBoxLayout
        layout = QVBoxLayout(top)
        # Add screen recording related buttons
        button1 = QPushButton("Custom recording")
        layout.addWidget(button1)
        button2 = QPushButton("Full screen recording")
        layout.addWidget(button2)
        button3 = QPushButton("Stop recording")
        layout.addWidget(button3)
        self.text = QPlainTextEdit('Welcome to use!')
        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('Custom recording selected:')
            self.text.setPlainText('Recording screen, please wait')
        elif n == 2 :
            print('Full screen recording selected:')
            self.text.setPlainText('Recording screen, please wait')
        else:
            print('End recording selected:')
            self.text.setPlainText('End of recording!(Click the close button to exit the program!)')
        self.workThread = WorkThread(n)
        self.workThread.start()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    top = QWidget()
    top.setWindowTitle('Video recorder')
    top.resize(300, 170)
    ui = Ui_Mainwindow()
    ui.setupUi(top)
    top.show()
    sys.exit(app.exec_())# coding=utf-8

 

2. Screen recording function

# coding=utf-8
# @Software : PyCharm

from PIL import ImageGrab
import numpy as np
import cv2
import os
import jpype

def Recording(tag=1):
    # Create at the beginning of recording test.txt,As a condition for ending recording
    #Python Learning group 827513319
    if not os.path.exists('test.txt'):
        f = open('test.txt', 'w')
        f.close()
    # according to tag Value judgment user defined recording screen or full recording screen
    if tag == 1:
        r = SelectRegion()
        record_region = (r.x, r.y, r.w + r.x, r.h + r.y) # Range of custom recording screen (upper left coordinate, lower right coordinate)
    elif tag == 2:
        record_region = None
    image = ImageGrab.grab(record_region)  # Gets the screen object for the specified range
    width, height = image.size
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    video = cv2.VideoWriter('test.avi', fourcc, 25, (width, height)) # Default video is 25 frames
    while True:
        captureImage = ImageGrab.grab(record_region)  # Grab the screen in the specified range
        frame = cv2.cvtColor(np.array(captureImage), cv2.COLOR_RGB2BGR)
        video.write(frame) # Write video file for each frame
        # Conditions for stopping screen recording: test.txt Be deleted
        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') #Load jar Packet path
    Screen = jpype.JClass('org.sikuli.script.Screen')
    myscreen = Screen()
    region = myscreen.selectRegion() # Custom get screen range
    return region

def StopRecording():
    os.remove('test.txt') #Trigger conditions for stopping screen recording

if __name__ == "__main__":
    Recording()

 

Five, summary

At this point, the basic realization of the code development of screen recording gadgets. But if you are not familiar with the relevant libraries in the code or haven't downloaded them, I believe you will encounter many pitfalls. Therefore, in order to facilitate some small partners to run the code quickly, I will talk about some pits I encountered in the development in the next article, so that you can avoid these problems. OK, let's get here first! Bye!

Posted by Monkee Of Evil on Fri, 17 Jan 2020 07:09:22 -0800