You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt绘图应用中可嵌套Group类如何实现子元素相对父级缩放?

实现步骤

你不需要改动现有上层的鼠标事件、ResizeCmd逻辑,只需要调整Figure类接口、重写Group类的坐标设置虚方法即可,核心是基于缩放锚点和比例递归处理所有子元素:

步骤1:修改抽象类Figure的接口

将QRect的坐标设置方法改为虚函数,支持多态调用:

class Figure : public QRect {
public:
    // 原有逻辑保留,仅添加虚函数声明
    virtual void setTopLeft(const QPoint &p) { QRect::setTopLeft(p); }
    virtual void setTopRight(const QPoint &p) { QRect::setTopRight(p); }
    virtual void setBottomLeft(const QPoint &p) { QRect::setBottomLeft(p); }
    virtual void setBottomRight(const QPoint &p) { QRect::setBottomRight(p); }
    virtual void setRect(int x, int y, int w, int h) { QRect::setRect(x,y,w,h); }
    virtual ~Figure() = default;
};

步骤2:实现Group类的缩放逻辑

Group类内维护子元素容器,重写坐标设置方法,每次缩放时先计算缩放比例和固定锚点,再递归更新所有子元素的位置和大小:

class Group : public Figure {
private:
    QList<Figure*> m_children;
    // 通用子元素缩放方法,传入固定锚点和XY轴缩放比例
    void scaleChildren(const QPoint &anchor, qreal scaleX, qreal scaleY) {
        for (Figure *child : m_children) {
            // 计算子元素相对锚点的原始偏移
            qreal oldOffsetX = child->x() - anchor.x();
            qreal oldOffsetY = child->y() - anchor.y();
            // 计算缩放后的新位置、新尺寸
            int newX = anchor.x() + qRound(oldOffsetX * scaleX);
            int newY = anchor.y() + qRound(oldOffsetY * scaleY);
            int newW = qRound(child->width() * scaleX);
            int newH = qRound(child->height() * scaleY);
            // 子元素如果是Group会自动触发自身的缩放逻辑,天然支持嵌套
            child->setRect(newX, newY, newW, newH);
        }
    }
public:
    // 重写拖拽左上角逻辑,固定锚点为右下角
    void setTopLeft(const QPoint &p) override {
        int oldW = width(), oldH = height();
        if (oldW == 0 || oldH == 0) return; // 避免除以0异常
        QPoint anchor = bottomRight();
        QRect::setTopLeft(p);
        qreal scaleX = (qreal)width() / oldW;
        qreal scaleY = (qreal)height() / oldH;
        scaleChildren(anchor, scaleX, scaleY);
    }
    // 重写拖拽右上角逻辑,固定锚点为左下角
    void setTopRight(const QPoint &p) override {
        int oldW = width(), oldH = height();
        if (oldW == 0 || oldH == 0) return;
        QPoint anchor = bottomLeft();
        QRect::setTopRight(p);
        qreal scaleX = (qreal)width() / oldW;
        qreal scaleY = (qreal)height() / oldH;
        scaleChildren(anchor, scaleX, scaleY);
    }
    // 重写拖拽左下角逻辑,固定锚点为右上角
    void setBottomLeft(const QPoint &p) override {
        int oldW = width(), oldH = height();
        if (oldW == 0 || oldH == 0) return;
        QPoint anchor = topRight();
        QRect::setBottomLeft(p);
        qreal scaleX = (qreal)width() / oldW;
        qreal scaleY = (qreal)height() / oldH;
        scaleChildren(anchor, scaleX, scaleY);
    }
    // 重写拖拽右下角逻辑,固定锚点为左上角
    void setBottomRight(const QPoint &p) override {
        int oldW = width(), oldH = height();
        if (oldW == 0 || oldH == 0) return;
        QPoint anchor = topLeft();
        QRect::setBottomRight(p);
        qreal scaleX = (qreal)width() / oldW;
        qreal scaleY = (qreal)height() / oldH;
        scaleChildren(anchor, scaleX, scaleY);
    }
    // 重写setRect,方便批量设置坐标尺寸
    void setRect(int x, int y, int w, int h) override {
        int oldW = width(), oldH = height();
        if (oldW == 0 || oldH == 0) {
            QRect::setRect(x,y,w,h);
            return;
        }
        QPoint anchor = topLeft();
        QRect::setRect(x,y,w,h);
        qreal scaleX = (qreal)w / oldW;
        qreal scaleY = (qreal)h / oldH;
        scaleChildren(anchor, scaleX, scaleY);
    }
    // 子元素管理方法示例,新增子元素后要更新Group自身的边界
    void addChild(Figure *fig) {
        m_children.append(fig);
        *dynamic_cast<QRect*>(this) = m_children.first()->rect();
        for (auto f : m_children) {
            *this = united(*f);
        }
    }
};

注意事项

  • 每次新增、删除、修改Group内的子元素时,要重新调用united方法更新Group的边界,确保Group的rect刚好包裹所有子元素,避免缩放比例计算错误。
  • 如果你的子元素用的是相对Group的局部坐标,计算偏移时直接取child->x() child->y()即可,不需要减去锚点坐标。
  • 如果要实现等比缩放,只需要在计算scaleX、scaleY后取两者的最小值统一赋值给scaleX和scaleY即可。

内容的提问来源于stack exchange,提问作者Trommel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 02:54:01