You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PyQt5的QLabel视频上方绘制图形而非设为背景?

解决PyQt5中视频QLabel上方绘制图形被覆盖的问题

你的问题核心是子部件QLabel会显示在主窗口绘制内容的上层,所以直接在主窗口的paintEvent里绘制的图形会被视频标签遮挡。下面提供两种可行的解决方案:

方法一:自定义QLabel,在标签内部绘制图形

直接继承QLabel重写它的paintEvent,先绘制视频帧,再在帧上方绘制图形。这样图形和视频都在同一个QLabel里,自然图形会显示在视频顶层。

修改后的完整代码:

import os
import sys
import cv2

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class ThreadOpenCV(QThread):
    changePixmap = pyqtSignal(QImage)

    def __init__(self, source):
        super().__init__()
        self.source = source
        self.running = True

    def run(self):
        print('start')
        cap = cv2.VideoCapture(self.source)
        self.running = True
        
        while self.running:
            ret, frame = cap.read()
            if ret:
                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                h, w, ch = frame.shape
                bytes_per_line = ch * w
                image = QImage(frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
                image = image.scaled(640, 480, Qt.KeepAspectRatio)
                self.changePixmap.emit(image)
            
        cap.release()
        print('stop')
        
    def stop(self):
        self.running = False

# 自定义带绘制功能的QLabel
class VideoLabel(QLabel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.video_pixmap = QPixmap()

    def setVideoPixmap(self, pixmap):
        self.video_pixmap = pixmap
        self.update()  # 触发重绘

    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter(self)
        # 先绘制视频帧
        if not self.video_pixmap.isNull():
            painter.drawPixmap(self.rect(), self.video_pixmap)
        # 再绘制图形
        pen = QPen(Qt.red, 3)
        painter.setPen(pen)
        # 以标签自身的中心绘制椭圆,避免依赖窗口尺寸
        center = self.rect().center()
        painter.drawEllipse(center.x()-25, center.y()-25, 50, 50)

class Widget(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        PATH_TO_VIDEO = os.path.join(os.getcwd(), 'videoname.mp4')

        self.thread = ThreadOpenCV(PATH_TO_VIDEO)
        self.thread.changePixmap.connect(self.setImage)
        
        # layout and adding buttons and images
        layout = QVBoxLayout()

        # 使用自定义的VideoLabel
        self.label_video = VideoLabel()
        layout.addWidget(self.label_video)

        self.btn1 = QPushButton("PLAY")
        self.btn1.clicked.connect(self.playVideo)
        layout.addWidget(self.btn1)

        self.btn_stop = QPushButton("STOP")
        self.btn_stop.clicked.connect(self.stopVideo)
        layout.addWidget(self.btn_stop)
        
        self.widget = QWidget()
        self.widget.setLayout(layout)
        self.setCentralWidget(self.widget)
        
    def playVideo(self):
        self.thread.start()

    def stopVideo(self):
        self.thread.running = False

    def setImage(self, image):
        pixmap = QPixmap.fromImage(image)
        self.label_video.setVideoPixmap(pixmap)

if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    mw = Widget()
    mw.show()
    app.exec()

关键修改点:

  • 创建VideoLabel继承QLabel,新增setVideoPixmap方法接收视频帧
  • 重写VideoLabel的paintEvent,先调用父类方法(保证QLabel的基础绘制),再绘制视频帧和图形
  • 图形绘制基于标签自身的矩形区域,避免受窗口尺寸变化影响

方法二:在QLabel上方叠加透明绘制层

在视频QLabel的同一布局位置,叠加一个透明的QWidget,专门用于绘制图形。这个透明部件会显示在QLabel上方,且不遮挡视频内容。

核心代码片段:

class DrawOverlay(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 设置部件透明,不遮挡下方视频
        self.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.setAttribute(Qt.WA_NoSystemBackground)

    def paintEvent(self, event):
        painter = QPainter(self)
        pen = QPen(Qt.red, 3)
        painter.setPen(pen)
        center = self.rect().center()
        painter.drawEllipse(center.x()-25, center.y()-25, 50, 50)

# 在Widget的__init__中修改布局部分:
layout = QVBoxLayout()
# 创建一个容器放置视频标签和叠加层
video_container = QWidget()
video_layout = QVBoxLayout(video_container)
video_layout.setContentsMargins(0,0,0,0)

self.label_video = QLabel()
video_layout.addWidget(self.label_video)

# 添加叠加层
self.draw_overlay = DrawOverlay(video_container)
self.draw_overlay.setGeometry(self.label_video.rect())
# 监听标签尺寸变化,同步更新叠加层尺寸
self.label_video.resizeEvent = lambda e: self.draw_overlay.setGeometry(self.label_video.rect())

layout.addWidget(video_container)

方法二说明:

  • DrawOverlay是透明的QWidget,只负责绘制图形
  • 把视频标签和叠加层放在同一个容器里,确保叠加层始终覆盖在视频上方
  • 通过监听视频标签的尺寸变化,同步调整叠加层的大小,保证图形位置正确

两种方法都能实现“视频上方绘制图形”的需求,方法一更简洁高效,推荐优先使用。

内容的提问来源于stack exchange,提问作者Bosseness

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 00:37:16