You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows下Qt的QScreen refreshRateChanged信号未触发问题排查

Qt 6.6.0 下Windows平台刷新率变化、主屏切换无回调的问题分析与解决

问题描述

我开发的Qt程序需要感知显示器刷新率变化,逻辑为:窗口全屏时适配所在显示器刷新率,非全屏时沿用主屏刷新率。最初实现逻辑:

  • 窗口全屏时通过screen()方法获取当前QScreen并检查刷新率
  • 连接QGuiApplication::primaryScreenChanged()信号监听主屏切换

但实际测试中遇到两个问题:

  1. 修改显示器刷新率后,QScreen::refreshRateChanged信号未触发,QScreen的刷新率值也未更新
  2. 切换系统主屏时,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 00:40:04