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


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

    在这里插入图片描述

    1、现象

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

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

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

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

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

    2、解决方法

    老规矩,我先说下解决方案。在编译的时候可能会报错,你需要在你的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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    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

    {(x1y)×factor+y=x2(x3y)×factor+y=x1" role="presentation">{(x1y)×factor+y=x2(x3y)×factor+y=x1
    {(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;
    }
    
    • 1
    • 2
    • 3
    • 4

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

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

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

    在这里插入图片描述

    4、总结

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

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

  • 相关阅读:
    idea插件开发问题
    Java并发编程—java异步Future的迭代过程
    Pipenv
    C++ 实现KMP字符串匹配算法
    网工内推 | 技术支持工程师,厂商公司,HCIA即可,有带薪年假
    CircuitPython入门贴
    概念解析 | 自动驾驶中的Corner Case剖析: 分类、处理方法和挑战
    windows下mysql中binlog日志分析和数据恢复
    vue环境搭建
    docker 构建docker-compose
  • 原文地址:https://blog.csdn.net/Mingyueruya/article/details/128111104