适配高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位置。\\ 本质上我就是把原来的缩放公式进行了逆推。 x1就是我们需要移动到的点(目标),x2就是经过了缩放的点,factor就是缩放因子。但是我们现在的目标是x1,也就是要有个点x3经过同样的缩放要能达到x1位置。本质上我就是把原来的缩放公式进行了逆推。
{ ( 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} {(x1−y)×factor+y=x2(x3−y)×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)×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()
4、总结
从现象来看是很奇怪,但是我们一旦知道原理之后,就发现这一切理所当然的。
需要深究原理,不能有懈怠之心。谜题总是会解开的。