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
相关产品推荐
相关产品推荐

