ARTICLE · 1295134
全网罕见的二维《工业制图软件》源码项目

欢迎来到【Qt开发笔记】微信公众号【知识宝库】!
大家好,今天给大家看一个全网罕见的二维《工业制图软件》源码项目——工程制图软件 v1.0。
它不是简单的画线画圆Demo,而是一套基于Qt 6 / C++17 + Graphics View Framework构建的二维工程绘图编辑器雏形:基础图形、电气符号、流程图符号三大组件库支持拖拽创建;直线、圆、矩形、折线、样条曲线、图形连线等绘制模式齐全;属性编辑、网格显示、对象捕捉、图层管理、缩放平移、橡皮筋框选、撤销重做、XML工程文件保存/加载,一条绘图编辑器的核心链路基本打通。
更硬核的是,项目采用.edw + XML文本格式保存图层与图形对象,基于QUndoStack实现AddShapeCommand、RemoveShapeCommand、MoveShapeCommand、ChangePropertyCommand等命令体系,并包含GridManager、SnapManager、图层模型与图层管理器等可扩展模块。对于想研究Qt桌面应用架构、图形编辑器、工业符号库、撤销重做与序列化的开发者来说,这套源码的参考价值远超普通画板项目。
如果你想做一个真正像“软件”的Qt项目,而不是只能截图交作业的Demo,这个工程值得拆开看看。
👉 关注【Qt开发笔记】,获取更多Qt/C++硬核技术干货。
⭐关注博主后私信领取源码/学习资料📑。
一、【工程项目运行】
1、基础图形

2、电气符号

3、流程图符号

