Windows下Qt的QScreen refreshRateChanged信号未触发问题排查
Qt 6.6.0 下Windows平台刷新率变化、主屏切换无回调的问题分析与解决
问题描述
我开发的Qt程序需要感知显示器刷新率变化,逻辑为:窗口全屏时适配所在显示器刷新率,非全屏时沿用主屏刷新率。最初实现逻辑:
- 窗口全屏时通过
screen()方法获取当前QScreen并检查刷新率 - 连接
QGuiApplication::primaryScreenChanged()信号监听主屏切换
但实际测试中遇到两个问题:
- 修改显示器刷新率后,
QScreen::refreshRateChanged信号未触发,QScreen的刷新率值也未更新 - 切换系统主屏时,
primaryScreenChanged信号无响应
调试时重写QWindow::nativeEvent()发现,系统发送的WM_DISPLAYCHANGE消息(实测刷新率变化也会触发该消息)被Qt归类到QtWindows::UnknownEvent分支,直接返回false未做后续处理。
最小复现代码:
#include <QGuiApplication> #include <QQuickView> struct Foo : QObject { public slots: void refreshRateChangedCallback(qreal refreshRate) { qDebug() << "Refresh rate of monitor changed! " << refreshRate; } void primaryScreenChangedCallback(QScreen *screen) { qDebug() << "Primary screen changed! " << screen->name(); } }; int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickView view; view.setSource(u"qrc:/test/Main.qml"_qs); view.show(); QScreen *primaryScreen = view.screen(); Foo *test = new Foo; QObject::connect(primaryScreen, &QScreen::refreshRateChanged, test, &Foo::refreshRateChangedCallback); QObject::connect(&app, &QGuiApplication::primaryScreenChanged, test, &Foo::primaryScreenChangedCallback); return app.exec(); }
环境信息:Qt 6.6.0(MSVC 2019编译),Windows 10 Enterprise 22H2(OS版本19045.3930)
原因分析
这是Qt 6.6.0在Windows平台的已知问题,核心原因是:
- Windows系统在刷新率修改、主屏切换时都会发送
WM_DISPLAYCHANGE消息,但Qt 6.6.0的Windows平台插件仅处理了分辨率变化的场景 - 该消息被归类为
UnknownEvent后直接跳过处理,导致QScreen的属性未更新,对应信号也无法触发
解决方案
方式一:手动处理WM_DISPLAYCHANGE消息
重写窗口的nativeEvent()方法,捕获消息后主动更新QScreen信息并触发对应信号:
#include <QGuiApplication> #include <QQuickView> #include <windows.h> class CustomQuickView : public QQuickView { protected: bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override { MSG *msg = static_cast<MSG*>(message); if (msg->message == WM_DISPLAYCHANGE) { // 更新当前窗口关联屏幕的属性 if (QScreen *screen = this->screen()) { screen->updateGeometry(); // 强制从系统查询最新属性 // 检测刷新率变化并手动触发信号 static qreal lastRefreshRate = screen->refreshRate(); qreal currentRate = screen->refreshRate(); if (qAbs(currentRate - lastRefreshRate) > 0.01) { Q_EMIT screen->refreshRateChanged(currentRate); lastRefreshRate = currentRate; } } // 检测主屏切换并手动触发信号 QScreen *newPrimary = QGuiApplication::primaryScreen(); static QScreen *lastPrimary = newPrimary; if (newPrimary != lastPrimary) { Q_EMIT QGuiApplication::primaryScreenChanged(newPrimary); lastPrimary = newPrimary; } } return QQuickView::nativeEvent(eventType, message, result); } }; struct Foo : QObject { public slots: void refreshRateChangedCallback(qreal refreshRate) { qDebug() << "Refresh rate of monitor changed! " << refreshRate; } void primaryScreenChangedCallback(QScreen *screen) { qDebug() << "Primary screen changed! " << screen->name(); } }; int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); CustomQuickView view; view.setSource(u"qrc:/test/Main.qml"_qs); view.show(); Foo *test = new Foo; QObject::connect(view.screen(), &QScreen::refreshRateChanged, test, &Foo::refreshRateChangedCallback); QObject::connect(&app, &QGuiApplication::primaryScreenChanged, test, &Foo::primaryScreenChangedCallback); return app.exec(); }
方式二:升级Qt版本
该问题在Qt 6.7及以上版本中已被官方修复,Windows平台插件完善了WM_DISPLAYCHANGE消息的处理逻辑,能够正确解析刷新率变化和主屏切换事件并触发对应信号。若项目允许,直接升级到Qt 6.7+即可解决问题。
内容的提问来源于stack exchange,提问作者iexav
相关产品推荐
相关产品推荐

