Qt5如何在支持透明效果的QVideoWidget上使用paintEvent绘制内容
Qt5 QVideoWidget播放RTSP流叠加标注无遮挡解决方案
问题根本原因
QVideoWidget的视频渲染默认走独立硬件加速管线,和普通QWidget的软件渲染管线分离,直接叠加控件、或者绘制时未先渲染视频帧的情况下,就会出现标注遮挡视频、透明属性失效穿透到桌面的问题。你之前添加的Qt::WA_X11OpenGLOverlay属性会绕过窗口管理器合成逻辑,直接渲染到屏幕最底层,不适合当前场景,直接删除该行代码即可。
推荐解决方案
方案一:改用QGraphicsVideoItem+QGraphicsView架构(兼容性最好)
该架构原生支持多层元素分层渲染,是Qt官方推荐的视频叠加标注实现方案,适配全平台,不会出现遮挡问题:
- 初始化场景、视图,关闭视图默认背景,开启全视口更新
- 底层添加
QGraphicsVideoItem作为视频输出载体,绑定到你的QMediaPlayer实例 - 标注可以通过自定义
QGraphicsItem添加到场景上层,也可以重写QGraphicsView的paintEvent直接绘制
核心示例代码:
// 初始化图形场景和视图 QGraphicsScene *scene = new QGraphicsScene(this); QGraphicsView *videoView = new QGraphicsView(scene, this); videoView->setStyleSheet("background: transparent;"); videoView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); videoView->show(); // 添加视频渲染项 QGraphicsVideoItem *videoItem = new QGraphicsVideoItem(); scene->addItem(videoItem); // 你的QMediaPlayer实例绑定视频输出 mediaPlayer->setVideoOutput(videoItem); // 上层绘制标注示例:重写QGraphicsView的paintEvent void CustomGraphicsView::paintEvent(QPaintEvent *event) { QGraphicsView::paintEvent(event); QPainter painter(viewport()); painter.setPen(QPen(Qt::red, 3)); painter.drawText(50,50,"自定义标注"); painter.drawRect(100,100,300,200); }
方案二:子类化QVideoWidget重写paintEvent
如果要保留QVideoWidget的使用方式,直接继承QVideoWidget重写绘制逻辑即可,注意必须先调用父类的paintEvent渲染视频帧,再绘制标注:
- 自定义子类继承QVideoWidget,重写paintEvent方法
- 关闭QVideoWidget的自动背景填充,必要时关闭
WA_PaintOnScreen属性强制走统一绘制管线
核心示例代码:
// 自定义视频控件类 class CustomVideoWidget : public QVideoWidget { Q_OBJECT public: CustomVideoWidget(QWidget *parent = nullptr) : QVideoWidget(parent) { setAutoFillBackground(false); setAttribute(Qt::WA_PaintOnScreen, false); } protected: void paintEvent(QPaintEvent *event) override { // 先调用父类方法渲染底层视频帧 QVideoWidget::paintEvent(event); // 再在上层绘制自定义标注 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(QPen(Qt::green, 2)); painter.drawEllipse(200,200,100,100); } };
写完之后将UI文件中的默认QVideoWidget提升为CustomVideoWidget类即可。
方案三:QLabel叠加适配(兼容性一般)
如果一定要用叠加QLabel的实现方式,需要给QLabel开启透明背景属性,重写QLabel的paintEvent避免填充背景:
QLabel *overlayLabel = new QLabel(ui->videoWidget); overlayLabel->resize(ui->videoWidget->size()); // 开启透明背景 overlayLabel->setAttribute(Qt::WA_TranslucentBackground); overlayLabel->setStyleSheet("background-color: transparent;"); overlayLabel->show(); // 重写QLabel的paintEvent绘制标注 void CustomLabel::paintEvent(QPaintEvent *event) { Q_UNUSED(event) QPainter painter(this); painter.setPen(Qt::blue); painter.drawText(100,100,"叠加层标注"); }
该方案在部分开启强硬件加速的平台可能依然存在透明失效问题,优先选择前两个方案。
内容的提问来源于stack exchange,提问作者Przemysław Wojcik
相关产品推荐
相关产品推荐

