Qt-无边框窗口 设置任务栏缩略图
本文最后更新于:2022年1月30日 晚上
前情提要
任务栏缩略图可以在不激活窗口的状态下提供窗口的缩略信息
对于音乐播放器等软件,尤其重要,不仅可以增加观感,还能添加控制按钮
是专业程序员的不二之选
且
都1202年了,不会还有人不用无边框窗口(Framless)
吧
でも
Qt
自带的QWinThumbnailToolBar
类是可以很方便地进行任务栏缩略图的控制
但是,对于无边框窗口,貌似出现了一些问题:
- 缩略图周围出现了[类似标题栏的边框] 导致图片错位 十分影响美观
在仔细阅读了Qt
文档后,并没有什么收获,该类十分简洁,并无多于函数可供操作
焯
止まるんじゃねよ
不要停下来啊 团长!
毕竟Qt
还是封装的Windows API
所以还得从Windows API
文档入手
在搜寻了关于TaskBar
的内容后,我找到了这样一个函数:DwmSetIconicThumbnail
这个函数与Qt-QWinThumbnailToolBar::setIconicThumbnailPixmap
不能说很像,简直就是同父异母
DwmSetIconicThumbnail
1 |
|
重点在于最后一个参数 dwSITFlags
:
- 0 (0x00000000) - 提供的缩略图周围不显示任何框架
- DWM_SIT_DISPLAYFRAME (0x00000001) - 在提供的缩略图周围显示一个框架
主要就在于这个参数中的框架二字,联想我们之前出现的错位情况,那可不就是多了一个框架嘛
那么只要将参数设置为0,不就可以解决了嘛
根源
可是,Qt
为什么会出现这样的问题,难道是!
说罢,我便去查阅了Qt
源码:
やはり啊
1 |
|
注意观察第7行:
1 |
|
最后一个参数 写死了 dWM_SIT_DISPLAYFRAME
导致强制产生边框,与无边框窗口
产生冲突
而且还不提供接口修改参数
Qt
程序员也太不走心了吧
Solution
那咋办
其实哈,与Windows API
打交道是很烦人的一件事
所以,能少写就少写,尽量用Qt
封装的类
だから、我们将采用Qt API
与Windows API
结合的方式 完成缩略图
毕竟 就只有setIconicThumbnailPixmap
这一个函数写得不好嘛
我们只要用DwmSetIconicThumbnail
代替它即可
1 |
|
困难总比办法多
即可?什么TM
的叫TM
的即可
想在Qt
中使用这样一个冷门的Windows API
可不容易
+头文件:dwmapi.h
Error:undefined function 缺少定义
What,不是加了头文件吗,一看,由于 _WIN32_WINNT
数值过低(版本不足),导致定义屏蔽
头文件中覆盖宏定义:
1 |
|
Error:undefined reference 缺少引用(需要链接库)
+.pro:LIBS += -lDwmapi -lGdi32
这就完了?Nonono
缩略图大小是有限制的,Windows
不会自动帮你缩放
那么如何知道大小限制
答:在Windows
传递的native Message中有
但是很可惜 我们用了QWinThumbnailToolBar
,导致事件被拦截,我们无法获取
焯
什么叫寄人篱下
寄
这时候就只能想点偷鸡摸狗的办法了
DwmSetIconicThumbnail
函数的返回值可以标识调用是否成功
图片过大则不成功
Size
从大到小开始遍历,直到返回S_OK
我滴任务,完成啦!啊哈哈哈哈
TNND 跟我玩阴的是吧
遍历Size会导致耗时过长,导致缩略图刷新不及时,显示为空白(需要鼠标移动刷新)
焯
根源还是在于获取不到maxSize
(Qt
类获取NativeEvent
后return true
拦截消息 不再传递)
所以还是得想办法以高优先级 获取NativeEvent
直接来吧
参考Qt
源码
1 |
|
要想以高优先级获取NativeEvent
必须installNativeEventFilter
并且重载nativeEventFilter
函数
但要想重载这个函数,就必须实现QAbstractNativeEventFilter
接口
在C++
中,采用多继承实现:
1 |
|
关于installNativeEventFilter
If multiple event filters are installed, the filter that was installed last is activated first.
也就是如果多个filter被安装,则后安装的先激活
我们要与QWinThumbnailBar
竞争所以必须在其之后安装,也就是在它构造之后
1 |
|
这样,在nativeEventFilter
中就可以接收到请求缩略图和实时预览图的消息了
1 |
|
原本的信号连接就可以删了,但是
1 |
|
这句话还是得留着的↑
我滴任务 又完成啦 啊哈哈哈哈
ちょっと待って(2022.1.30)
既然是对类的修正 就应该封装为类
重点就是:
- 继承
QAbstractNativeEventFilter
- 安装过滤器
QCoreApplication::instance()->installNativeEventFilter(this);
- 重载
nativeEventFilter
并捕获WM_DWMSENDICONICTHUMBNAIL
消息 - 重写
setIconicThumbnailPixmap
,采用Windows API(DwmSetIconicThumbnail)
注:删去对WM_DWMSENDICONICLIVEPREVIEWBITMAP
的捕获,没必要,直接用原生的即可
附:Qt源码
https://code.woboq.org/qt5/qtwinextras/src/winextras/qwinthumbnailtoolbar.cpp.html
附:QtWinThumbnailToolBar原理浅析(Qt源码)
Native
事件交给单独的QWinThumbnailToolBarPrivate
类(继承自QAbstractNativeEventFilter
)处理,本体持有其指针
接收到Native
事件后,开始更新缩略图(如果用setIconicThumbnailPixmap
设置过的话)
并emit iconicThumbnailPixmapRequested
供用户更新缩略图
如果要调用Windows API
手动设置缩略图 就不能调用setIconicThumbnailPixmap
否则会被覆盖
setIconicLivePreviewPixmap
不进行实际更新
实际更新在Native Event
时updateIconicLivePreview()
所以我们在接收事件时必须往下传递,不能return true
,否则没有代码去实现LivePreview
了
# 其实根本没有必要重写与拦截LivePreview
事件,用原生的就行
# 重写Thumbail
就行
Reference
任务栏扩展 - Win32 apps | Microsoft Docs
DwmSetIconicThumbnail 函数 (dwmapi.h) - Win32 应用 | 微软文档
Update WINVER and _WIN32_WINNT | Microsoft Docs
一个体验好的Windows 任务栏缩略图开发心得 - 网易数帆 - 博客园
C++ (Cpp) DwmSetIconicThumbnail Examples - HotExamples
c++ - Qt::nativeEvent 调用 - IT工具网