Is the desktop too monotonous? Let's use Python to make a custom animation pendant, which is fun and interesting!

foreword

Some time ago, I wrote a blog about a cool music player made by Python . Some fans asked me why I use PyQt5 for music players. Is the effect better than Tkinter? Can PyQt5 really achieve these cool UI pictures? I have never been in contact with PyQt5 before, can you share more development cases in this regard?

Today, I will take you all to develop an interesting custom desktop animation pendant with Python's PyQt5 and see the effect of the animation pendant!
insert image description here

Next, we start to introduce the production process of this custom desktop animation pendant.

Skip directly to the end of the article to get exclusive fan benefits.

1. Core function design

In general, we need to convert our favorite dynamic gif or video into a desktop animation pendant. The knowledge points mainly include GIF image/video analysis , portrait extraction and segmentation , PyQt5 form settings , custom pendant animation implementation , ico icon generation , program packaging , etc.

The dismantling requirements can be roughly sorted out and we need to divide them into the following steps to complete:

  1. Analyze the gif or video frame by frame, obtain the converted image, extract the human body area in the image, and modify and replace the image size in batches
  2. Initialize and set the display effect of the animation pendant form, the position and size of the form, etc.
  3. Desktop animation pendant function realization, animation carousel, mouse control pendant position drag
  4. Pendant packaging icon settings, packaging configuration

2. Implementation steps

1. Parse and extract, modify pictures

GIF image analysis:

Gif dynamic map resources, you can choose according to your own preferences. The blogger used the fairy dance dynamic Gif written before to demonstrate the effect.
insert image description here

First, we need to parse the Gif dynamic image according to each frame and convert it into a picture format. code show as below:

from PIL import Image  # 导入PIL的Image包
import os
gifFileName = "./demo.gif"  # 把gif图赋值给gifFileName
im = Image.open(gifFileName)  # 使用Image的open函数打开test.gif图像
pngDir = gifFileName[:-4]  # 倒着从gifFileName中的倒数第四个开始取字符(跳过.gif),赋值给pngDir,作为文件夹的名字
if not os.path.exists(pngDir):
    os.makedirs('./img')  # 用图片名创建一个文件夹,用来存放每帧图片,名字为pngDir的值

try:
  while True:  # 死循环
    current = im.tell()  # 用tell函数保存当前帧图片,赋值给current
    im.save(pngDir+'/'+str(current+1)+'.png')  # 调用save函数保存该帧图片
    im.seek(current+1)  # 调用seek函数获取下一帧图片,参数变为current帧图片+1
    # 这里再次进入循环,当为最后一帧图片时,seek会抛出异常,代码执行except
except EOFError:
    pass  # 最后一帧时,seek抛出异常,进入这里,pass跳过

In this way, the dynamic Gif image can be converted into a picture, and the effect is as follows:

insert image description here
Video analysis:

Similarly, for video parsing, each frame is also parsed and converted into a picture format. The core code is as follows:

# 将视频按照每一帧转成图片png
import cv2
videoFileName = "./demo.mp4"  # 把视频路径赋值给videoFileName
pngDir = videoFileName[:-4]  # 倒着从gifFileName中的倒数第四个开始取字符(跳过.后缀),赋值给pngDir,作为文件夹的名字
if not os.path.exists(pngDir):
    os.makedirs(pngDir)  # 用图片名创建一个文件夹,用来存放每帧图片,名字为pngDir的值
# 视频处理 分割成一帧帧图片
cap = cv2.VideoCapture(videoFileName)
num = 1
while True:
    # 逐帧读取视频  按顺序保存到本地文件夹
    ret, frame = cap.read()
    if ret:
        cv2.imwrite(f"{
      
      pngDir}/{
      
      num}.png", frame)  # 保存一帧帧的图片
        num += 1
    else:
        break
cap.release()   # 释放资源

The effect is as follows:
insert image description here
The pictures extracted frame by frame have been obtained, and we need to segment and extract the portraits in these pictures.

Portrait segmentation:

What we call is Baidu's open human body analysis interface - Baidu AI Open Platform Link .

insert image description here
Here we can create a portrait segmentation application, in which the API Key and Secret Key will be used later when we call the face recognition detection interface.

insert image description here
We can see the official help documentation, which is very detailed. How to call the request URL data format and send a request to the API service address using POST , the parameter access_token must be included in the URL , which can be generated by the API Key and Secret Key in the background. The API Key and Secret Key here are the ones we mentioned above.
insert image description here
So how do we get a portrait image with an empty background? According to the API documentation, you can see that there is a type attribute set to foreground to extract portrait images with an empty background.

insert image description here
The interface process of portrait segmentation is basically clear, and the code can be implemented.

# 保存图片
def save_base_image(img_str, filename):
    img_data = base64.b64decode(img_str)
    with open(filename, 'wb') as f:
        f.write(img_data)

# 获取token
def get_token():
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=' + client_id + '&client_secret=' + client_secret
    request = urllib.request.Request(host)
    request.add_header('Content-Type', 'application/json; charset=UTF-8')
    response = urllib.request.urlopen(request)
    token_content = response.read()
    if token_content:
        token_info = json.loads(token_content)
        token_key = token_info['access_token']
    return token_key

