Frontend Development 12 min read

Creating a Custom Dynamic Desktop Wallpaper with Python and PyQt5

This tutorial walks through building a dynamic desktop wallpaper on Windows using Python's PyQt5, covering UI layout design, video loading and preview, desktop handle acquisition, wallpaper rendering, and graceful shutdown, with complete code examples for each step.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Creating a Custom Dynamic Desktop Wallpaper with Python and PyQt5

The article presents a practical case study for developing a custom dynamic desktop wallpaper using Python and the PyQt5 framework. It begins with an overview of the required functionalities, including video extraction, video carousel, PyQt5 window configuration, desktop handle acquisition, and the implementation of the dynamic wallpaper itself.

Core Feature Design outlines the main tasks: UI layout design, loading local video files, applying the dynamic wallpaper, and providing a way to close it. These tasks are broken down into four sequential steps.

Implementation Steps

1. UI Layout Design – The UI is built with PyQt5 widgets such as QPushButton, QGroupBox, and QGridLayout. The essential code for setting up the main window and its components is shown below:

import os
import sys
from subprocess import call
from threading import Thread
from time import sleep
import cv2
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QImage, QPixmap, QIcon
from PyQt5.QtWidgets import QGridLayout, QPushButton, QMainWindow, QFileDialog, QLabel, QSystemTrayIcon, QAction, QMenu, QMessageBox
# author:CSDN-Dragon少年
def setupUi(self, MainWindow):
    MainWindow.setObjectName("MainWindow")
    MainWindow.resize(505, 615)
    MainWindow.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
    self.centralwidget = QtWidgets.QWidget(MainWindow)
    self.centralwidget.setObjectName("centralwidget")
    self.pushButton = QtWidgets.QPushButton(self.centralwidget)
    self.pushButton.setGeometry(QtCore.QRect(22, 10, 89, 31))
    self.pushButton.setObjectName("pushButton")
    self.pushButton.clicked.connect(self.openmp4)
    self.pushButton.setStyleSheet('''QPushButton{background:#F7D674;border-radius:5px;}QPushButton:hover{background:yellow;}''')
    # ... (additional widget setup omitted for brevity) ...

2. Video Loading and Preview – Users select a video file via a file dialog, after which a separate thread reads the video frames using OpenCV and displays them in a QLabel. The key functions are:

# author:CSDN-Dragon少年
def openmp4(self):
    try:
        global path
        path, filetype = QFileDialog.getOpenFileName(None, "选择文件", ".", "视频文件(*.AVI;*.mov;*.rmvb;*.rm;*.FLV;*.mp4;*.3GP)")
        if path == "":
            return
        self.slotStart()
        t = Thread(target=self.Stop)
        t.start()
    except Exception as e:
        print(e)
# author:CSDN-Dragon少年
def slotStart(self):
    videoName = path
    if videoName != "":
        self.cap = cv2.VideoCapture(videoName)
        self.timer_camera.start(50)
        self.timer_camera.timeout.connect(self.openFrame)
# author:CSDN-Dragon少年
def openFrame(self):
    if self.cap.isOpened():
        ret, self.frame = self.cap.read()
        if ret:
            frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
            height, width, bytesPerComponent = frame.shape
            bytesPerLine = bytesPerComponent * width
            q_image = QImage(frame.data, width, height, bytesPerLine, QImage.Format_RGB888).scaled(self.label.width(), self.label.height())
            self.label.setPixmap(QPixmap.fromImage(q_image))
        else:
            self.cap.release()
            self.timer_camera.stop()

3. Dynamic Wallpaper Rendering – The desktop window handle is obtained using the win32gui library, and the video widget is re‑parented to the desktop so that the video plays as the wallpaper. The handle acquisition code is:

# author:CSDN-Dragon少年
def pretreatmentHandle():
    hwnd = win32gui.FindWindow("Progman", "Program Manager")
    win32gui.SendMessageTimeout(hwnd, 0x052C, 0, None, 0, 0x03E8)
    hwnd_WorkW = None
    while True:
        hwnd_WorkW = win32gui.FindWindowEx(None, hwnd_WorkW, "WorkerW", None)
        if not hwnd_WorkW:
            continue
        hView = win32gui.FindWindowEx(hwnd_WorkW, None, "SHELLDLL_DefView", None)
        if not hView:
            continue
        h = win32gui.FindWindowEx(None, hwnd_WorkW, "WorkerW", None)
        while h:
            win32gui.SendMessage(h, 0x0010, 0, 0)  # WM_CLOSE
            h = win32gui.FindWindowEx(None, hwnd_WorkW, "WorkerW", None)
        break
    return hwnd

The main window class inherits from QMainWindow and the generated UI class, sets up a QMediaPlayer, and re‑parents both the window and the video widget to the desktop handle:

# author:CSDN-Dragon少年
class MyMainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.player = QMediaPlayer()
        self.player.setNotifyInterval(10000)
        self.player.setVideoOutput(self.ui.videowidget)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setupUi(self)
        self.go()

    def go(self):
        self.ui.videowidget.setFullScreen(True)
        with open("./filename.txt", 'r', encoding='utf-8') as f:
            file_name = f.read() or 'lkf.mp4'
        if not os.path.exists(file_name):
            sys.exit()
        media = QMediaContent(QUrl(file_name))
        self.player.setMedia(media)
        self.mplayList = QMediaPlaylist()
        self.mplayList.addMedia(QMediaContent(QUrl.fromLocalFile(file_name)))
        self.player.setPlaylist(self.mplayList)
        self.mplayList.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop)
        win_hwnd = int(self.winId())
        video_h = int(self.ui.videowidget.winId())
        win32gui.SetParent(win_hwnd, h)
        win32gui.SetParent(video_h, h)
        win32gui.SetParent(video_h, win_hwnd)
        self.player.play()

4. Closing the Dynamic Wallpaper – A simple function terminates the external playback executable and releases resources:

# author:CSDN-Dragon少年
def close_wall(self):
    try:
        call('taskkill /F /IM play.exe')
    except:
        pass

The article concludes with screenshots showing the UI, video preview, and the final dynamic wallpaper effect, and provides a link to the original CSDN post for further reading.

Pythonvideo processingDesktop ApplicationPyQt5Dynamic Wallpaper
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.