前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >qt多屏不同DPI下的拖拽问题

qt多屏不同DPI下的拖拽问题

原创
作者头像
lealc
发布2024-07-22 20:04:44
2882
发布2024-07-22 20:04:44

背景

在主屏设置DPI=1.5,副屏设置DPI=1.0时,将qt窗口移动到副屏,拖拽qt treeview或者listview中的元素时,会发生异常显示,位置错误等问题

定位

这里复现之后,考虑的就是windows下面的Dpi感知,从任务管理器查看DPI感知为【系统】,符合预期,但是为什么还会发生这个异常问题呢

仔细定位这里问题发现,出现问题是因为接管了拖拽事件,绘制对应的分割线和移动目标元素都是通过下面代码来找到拖向元素

代码语言:c
复制
tree_view->indexAt(tree_view->mapFromGlobal(QCursor::pos()));

这里mapFromGlobal出来的坐标显然出现异常导致拖向元素不准确,出现一系列问题

跟进qt源码,发现qt在win8.1之后会自动设置DPI感知为PerMonitor类型,来避免不同DPI的多屏缩放问题

代码语言:c
复制
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();
}

解决

这里qt设置QtWindows::ProcessPerMonitorDpiAware显然不符合预期,需要修改默认DPI感知值,而setProcessDpiAwareness仅支持进程生命周期期间设置一次,所以需要提前qt来设置

代码语言:c
复制
#if defined(Q_OS_WIN)
    // 设置DPIAware为SystemAware
    InitDpiAware();
#endif

void InitDpiAware() {
    qInfo() << "InitDpiAware";
    if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1) {
        qInfo() << "InitDpiAware system version lower for win8.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);
    }
}

注意:这里需要在创建第一个窗口前设置,否则会晚于qt设置,导致设置失败。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 定位
  • 解决
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档