# 人像分割
def body_seg_fore(filename, resultfilename):
    request_url = "https://aip.baidubce.com/rest/2.0/image-classify/v1/body_seg"

    # 二进制方式打开图片文件
    f = open(filename, 'rb')
    img = base64.b64encode(f.read())

    params = dict()
    params['image'] = img
    params['type'] = 'foreground'
    params = urllib.parse.urlencode(params).encode("utf-8")
    # params = json.dumps(params).encode('utf-8')

    access_token = get_token()
    request_url = request_url + "?access_token=" + access_token
    request = urllib.request.Request(url=request_url, data=params)
    request.add_header('Content-Type', 'application/x-www-form-urlencoded')
    response = urllib.request.urlopen(request)
    content = response.read()
    if content:
        # print(content)
        content = content.decode('utf-8')
        # print(content)
        data = json.loads(content)
        # print(data)
        img_str = data['foreground']
        save_base_image(img_str, resultfilename)

In this way, we can perform portrait segmentation according to the picture, and extract the portrait image with an empty background . The effect is as follows:

insert image description here
insert image description here
If the size of the extracted portrait image does not meet our requirements, we can also adjust the image size.

file_list = os.listdir("./image")  # 读取当前文件夹所有文件
# print(file_list)
n = len(file_list)
for i in range(n):
    s = str(file_list[i])
    if s[-4:] == ".png":  # 检查后缀
        src = os.path.join(os.path.abspath('./image/'), s)  # 原先的图片名字
        img = Image.open(src)
        new_img = img.resize((128, 128), Image.BILINEAR)
        new_img.save(src)

The empty background image we need has been obtained, and then we will implement the desktop pendant function.

2. Initialize the animation widget

# 窗体初始化
def windowinit(self):
    self.x = 1650
    self.y = 860
    self.setGeometry(self.x, self.y, 300, 300)
    self.setWindowTitle('My Gadgets')
    self.img_num = 1
    self.img_path = './image/{file}/{img}.png'.format(file=self.dis_file, img=str(self.img_num))
    self.lab = QLabel(self)
    self.qpixmap = QPixmap(self.img_path)
    self.lab.setPixmap(self.qpixmap)
    self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.SubWindow)
    self.setAutoFillBackground(False)
    self.setAttribute(Qt.WA_TranslucentBackground, True)
    self.show()

def __init__(self):
    super(Gadgets, self).__init__()

    self.dis_file = "img1"
    self.windowinit()
    self.icon_quit()

    self.pos_first = self.pos()
    self.img_count = len(os.listdir('./image/{}'.format(self.dis_file)))

In this way, the picture pendant can be displayed, and the effect is as follows:
insert image description here
the current desktop pendant is a static display, and now we can display the pendant animation carousel through the timer.

3. Implementation of animation pendant function

Animated Carousel:

self.timer = QTimer()
self.timer.timeout.connect(self.img_update)
self.timer.start(100)
def img_update(self):
    if self.img_num < self.img_count:
        self.img_num += 1
    else:
        self.img_num = 1
    self.img_path = './image/{file}/{img}.png'.format(file=self.dis_file, img=str(self.img_num))
    self.qpixmap = QPixmap(self.img_path)
    self.lab.setPixmap(self.qpixmap)

Drag the mouse to control the pendant position:

def mousePressEvent(self, QMouseEvent):
    if QMouseEvent.button() == Qt.LeftButton:
        self.pos_first = QMouseEvent.globalPos() - self.pos()
        QMouseEvent.accept()
        self.setCursor(QCursor(Qt.OpenHandCursor))

def mouseMoveEvent(self, QMouseEvent):
    if Qt.LeftButton:
        self.move(QMouseEvent.globalPos() - self.pos_first)
        print(self.pos())
        self.x, self.y = self.pos().x, self.pos().y
        QMouseEvent.accept()

def quit(self):
    self.close()
    sys.exit()

So far, the custom animation pendant function has been completed, and the animation pendant effect is as follows:
insert image description here

4. Packaging configuration

Some time ago, a fan asked me, can Python be packaged into an exe? How to pack it? Let us introduce it together today.

The commonly used packaging tool for Python is the third-party library Pyinstaller. First, you need to install pyinstaller.

pip install Pyinstaller

Next, we need to open the command window, switch to the project directory and execute the package command.

pyinstaller -F -i ./img.ico Qt_Gadgets.py

The commonly used parameters for packaging are as follows:

  • -F means generate a single executable

  • -w means to remove the console window, which is very useful in GUI interface. But if it is a command line program, then delete this option!

  • -p means that you customize the classpath to be loaded by yourself, which is generally not used

  • -i represents the icon of the executable file

A friend asked me before that the packaged icons need pictures with .ico suffix . How should I convert ordinary pictures into icon format .ico? Of course Python can help you achieve it, and I will teach you today. The core code is as follows:

import PythonMagick

# 生成图标ico(png格式图片转成ico)
img = PythonMagick.Image('./image/img1/1.png')
# 这里要设置一下尺寸,不然会报ico尺寸异常错误
img.sample('128x128')
img.write('./img.ico')

Now that the icon is obtained, we can proceed with the packaging operation.
insert image description here
After the packaging is complete, we can see that there will be a generated exe program in the project directory.
insert image description here
At this point, the entire custom animation pendant is completed. Let's run the exe together to see the effect of the animation pendant.

insert image description here

The source code and data have been uploaded, pay attention to the public account at the end of the article and reply to [source code] to get the complete source code

Python's past highlights:

Guess you like

Origin blog.csdn.net/hhladminhhl/article/details/119568090