适配高DPI QWidget::move移动有错误?

本文介绍了在Qt中适配高DPI时,遇到QWidget::move和setGeometry方法导致坐标及尺寸不符的问题。通过解析Qt内部缩放公式,提供了解决方法和代码实现,帮助读者理解并修复高DPI环境下窗口移动和大小设置的误差。
摘要由CSDN通过智能技术生成

适配高DPI QWidget::move移动有错误?

在这里插入图片描述

1、现象

在适配高DPI文章发布之后,有个小伙伴立马联系我说增加了这个特性之后,发现一个移动坐标的问题。

比如说:QWidget::move(500, 500),在实际的高DPI屏幕上移动的像素并不是QPoint(500, 500),而是比QPoint(500, 500)要大。

我觉得挺有意思,立马就写个demo测试下,果然如他所说的。

同理我测试了QWidget::setGeometry()也是这样的。

这个现象还是挺奇怪的,让我们一探究竟。

2、解决方法

老规矩,我先说下解决方案。在编译的时候可能会报错,你需要在你的vs中添加头文件目录

在这里插入图片描述

#include <QtGui/5.15.2/QtGui/private/qhighdpiscaling_p.h>

BOOL g_RecalcPoint(QWidget *widget, const QRect &oldRect, QPoint &point)
{
    if (nullptr == widget || nullptr == widget->windowHandle())
    {
        return FALSE;
    }
	// 获取到缩放之后的坐标
    QRect screenAboutRect = QHighDpi::toNativePixels(oldRect, widget->windowHandle()->screen());
    // 获取缩放比例
    QHighDpiScaling::ScaleAndOrigin scale = QHighDpiScaling::scaleAndOrigin(widget->windowHandle()->screen());
    // 目标点
    QPoint x1 = QPoint(oldRect.x(), oldRect.y());
    QPoint x2 = QPoint(screenAboutRect.x(), screenAboutRect.y());
    if (0 == scale.factor)
        scale.factor = 1.0;
    // 缩放公式逆推
    QPoint x3 = ((scale.factor+1)*x1 - x2)/scale.factor;
    point = x3;
    return TRUE;
}

//计算高度
BOOL g_RecalcRect(QWidget *widget, const QRect &oldRect, QRect &newRect, bool updateWidget, bool updateHeight)
{
    if (nullptr == widget || nullptr == widget->windowHandle())
    {
        return FALSE;
    }

    newRect = oldRect;
    QHighDpiScaling::ScaleAndOrigin scale = QHighDpiScaling::scaleAndOrigin(widget->windowHandle()->screen());
    if (updateWidget)
    {
        newRect.setWidth(oldRect.width()/scale.factor);
    }

    if (updateHeight)
    {
        newRect.setHeight(oldRect.height()/scale.factor);
    }

    return TRUE;
}

3、原理解释

3.1公式解释

x 1 就 是 我 们 需 要 移 动 到 的 点 ( 目 标 ) , x 2 就 是 经 过 了 缩 放 的 点 , f a c t o r 就 是 缩 放 因 子 。 但 是 我 们 现 在 的 目 标 是 x 1 , 也 就 是 要 有 个 点 x 3 经 过 同 样 的 缩 放 要 能 达 到 x 1 位 置 。 本 质 上 我 就 是 把 原 来 的 缩 放 公 式 进 行 了 逆 推 。 x_1 就是我们需要移动到的点(目标),x_2就是经过了缩放的点,factor就是缩放因子。\\ 但是我们现在的目标是x_1,也就是要有个点x_3经过同样的缩放要能达到x_1位置。\\ 本质上我就是把原来的缩放公式进行了逆推。 x1x2factorx1,x3x1

{ ( x 1 − y ) × f a c t o r + y = x 2 ( x 3 − y ) × f a c t o r + y = x 1 \begin{cases} (x_1-y) \times factor + y = x_2\\ (x_3-y) \times factor + y = x1 \end{cases} {(x1y)×factor+y=x2(x3y)×factor+y=x1

解方程组得出如下的公式,就是上面的公式由来。
x 3 = ( ( 1 + f a c t o r ) × x 1 − x 2 ) f a c t o r x_3 = ((1+factor) \times x_1 - x_2) \over factor factorx3=((1+factor)×x1x2)

还有一个疑问:缩放公式我是怎么来的?

除了看源代码,没有技巧可言。

我把代码贴出来给你看下就知道了。这里的origin其实就是上面方程组的y,其实这个参数不重要,因为最后被消除掉了。

inline QPoint scale(const QPoint &pos, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
     return (pos - origin) * scaleFactor + origin;
}

再提一句,为什么在g_RecalcRect中计算高度和宽度的需要oldRect.height()/scale.factor,也是根据源码的公式逆推的。

最有一个问题:我怎么就找到这里了呢?

看代码分析流程,再加上调试手段,就会定位到这里了。我把调用堆栈截图你看下就明白了。注意我这里给的QWidget::setGeometry()流程,其实QWidget::move()也是会调用QWidget::setGeometry()

在这里插入图片描述

4、总结

从现象来看是很奇怪,但是我们一旦知道原理之后,就发现这一切理所当然的。

需要深究原理,不能有懈怠之心。谜题总是会解开的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值