
在适配高DPI文章发布之后,有个小伙伴立马联系我说增加了这个特性之后,发现一个移动坐标的问题。
比如说:QWidget::move(500, 500),在实际的高DPI屏幕上移动的像素并不是QPoint(500, 500),而是比QPoint(500, 500)要大。
我觉得挺有意思,立马就写个demo测试下,果然如他所说的。
同理我测试了QWidget::setGeometry()也是这样的。
这个现象还是挺奇怪的,让我们一探究竟。
老规矩,我先说下解决方案。在编译的时候可能会报错,你需要在你的vs中添加头文件目录

#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;
}
x1就是我们需要移动到的点(目标),x2就是经过了缩放的点,factor就是缩放因子。但是我们现在的目标是x1,也就是要有个点x3经过同样的缩放要能达到x1位置。本质上我就是把原来的缩放公式进行了逆推。x_1 就是我们需要移动到的点(目标),x_2就是经过了缩放的点,factor就是缩放因子。\\ 但是我们现在的目标是x_1,也就是要有个点x_3经过同样的缩放要能达到x_1位置。\\ 本质上我就是把原来的缩放公式进行了逆推。 x1就是我们需要移动到的点(目标),x2就是经过了缩放的点,factor就是缩放因子。但是我们现在的目标是x1,也就是要有个点x3经过同样的缩放要能达到x1位置。本质上我就是把原来的缩放公式进行了逆推。
{(x1−y)×factor+y=x2(x3−y)×factor+y=x1\begin{cases} (x_1-y) \times factor + y = x_2\\ (x_3-y) \times factor + y = x1 \end{cases} {(x1−y)×factor+y=x2(x3−y)×factor+y=x1
解方程组得出如下的公式,就是上面的公式由来。
x3=((1+factor)×x1−x2)factorx_3 = ((1+factor) \times x_1 - x_2) \over factor factorx3=((1+factor)×x1−x2)
还有一个疑问:缩放公式我是怎么来的?
除了看源代码,没有技巧可言。
我把代码贴出来给你看下就知道了。这里的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()

从现象来看是很奇怪,但是我们一旦知道原理之后,就发现这一切理所当然的。
需要深究原理,不能有懈怠之心。谜题总是会解开的。
上一篇:计算鞍点c++