[Share Experiences] 如何在 Qt6 中将 QOpenGLWidget 嵌入 QWidget 显示 Resolved
Tofloor
poster avatar
忘记、过去
deepin
2025-03-14 15:32
Author

如何在 Qt6 中将 QOpenGLWidget 嵌入 QWidget 显示

前言

在 Qt5 中,使用 QOpenGLWidget 并加入 QLayout 布局嵌入 QWidget,是没有啥大问题的,无边框窗口效果都是正常的;但是在 Qt6 中,会莫名其妙出现窗口管理器原生标题栏,破坏无边框窗口效果。

在给 deepin 23 修改 dde-desktop 动态壁纸插件 时就不幸遇到了这个问题......
原本的代码使用 Qt5 + libdmr 播放视频,在早期 deepin 23 版本中,dde-desktop 使用 Qt5 编译,可以正常播放动态壁纸;后期 dde-desktop 修改为 dde-shell 插件,使用 Qt6 编译,一旦加入 QOpenGLWidget 播放视频,整个桌面就会从全屏状态变成一个有标题栏的窗口,即使修改窗口属性也无法去除标题栏。

libdmrdeepin-movie-reborn 提供的播放能力,类似 libmpv + QOpenGLWidget 方式

无奈之下,用了一些 骚操作 暂时规避了该问题,在桌面显示前先把 QOpenGLWidget 塞进布局,一起显示出来,就不会有标题栏了(但是 QOpenGLWidget 此后就不能析构了,一旦重新插入布局,标题栏就会出现)。

然而,这种操作在 deepin 25 上也不行了,咋搞都会导致桌面变成一个独立窗口。下定决心搜索了下 Qt opengl 相关的问题和文档,才搞清楚怎么回事......

省流

  • 以下操作均在 deepin 25 中进行测试,仓库 Qt6 版本为 6.8.0

方法一:修改父窗口 QSurfaceType

https://doc.qt.io/qt-6/qopenglwidget.html

Note: When dynamically adding a QOpenGLWidget into a widget hierarchy, e.g. by parenting a new QOpenGLWidget to a widget where the corresponding top-level widget is already shown on screen, the associated native window may get implicitly destroyed and recreated if the QOpenGLWidget is the first of its kind within its window. This is because the window type changes from RasterSurface to OpenGLSurface and that has platform-specific implications. This behavior is new in Qt 6.4.

大概意思就是从 Qt 6.4 开始,如果首次把 QOpenGLWidget 嵌入一个已经显示出来的 QWidget 中,会导致原生窗口被销毁并重建,因为窗口 type 从 RasterSurface 变成 OpenGLSurface

所以,可以在调用父对象 QWidget::show() 显示窗口前,提前修改 surfaceType 为 QSurfaceType::OpenGLSurface,可以避免 QOpenGLWidget 插入布局后出现标题栏:

#include 
#include 
#include 
......
    QMainWindow w;
    ......
    QOpenGLWidget childWidget = new QOpenGLWidget(w);
    w.windowHandle()->setSurfaceType(QSurface::OpenGLSurface);

    w.show();

方法二(不推荐):使用 QOpenGLWindow

https://doc.qt.io/qt-6/qopenglwidget.html

Alternatives
Adding a QOpenGLWidget into a window turns on OpenGL-based compositing for the entire window. In some special cases this may not be ideal, and the old QGLWidget-style behavior with a separate, native child window is desired. Desktop applications that understand the limitations of this approach (for example when it comes to overlaps, transparency, scroll views and MDI areas), can use QOpenGLWindow with QWidget::createWindowContainer(). This is a modern alternative to QGLWidget and is faster than QOpenGLWidget due to the lack of the additional composition step. It is strongly recommended to limit the usage of this approach to cases where there is no other choice. Note that this option is not suitable for most embedded and mobile platforms, and it is known to have issues on certain desktop platforms (e.g. macOS) too. The stable, cross-platform solution is always QOpenGLWidget.

大概翻译一下,QOpenGLWindow 和 QOpenGLWidget 使用方式类似,可以利用 QWidget::createWindowContainer() 把 QOpenGLWindow 转换为 QWidget 并塞进布局。

测试了一下,这种方式确实不会导致有标题栏,但问题也很大。根据帮助手册,这种方式无法保证窗口层级的正确显示。用在动态壁纸插件中,会导致视频层遮挡原本的桌面层,导致看不到图标也无法调用菜单......

同时还有性能问题、嵌入式和移动端不兼容等问题,不建议频繁使用。

效果

使用上述 方法一 修改 后,已经可以在 deepin 25 上正常加载动态壁纸啦~

录屏_dde-shell_20250314145440.gif

此间心愿已了,可以安心 涩涩 摸鱼了~

P.S. 想要在 deepin 25 Alpha 安装动态壁纸插件,可以在 这里 下载安装(大概是 Alpha 吧,咱这里加了 25 的 ci 仓库,编译依赖版本比较高......)

后记

deepin-deepinid-client 类似现象

根据我的记忆,第一次在 deepin 23 上听说类似的问题,应该是刚开始迁移 Qt6 的时候。以 deepin-deepinid-client 为例,登录 deepin ID 账号的客户端里面使用了 QWebEngineWidget 用于显示网页(也算是 opengl widget ?)。

早期版本里,登录界面就会显示两个标题栏。根据大佬的 提交记录,可以在窗口 id 变化时重新处理下,保证 Dtk 的无标题栏正常工作。

虽然不是很懂为啥,但还是准备把这段逻辑照搬到动态壁纸里......然而比较麻烦的是,动态壁纸的父对象应该是桌面,不太方便在插件代码中直接修改,就算用事件过滤器也放哪里都不是很合适,随即放弃了这个想法......

DMainWindow 相关处理

上周偶然翻了下 dtkwidget 的提交记录,正好看到了大佬新的 提交记录

当时还不明所以,只知道和记忆中 deepin-deepinid-client 的标题栏问题应该有点关系。今天仔细看了 Qt 文档才算知道具体原因......

如果要在 DMainWindow 中加入 webengine,也使用了方法一,在构造主窗口前就已经设置了 surfaceType,来保证标题栏正常显示效果。

所以,星火商店在龙芯平台上也使用了 Qt6 + Dtk6 + QWebEngineWidget 显示网页,是不是也要做相关处理呢......?

Reply Favorite View the author
All Replies
dxy2020
deepin
2025-03-14 15:51
#1

like

Reply View the author
deepin流云
Super Moderator
Community OP
2025-03-14 15:55
#2

大佬沉寂好久啦……终于又回来了?kissing_heart

Reply View the author
忘记、过去
deepin
2025-03-14 16:41
#3
deepin流云

大佬沉寂好久啦……终于又回来了?kissing_heart

Reply View the author
deepin流云
Super Moderator
Community OP
2025-03-14 16:54
#4

秒懂~欢迎多来____。like

Reply View the author