一、问题描述
开发环境:Qt5.15.0、Win10、 Visual studio 2019、C++
在开发视频会议项目的过程中,被一个问题困扰了很久。就是整个视频会议的界面在拉伸四周改变大小的过程中,整个客户端界面闪烁的非常严重(非视频画面闪烁)。可以看到在下面的视频中,界面的背景会出现短暂的透明,非常影响使用感受。
图1.1 界面闪烁图
二、问题探索
一开始以为是因为子视频窗口多层嵌套,同时底层又传入了窗口句柄给D3D用于视频渲染,所以导致的在改变大小时 整个窗体在重绘时因为需要大量复杂的计算,导致背景和内容无法在同一个刷新周期内完成,于是做了以下尝试:
- 精简子窗口的界面
- 设置各种窗口属性比如Qt::WA_OpaquePaintEvent 、Qt::WA_PaintOnScreen等
- 在nativeEvent()中拦截WM_PAINT等事件进行双缓冲绘图处理
但是发现网上说的一些常见的解决窗口闪烁的方案都不起作用,只能暂时搁置。后来前几天有时间又来研究。突然想到这个现象和网上描述的不太一样,一般闪烁往往是因为在重绘过程中刷新出了窗口的默认的背景色,但是我的现象是在改变大小的过程的背景会直接变成透明,使我开始怀疑是因为我去除了边框,然后重写了鼠标拖动事件的代码逻辑有问题。于是我就尝试了取消设置Qt::FramelessWindowHint,让窗口露出系统自带的边框,竟然一下子就不闪了。
那为什么使用windows系统自带的边框就不闪了呢?这个东西到底是什么呢? 这里要引入一个概念:客户区和非客户区。我们以Qt的窗口体系为例,非客户区就是指标题栏,图标,窗口边框和标题按钮。而客户区就是我们平常操作geometry()获取的那一部分,一些在paintEvent()中的操作也是在客户区中进行的,Qt没有开放接口可以直接操作非客户区的样式,所以我们在开发过程中往往是直接设置Qt::FramelessWindowHint去除系统自带的标题栏和边框,然后通过自定义一个伪标题栏来实现定制化。
所以问题很可能就出现在去除边框后,客户区和非客户区大小相同,同时又叠加了视频渲染所导致的(