前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【QT】获取主屏幕DPI

【QT】获取主屏幕DPI

原创
作者头像
lealc
发布2024-09-05 20:01:38
2200
发布2024-09-05 20:01:38

背景

在DpiAware = SystemAware的情况下需要获取主屏的DPI值,

DPI感知

DPI(Dots Per Inch)是指每英寸的点数,通常用于描述屏幕分辨率。在Windows操作系统中,DPI感知(DPI Awareness)是指应用程序能够感知到屏幕的DPI设置,并根据DPI值调整其界面元素的大小和布局,以提供更好的用户体验。

DPI感知有两种模式:系统DPI感知和每个监视器DPI感知。

系统DPI感知(System aware)

系统DPI感知是指应用程序根据整个系统的DPI设置来调整其界面元素的大小和布局。这种模式下,当用户更改系统DPI设置时,所有应用程序的界面都会相应地调整。

每个监视器DPI感知(Per Monitor)

每个监视器DPI感知是指应用程序能够检测到每个显示器的DPI设置,并根据每个显示器的DPI值分别调整其界面元素的大小和布局。这种模式下,当用户在不同DPI设置的显示器之间移动应用程序窗口时,应用程序的界面会自动适应每个显示器的DPI设置。

注意事项

在实现DPI感知时,需要确保应用程序的界面元素能够正确地缩放,以避免在高DPI设置下出现模糊或过小的情况。

在使用每个监视器DPI感知时,需要注意处理不同显示器之间的DPI变化,以确保应用程序的界面在不同显示器之间保持一致。

在编写DPI感知应用程序时,建议使用支持高DPI的UI框架,如Windows Presentation Foundation (WPF)或Qt等。

QT应用

qt应用程序为了默认支持高清屏,设置的DPI感知类型为Per Monitor,以下为5.15.2源码

时机为程序创建第一个窗口之前,所以需要修改DPI感知类型需要在这个时机之前,否则会有warning提示设置失败。

代码语言:C
复制
// Src\qtbase\src\plugins\platforms\windows\qwindowsintegration.cpp
QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(const QStringList &paramList)
{
    initOpenGlBlacklistResources();

    static bool dpiAwarenessSet = false;
    int tabletAbsoluteRange = -1;
    // Default to per-monitor awareness to avoid being scaled when monitors with different DPI
    // are connected to Windows 8.1
    QtWindows::ProcessDpiAwareness dpiAwareness = QtWindows::ProcessPerMonitorDpiAware;
    m_options = parseOptions(paramList, &tabletAbsoluteRange, &dpiAwareness);
    QWindowsFontDatabase::setFontOptions(m_options);

    if (m_context.initPointer(m_options)) {
        QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents);
    } else {
        m_context.initTablet(m_options);
        if (tabletAbsoluteRange >= 0)
            m_context.setTabletAbsoluteRange(tabletAbsoluteRange);
    }

    if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication.
        if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) {
            m_context.setProcessDpiAwareness(dpiAwareness);
            qCDebug(lcQpaWindows)
                << __FUNCTION__ << "DpiAwareness=" << dpiAwareness
                << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness();
        }
        dpiAwarenessSet = true;
    }

    m_context.initTouch(m_options);
    QPlatformCursor::setCapability(QPlatformCursor::OverrideCursor);

    m_context.initPowerNotificationHandler();
}

Windows上主动设置qt应用的DPI感知,需要判断系统不低于windows8.1

代码语言:C
复制
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1) {
    return;
}

HMODULE shcoreModule = LoadLibraryW(L"SHCore.dll");
if (!shcoreModule) {
    qInfo() << "LoadLibraryW SHCore.dll error:" << GetLastError();
    return;
}

typedef HRESULT(WINAPI * SetProcessDpiAwareness)(int);
SetProcessDpiAwareness setProcessDpiAwareness =
    (SetProcessDpiAwareness)GetProcAddress(shcoreModule, "SetProcessDpiAwareness");

if (setProcessDpiAwareness) {
    HRESULT res = setProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE);
    if (res != S_OK) {
        qInfo() << "SetProcessDpiAwareness error:" << res;
    } else {
        qInfo() << "SetProcessDpiAwareness success";
    }
}

if (shcoreModule) {
    FreeLibrary(shcoreModule);
}

获取主屏DPI

在默认qt程序下,获取主屏DPI需要先调整DPI感知类型然后再获取,否则会拿到错误的DPI值,主要利用SHCore.dll和User32.dll两个系统模块,系统不低于windows 8.1

话不多说,直接上代码,仅供参考学习

代码语言:C
复制
bool GetPrimaryMonitorDpi(float &dpi) {
    bool bRes = false;

    HMODULE shcoreModule = nullptr;

    do 
    {
        if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1) {
            break;
        }

        class ScopedPerMonitorAware {
           typedef DPI_AWARENESS_CONTEXT(WINAPI* SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);

           public:
            ScopedPerMonitorAware() {
                user32Module_ = LoadLibraryW(L"User32.dll");
                if (!user32Module_) {
                    return;
                }

                setThreadDpiAwarenessContext_ =
                    (SetThreadDpiAwarenessContext)GetProcAddress(user32Module_, "SetThreadDpiAwarenessContext");
                if (!setThreadDpiAwarenessContext_) {
                    return;
                }

                bak_ = setThreadDpiAwarenessContext_(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
                if (bak_ == NULL)
                    qInfo() << __FUNCTION__ << " SetThreadDpiAwarenessContext failed " << ::GetLastError();
            }
            ~ScopedPerMonitorAware() {
                if (bak_ != NULL && setThreadDpiAwarenessContext_) {
                    setThreadDpiAwarenessContext_(bak_);
                }
                
                if (user32Module_) {
                    FreeLibrary(user32Module_);
                }
            }

           private:
            DPI_AWARENESS_CONTEXT bak_;
            HMODULE user32Module_ = nullptr;
            SetThreadDpiAwarenessContext setThreadDpiAwarenessContext_ = nullptr;
        } scoped_per_monitor_aware;

        HMODULE shcoreModule = LoadLibraryW(L"SHCore.dll");
        if (!shcoreModule) {
            break;
        }

        typedef HRESULT(WINAPI * GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*);
        GetDpiForMonitor getDpiForMonitor =
            (GetDpiForMonitor)GetProcAddress(shcoreModule, "GetDpiForMonitor");

        if (!getDpiForMonitor) {
            break;
        }

        HMONITOR monitor = ::MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY);
        UINT result = 0, dpiY = 0;
        const auto ret = getDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &result, &dpiY);
        if (ret != S_OK) {
            break;
        }

        dpi = dpiY / 96.0f;
        bRes = true;
    } while (0);

    if (shcoreModule) {
        FreeLibrary(shcoreModule);
    }

    return bRes;
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • DPI感知
    • 系统DPI感知(System aware)
      • 每个监视器DPI感知(Per Monitor)
        • 注意事项
        • QT应用
        • 获取主屏DPI
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档