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

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

适配高DPI QWidget::move移动有错误?_第1张图片

1、现象

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

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

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

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

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

2、解决方法

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

适配高DPI QWidget::move移动有错误?_第2张图片

#include 

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()

适配高DPI QWidget::move移动有错误?_第3张图片

4、总结

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

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

你可能感兴趣的:(Qt实战记录,qt,高DPI,分辨率)