二、【工程项目简介】
工程制图软件是一款基于Qt 6 / C++17开发的桌面端二维工程绘图应用。项目使用Qt Widgets与Graphics View Framework构建绘图画布,支持基础几何图形、电气符号、流程图符号的绘制与拖拽创建,并提供文件保存/加载、撤销重做、属性编辑、网格显示、对象捕捉、图层模型、测量工具等功能。
当前应用名称与窗口标题为工程制图软件 v1.0,默认工程文件扩展名为.edw,底层采用XML文本格式保存。
2、使用技术栈
| 分类 | 技术 || 编程语言 | C++17|| GUI 框架 | Qt 6 Widgets || 绘图框架 | Qt Graphics View Framework || 文件格式 | XML / .edw || 撤销重做 | Qt Undo Framework / QUndoStack || 资源系统 | Qt Resource System/ .qrc || 构建系统 | qmake || 推荐环境 | Qt 6.5.x、MinGW 64-bit、Windows 10/11|
3.1 文件管理
- 新建工程图
- 打开.edw工程图文件
- 保存当前工程图
- 另存为工程图
- 关闭窗口时检测绘图区内容并提示保存
- 使用 XML 保存图层与图形对象
- 支持底层网表文本生成接口
3.2 绘图能力
工具栏和菜单支持以下绘制模式:
- 选择
- 直线
- 圆
- 矩形
- 折线
- 样条曲线
- 图形连线
状态栏会根据当前工具显示操作提示,例如绘制直线、绘制圆、绘制折线、绘制样条曲线和连接图形等。
3.3 组件库
左侧组件库支持拖拽组件到绘图区域创建图形,分为三类。
基础图形
- 直线 Line
- 圆 Circle
- 椭圆 Ellipse
- 圆弧 Arc
- 扇形 Sector
- 多边形 Polygon
- 星形 Star
- 矩形 Rectangle
- 圆角矩形 RoundedRect
- 箭头 Arrow
- 文本框 TextBox
- 折线 Polyline
- 样条曲线 Spline
电气符号
- 普通二极管 Diode
- 变容二极管 VaricapDiode
- NPN 三极管 NPNTransistor
- PNP 三极管 PNPTransistor
- 电阻 Resistor
- 电容 Capacitor
- 电感 Inductor
- 开关 Switch
- 接地 Ground
- 电源 PowerSupply
流程图符号
- 开始/结束 Terminator
- 处理 Process
- 判断 Decision
- 数据 Data
- 文档 Document
- 连接器 Connector
3.4 属性编辑
右侧属性面板支持对选中图形进行属性查看与编辑,主要包括:
- 图形名称
- 画笔宽度
- 画笔颜色
- 填充颜色
- 部分图形的专有属性显示
3.5 视图操作
- 抗锯齿绘制
- 平滑图像变换
- 橡皮筋框选
- Ctrl + 鼠标滚轮 缩放
- 鼠标中键拖动画布平移
- 放大、缩小、适应窗口
- 以鼠标位置为缩放锚点
3.6 网格与捕捉
GridManager支持:
- 显示/隐藏网格
- 设置网格大小
- 设置网格颜色
- 在场景背景中绘制网格
SnapManager支持的捕捉类型:
- NoSnap
- EndPoint
- MidPoint
- Center
- Intersection
- Grid
- All
3.7 图层管理
项目包含图层模型与图层管理器,支持:
- 创建图层
- 删除图层
- 清空图层
- 设置当前图层
- 查找图层
- 图层可见性
- 图层锁定状态
- 图层颜色
- 图形与图层关联
图层信息会保存到.edw文件中。
3.8 撤销与重做
基于QUndoStack实现撤销重做,包含命令类:
- AddShapeCommand:添加图形
- RemoveShapeCommand:删除图形
- MoveShapeCommand:移动图形
- ChangePropertyCommand:修改图形属性
三、【工程核心源码】
1、arcshape.h
#ifndef ARCSHAPE_H#define ARCSHAPE_H#include "shape.h"classArcShape : publicShape{public:explicitArcShape(const QPointF ¢er = QPointF(), qreal radius = 50.0,qreal startAngle = 0.0, qreal spanAngle = 90.0,QGraphicsItem *parent = nullptr);ShapeType shapeType() constoverride { return CircleType; }Shape* clone() constoverride;QPointF center() const { return m_center; }voidsetCenter(const QPointF ¢er);qreal radius() const { return m_radius; }voidsetRadius(qreal radius);qreal startAngle() const { return m_startAngle; }voidsetStartAngle(qreal angle);qreal spanAngle() const { return m_spanAngle; }voidsetSpanAngle(qreal angle);qreal endAngle() const { return m_startAngle + m_spanAngle; }qreal arcLength() const;QPointF startPoint() const;QPointF endPoint() const;// QGraphicsItem接口QRectF boundingRect() constoverride;voidpaint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;QPainterPath shape() constoverride;// 序列化voidsaveToXml(QXmlStreamWriter &writer) constoverride;voidloadFromXml(QXmlStreamReader &reader) override;// 调整大小voidupdateResizeHandles() override;private:QPointF m_center;qreal m_radius;qreal m_startAngle; // 起始角度(度)qreal m_spanAngle; // 跨越角度(度)};#endif // ARCSHAPE_H
#include "arcshape.h"#include "resizehandle.h"#include <QPainter>#include <QtMath>ArcShape::ArcShape(const QPointF ¢er, qreal radius, qreal startAngle, qreal spanAngle, QGraphicsItem *parent): Shape(parent), m_center(center), m_radius(radius), m_startAngle(startAngle), m_spanAngle(spanAngle){}Shape* ArcShape::clone() const{ArcShape *newShape = new ArcShape(m_center, m_radius, m_startAngle, m_spanAngle);newShape->setPen(m_pen);newShape->setBrush(m_brush);newShape->setPos(pos());return newShape;}voidArcShape::setCenter(const QPointF ¢er){prepareGeometryChange();m_center = center;update();}voidArcShape::setRadius(qreal radius){prepareGeometryChange();m_radius = qAbs(radius);update();}voidArcShape::setStartAngle(qreal angle){prepareGeometryChange();// 规范化角度到 0-360 范围m_startAngle = fmod(angle, 360.0);if(m_startAngle < 0) m_startAngle += 360.0;update();}voidArcShape::setSpanAngle(qreal angle){prepareGeometryChange();// 限制跨越角度在 -360 到 360 之间m_spanAngle = qBound(-360.0, angle, 360.0);update();}qreal ArcShape::arcLength() const{// 弧长 = 半径 × 弧度qreal radians = qDegreesToRadians(qAbs(m_spanAngle));return m_radius * radians;}QPointF ArcShape::startPoint() const{qreal rad = qDegreesToRadians(m_startAngle);return QPointF(m_center.x() + m_radius * qCos(rad),m_center.y() - m_radius * qSin(rad));}QPointF ArcShape::endPoint() const{qreal rad = qDegreesToRadians(m_startAngle + m_spanAngle);return QPointF(m_center.x() + m_radius * qCos(rad),m_center.y() - m_radius * qSin(rad));}QRectF ArcShape::boundingRect() const{qreal extra = m_pen.width() / 2.0;return QRectF(m_center.x() - m_radius - extra,m_center.y() - m_radius - extra,2 * m_radius + 2 * extra,2 * m_radius + 2 * extra);}voidArcShape::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){Q_UNUSED(option);Q_UNUSED(widget);painter->setPen(m_pen);painter->setBrush(Qt::NoBrush); // 确保不填充// 使用 QPainterPath 绘制圆弧,确保不填充QRectF rect(m_center.x() - m_radius, m_center.y() - m_radius,2 * m_radius, 2 * m_radius);int startAngle16 = qRound(m_startAngle * 16);int spanAngle16 = qRound(m_spanAngle * 16);QPainterPath path;path.arcMoveTo(rect, m_startAngle);path.arcTo(rect, m_startAngle, m_spanAngle);painter->drawPath(path); // 只绘制路径,不填充if(isSelected()) {painter->setPen(QPen(Qt::blue, 1, Qt::DashLine));painter->setBrush(Qt::NoBrush);painter->drawRect(boundingRect());// 绘制中心点painter->setPen(QPen(Qt::blue, 2));painter->setBrush(Qt::blue);painter->drawEllipse(m_center, 3, 3);// 绘制起点和终点painter->setPen(QPen(Qt::green, 2));painter->setBrush(Qt::green);painter->drawEllipse(startPoint(), 4, 4);painter->setPen(QPen(Qt::red, 2));painter->setBrush(Qt::red);painter->drawEllipse(endPoint(), 4, 4);// 绘制半径线painter->setPen(QPen(Qt::blue, 1, Qt::DotLine));painter->setBrush(Qt::NoBrush);painter->drawLine(m_center, startPoint());painter->drawLine(m_center, endPoint());// 绘制完整圆的虚线参考(不填充)painter->setPen(QPen(Qt::gray, 1, Qt::DotLine));painter->setBrush(Qt::NoBrush);painter->drawEllipse(m_center, m_radius, m_radius);}}QPainterPath ArcShape::shape() const{QPainterPath path;QRectF rect(m_center.x() - m_radius, m_center.y() - m_radius,2 * m_radius, 2 * m_radius);path.arcMoveTo(rect, m_startAngle);path.arcTo(rect, m_startAngle, m_spanAngle);QPainterPathStroker stroker;stroker.setWidth(qMax(m_pen.widthF(), 5.0));return stroker.createStroke(path);}voidArcShape::saveToXml(QXmlStreamWriter &writer) const{writer.writeStartElement("Arc");writer.writeAttribute("cx", QString::number(m_center.x()));writer.writeAttribute("cy", QString::number(m_center.y()));writer.writeAttribute("radius", QString::number(m_radius));writer.writeAttribute("startAngle", QString::number(m_startAngle));writer.writeAttribute("spanAngle", QString::number(m_spanAngle));saveBaseProperties(writer);writer.writeEndElement();}voidArcShape::loadFromXml(QXmlStreamReader &reader){QXmlStreamAttributes attrs = reader.attributes();if(attrs.hasAttribute("cx") && attrs.hasAttribute("cy")) {m_center.setX(attrs.value("cx").toDouble());m_center.setY(attrs.value("cy").toDouble());}if(attrs.hasAttribute("radius")) {m_radius = attrs.value("radius").toDouble();}if(attrs.hasAttribute("startAngle")) {m_startAngle = attrs.value("startAngle").toDouble();}if(attrs.hasAttribute("spanAngle")) {m_spanAngle = attrs.value("spanAngle").toDouble();}loadBaseProperties(reader);}voidArcShape::updateResizeHandles(){hideResizeHandles();// 在圆弧的起点添加手柄(调整起始角度)ResizeHandle *startHandle = new ResizeHandle(ResizeHandle::TopLeft, this);startHandle->setPos(startPoint());m_resizeHandles.append(startHandle);// 在圆弧的终点添加手柄(调整跨越角度)ResizeHandle *endHandle = new ResizeHandle(ResizeHandle::TopRight, this);endHandle->setPos(endPoint());m_resizeHandles.append(endHandle);// 在圆弧的中点添加手柄(调整半径)qreal midAngle = m_startAngle + m_spanAngle / 2.0;qreal rad = qDegreesToRadians(midAngle);QPointF midPoint(m_center.x() + m_radius * qCos(rad),m_center.y() - m_radius * qSin(rad));ResizeHandle *radiusHandle = new ResizeHandle(ResizeHandle::Right, this);radiusHandle->setPos(midPoint);m_resizeHandles.append(radiusHandle);}
#ifndef ARROWSHAPE_H#define ARROWSHAPE_H#include "shape.h"classArrowShape : publicShape{public:enum ArrowStyle {SingleArrow, // 单向箭头DoubleArrow, // 双向箭头FilledArrow, // 实心箭头OpenArrow // 空心箭头};explicitArrowShape(const QPointF &start = QPointF(), const QPointF &end = QPointF(),qreal arrowSize = 15.0, ArrowStyle style = FilledArrow,QGraphicsItem *parent = nullptr);ShapeType shapeType() constoverride { return LineType; }Shape* clone() constoverride;QPointF startPoint() const { return m_start; }voidsetStartPoint(const QPointF &point);QPointF endPoint() const { return m_end; }voidsetEndPoint(const QPointF &point);qreal arrowSize() const { return m_arrowSize; }voidsetArrowSize(qreal size);ArrowStyle arrowStyle() const { return m_style; }voidsetArrowStyle(ArrowStyle style);qreal length() const;qreal angle() const;// QGraphicsItem接口QRectF boundingRect() constoverride;voidpaint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;QPainterPath shape() constoverride;// 序列化voidsaveToXml(QXmlStreamWriter &writer) constoverride;voidloadFromXml(QXmlStreamReader &reader) override;// 调整大小voidupdateResizeHandles() override;private:QPointF m_start;QPointF m_end;qreal m_arrowSize;ArrowStyle m_style;QPolygonF createArrowHead(const QPointF &tip, qreal angle) const;};#endif // ARROWSHAPE_H
#include"arrowshape.h"#include"resizehandle.h"#include<QPainter>#include<QtMath>ArrowShape::ArrowShape(const QPointF &start, const QPointF &end, qreal arrowSize, ArrowStyle style, QGraphicsItem *parent): Shape(parent), m_start(start), m_end(end), m_arrowSize(qAbs(arrowSize)), m_style(style){}Shape* ArrowShape::clone()const{ArrowShape *newShape = newArrowShape(m_start, m_end, m_arrowSize, m_style);newShape->setPen(m_pen);newShape->setBrush(m_brush);newShape->setPos(pos());return newShape;}voidArrowShape::setStartPoint(const QPointF &point){prepareGeometryChange();m_start = point;update();}voidArrowShape::setEndPoint(const QPointF &point){prepareGeometryChange();m_end = point;update();}voidArrowShape::setArrowSize(qreal size){prepareGeometryChange();m_arrowSize = qAbs(size);update();}voidArrowShape::setArrowStyle(ArrowStyle style){prepareGeometryChange();m_style = style;update();}qreal ArrowShape::length()const{returnQLineF(m_start, m_end).length();}qreal ArrowShape::angle()const{QLineF line(m_start, m_end);return line.angle();}QPolygonF ArrowShape::createArrowHead(const QPointF &tip, qreal angle)const{QPolygonF arrowHead;qreal radAngle = qDegreesToRadians(angle);qreal arrowAngle = M_PI / 6.0; // 30度箭头角QPointF p1 = tip - QPointF(m_arrowSize * qCos(radAngle - arrowAngle),-m_arrowSize * qSin(radAngle - arrowAngle));QPointF p2 = tip - QPointF(m_arrowSize * qCos(radAngle + arrowAngle),-m_arrowSize * qSin(radAngle + arrowAngle));arrowHead << tip << p1 << p2;return arrowHead;}QRectF ArrowShape::boundingRect()const{qreal extra = (m_pen.width() + m_arrowSize) / 2.0;QRectF rect = QRectF(m_start, m_end).normalized();return rect.adjusted(-extra, -extra, extra, extra);}voidArrowShape::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){Q_UNUSED(option);Q_UNUSED(widget);painter->setPen(m_pen);painter->setBrush(m_brush);// 绘制线段QLineF line(m_start, m_end);painter->drawLine(line);// 计算箭头角度qreal angle = line.angle();// 绘制终点箭头QPolygonF endArrow = createArrowHead(m_end, angle);if (m_style == FilledArrow || m_style == DoubleArrow) {painter->setBrush(m_pen.color());painter->drawPolygon(endArrow);} else {painter->setBrush(Qt::NoBrush);painter->drawPolyline(endArrow);}// 如果是双向箭头,绘制起点箭头if (m_style == DoubleArrow) {QPolygonF startArrow = createArrowHead(m_start, angle + 180.0);painter->setBrush(m_pen.color());painter->drawPolygon(startArrow);}if (isSelected()) {painter->setPen(QPen(Qt::blue, 1, Qt::DashLine));painter->setBrush(Qt::NoBrush);painter->drawRect(boundingRect());}}QPainterPath ArrowShape::shape()const{QPainterPath path;// 线段路径path.moveTo(m_start);path.lineTo(m_end);// 添加箭头区域QLineF line(m_start, m_end);qreal angle = line.angle();QPolygonF endArrow = createArrowHead(m_end, angle);path.addPolygon(endArrow);if (m_style == DoubleArrow) {QPolygonF startArrow = createArrowHead(m_start, angle + 180.0);path.addPolygon(startArrow);}QPainterPathStroker stroker;stroker.setWidth(m_pen.width() + 4);return stroker.createStroke(path);}voidArrowShape::saveToXml(QXmlStreamWriter &writer)const{writer.writeStartElement("Arrow");writer.writeAttribute("x1", QString::number(m_start.x()));writer.writeAttribute("y1", QString::number(m_start.y()));writer.writeAttribute("x2", QString::number(m_end.x()));writer.writeAttribute("y2", QString::number(m_end.y()));writer.writeAttribute("arrowSize", QString::number(m_arrowSize));writer.writeAttribute("style", QString::number(static_cast<int>(m_style)));saveBaseProperties(writer);writer.writeEndElement();}voidArrowShape::loadFromXml(QXmlStreamReader &reader){QXmlStreamAttributes attrs = reader.attributes();if (attrs.hasAttribute("x1") && attrs.hasAttribute("y1")) {m_start.setX(attrs.value("x1").toDouble());m_start.setY(attrs.value("y1").toDouble());}if (attrs.hasAttribute("x2") && attrs.hasAttribute("y2")) {m_end.setX(attrs.value("x2").toDouble());m_end.setY(attrs.value("y2").toDouble());}if (attrs.hasAttribute("arrowSize")) {m_arrowSize = attrs.value("arrowSize").toDouble();}if (attrs.hasAttribute("style")) {m_style = static_cast<ArrowStyle>(attrs.value("style").toInt());}loadBaseProperties(reader);}voidArrowShape::updateResizeHandles(){hideResizeHandles();// 起点手柄ResizeHandle *startHandle = newResizeHandle(ResizeHandle::TopLeft, this);startHandle->setPos(m_start);m_resizeHandles.append(startHandle);// 终点手柄ResizeHandle *endHandle = newResizeHandle(ResizeHandle::BottomRight, this);endHandle->setPos(m_end);m_resizeHandles.append(endHandle);}
#ifndef CAPACITORSHAPE_H#define CAPACITORSHAPE_H#include"shape.h"classCapacitorShape : public Shape{public:explicitCapacitorShape(const QPointF ¢er = QPointF(), qreal size = 70.0, QGraphicsItem *parent = nullptr);ShapeType shapeType()constoverride{ return LineType; }Shape* clone()constoverride;QPointF center()const{ return m_center; }voidsetCenter(const QPointF ¢er);qreal size()const{ return m_size; }voidsetSize(qreal size);QRectF boundingRect()constoverride;voidpaint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr)override;QPainterPath shape()constoverride;voidsaveToXml(QXmlStreamWriter &writer)constoverride;voidloadFromXml(QXmlStreamReader &reader)override;protected:voidupdateResizeHandles()override;private:QPointF m_center;qreal m_size;};#endif// CAPACITORSHAPE_H
#include "capacitorshape.h"#include "resizehandle.h"#include <QPainter>#include <QtMath>CapacitorShape::CapacitorShape(const QPointF ¢er, qreal size, QGraphicsItem *parent): Shape(parent), m_center(center), m_size(qAbs(size)){}Shape* CapacitorShape::clone() const{CapacitorShape *newShape = new CapacitorShape(m_center, m_size);newShape->setPen(m_pen);newShape->setBrush(m_brush);newShape->setPos(pos());return newShape;}voidCapacitorShape::setCenter(const QPointF ¢er){prepareGeometryChange();m_center = center;update();}voidCapacitorShape::setSize(qreal size){prepareGeometryChange();m_size = qAbs(size);update();}QRectF CapacitorShape::boundingRect() const{constqreal extra = m_pen.widthF() / 2.0 + 2.0;constqreal halfSize = m_size / 2.0;constqreal plateWidth = m_size * 0.45;return QRectF(m_center.x() - plateWidth / 2.0 - extra,m_center.y() - halfSize - extra,plateWidth + 2.0 * extra,m_size + 2.0 * extra);}voidCapacitorShape::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){Q_UNUSED(option);Q_UNUSED(widget);painter->setPen(m_pen);painter->setBrush(Qt::NoBrush);constqreal halfSize = m_size / 2.0;constqreal plateWidth = m_size * 0.45;constqreal gap = m_size * 0.12;constqreal plateY1 = m_center.y() - gap / 2.0;constqreal plateY2 = m_center.y() + gap / 2.0;// 上引线painter->drawLine(QPointF(m_center.x(), m_center.y() - halfSize),QPointF(m_center.x(), plateY1));// 电容两极板(两条平行线)painter->drawLine(QPointF(m_center.x() - plateWidth / 2.0, plateY1),QPointF(m_center.x() + plateWidth / 2.0, plateY1));painter->drawLine(QPointF(m_center.x() - plateWidth / 2.0, plateY2),QPointF(m_center.x() + plateWidth / 2.0, plateY2));// 下引线painter->drawLine(QPointF(m_center.x(), plateY2),QPointF(m_center.x(), m_center.y() + halfSize));if(isSelected()) {painter->setPen(QPen(Qt::blue, 1, Qt::DashLine));painter->setBrush(Qt::NoBrush);painter->drawRect(boundingRect());painter->setBrush(Qt::blue);painter->drawEllipse(m_center, 3, 3);}}QPainterPath CapacitorShape::shape() const{QPainterPath path;constqreal halfSize = m_size / 2.0;constqreal plateWidth = m_size * 0.45;constqreal gap = m_size * 0.12;constqreal plateY1 = m_center.y() - gap / 2.0;constqreal plateY2 = m_center.y() + gap / 2.0;path.moveTo(m_center.x(), m_center.y() - halfSize);path.lineTo(m_center.x(), plateY1);path.moveTo(m_center.x() - plateWidth / 2.0, plateY1);path.lineTo(m_center.x() + plateWidth / 2.0, plateY1);path.moveTo(m_center.x() - plateWidth / 2.0, plateY2);path.lineTo(m_center.x() + plateWidth / 2.0, plateY2);path.moveTo(m_center.x(), plateY2);path.lineTo(m_center.x(), m_center.y() + halfSize);QPainterPathStroker stroker;stroker.setWidth(qMax(m_pen.widthF(), 6.0));return stroker.createStroke(path);}voidCapacitorShape::saveToXml(QXmlStreamWriter &writer) const{writer.writeStartElement("Capacitor");writer.writeAttribute("cx", QString::number(m_center.x()));writer.writeAttribute("cy", QString::number(m_center.y()));writer.writeAttribute("size", QString::number(m_size));saveBaseProperties(writer);writer.writeEndElement();}voidCapacitorShape::loadFromXml(QXmlStreamReader &reader){constQXmlStreamAttributes attrs = reader.attributes();if(attrs.hasAttribute("cx") && attrs.hasAttribute("cy")) {m_center.setX(attrs.value("cx").toDouble());m_center.setY(attrs.value("cy").toDouble());}if(attrs.hasAttribute("size")) {m_size = attrs.value("size").toDouble();}loadBaseProperties(reader);}voidCapacitorShape::updateResizeHandles(){hideResizeHandles();ResizeHandle *handle = new ResizeHandle(ResizeHandle::Bottom, this);handle->setPos(m_center.x(), m_center.y() + m_size / 2.0);m_resizeHandles.append(handle);}















































































