2026/1/24 13:04:45
网站建设
项目流程
python 做下载网站,南通优普网站建设团队,黄石网站建设多少钱,国内外网站建设2017我们已经完成了 CAD 基础框架搭建和性能优化#xff0c;现在你可能会问#xff1a;“接下来该往哪个方向走#xff1f;” 新手最忌 “贪多求全”#xff08;比如直接上手 3D 建模、复杂约束#xff09;#xff0c;也忌 “停滞不前”#xff08;只停留在画点线圆#xf…我们已经完成了 CAD 基础框架搭建和性能优化现在你可能会问“接下来该往哪个方向走” 新手最忌 “贪多求全”比如直接上手 3D 建模、复杂约束也忌 “停滞不前”只停留在画点线圆。这篇文章会给你一条循序渐进、可落地、能看到明确成果的开发路线每个阶段都有具体目标、核心知识点和实操步骤帮你稳步推进 CAD 框架的功能迭代。一、先明确核心目标从 “能画图” 到 “能编辑”前两步我们实现了 “画点 / 线 / 圆 性能优化”下一步的核心是让框架具备图形编辑能力—— 这是 CAD 软件区别于普通画图工具的关键也是新手能快速掌握、且能显著提升框架实用性的方向。整体路线分 3 个阶段新手建议按顺序推进每个阶段 1-2 周基础编辑图形选中、移动、删除进阶编辑图形缩放、旋转、复制粘贴辅助功能图层管理、尺寸标注、文件读写。每个阶段都基于之前的代码优化不推翻原有逻辑只做增量开发降低学习成本。二、第一阶段实现图形选中、移动、删除核心1. 核心目标鼠标点击能选中图形高亮显示选中后拖动鼠标能移动图形按 Delete 键删除选中的图形保持操作流畅复用之前的性能优化技巧。2. 关键知识点几何命中测试判断鼠标是否 “点中” 图形选中状态管理标记选中的图形绘图时高亮事件联动选中→拖动→释放的完整交互链。3. 实操步骤附核心代码步骤 1给图形添加 “选中状态” 属性修改之前的图形结构体增加isSelected标记用于绘图时高亮// 基础图形结构体新增选中状态 struct BaseShape { ShapeType type; // 图形类型 QColor color; // 颜色 int penWidth; // 线宽 bool isSelected; // 选中状态新增 // 构造函数初始化默认值 BaseShape(ShapeType t) : type(t), color(Qt::black), penWidth(2), isSelected(false) {} };步骤 2实现 “命中测试”判断鼠标是否选中图形这是编辑功能的核心 —— 新手不用搞复杂的几何算法先实现 “简化版命中测试”足够满足基础需求后续再优化精度。在CanvasWidget中新增命中测试函数按图形类型分别实现// 新增判断鼠标点是否命中图形返回命中的图形指针 BaseShape* hitTest(const QPointF mousePos) { // 按“圆→线→点”的顺序判断圆的命中区域大优先判断 // 1. 测试圆 foreach (CircleShape* circle, circleShapes) { // 计算鼠标到圆心的距离小于半径5像素视为命中扩大范围新手操作更友好 qreal dist sqrt(pow(mousePos.x() - circle-center.x(), 2) pow(mousePos.y() - circle-center.y(), 2)); if (dist circle-radius 5) { return circle; } } // 2. 测试直线简化版点到直线的距离5像素视为命中 foreach (LineShape* line, lineShapes) { if (isPointNearLine(mousePos, line-startPos, line-endPos)) { return line; } } // 3. 测试点 foreach (PointShape* point, pointShapes) { qreal dist sqrt(pow(mousePos.x() - point-pos.x(), 2) pow(mousePos.y() - point-pos.y(), 2)); if (dist 5) { // 5像素范围内视为命中 return point; } } return nullptr; // 未命中任何图形 } // 辅助函数判断点是否靠近直线复用之前优化的版本避免开根号 bool isPointNearLine(const QPointF point, const QPointF p1, const QPointF p2) { QVector2D v1(p2 - p1); QVector2D v2(point - p1); qreal dot QVector2D::dotProduct(v1, v2); if (dot 0) return false; qreal lenSq v1.lengthSquared(); if (dot lenSq) return false; qreal distSq v2.lengthSquared() - dot * dot / lenSq; return distSq pow(5, 2); // 5像素阈值 }步骤 3处理鼠标事件实现 “选中 移动”修改CanvasWidget的鼠标事件增加选中和移动逻辑// CanvasWidget新增成员变量 private: BaseShape* selectedShape nullptr; // 当前选中的图形 QPointF lastMousePos; // 上次鼠标位置用于移动计算 bool isDraggingShape false; // 是否正在拖动图形 // 1. 鼠标按下事件选中图形 void mousePressEvent(QMouseEvent *event) override { QPointF mousePos event-pos(); // 先取消之前的选中状态 if (selectedShape ! nullptr) { selectedShape-isSelected false; selectedShape nullptr; update(); // 刷新画布取消高亮 } // 如果是右键/中键走原有逻辑平移画布 if (event-button() Qt::MiddleButton) { // 平移画布的逻辑之前实现的 m_isDragging true; m_lastMousePos mousePos; return; } // 左键命中测试选中图形 selectedShape hitTest(mousePos); if (selectedShape ! nullptr) { selectedShape-isSelected true; isDraggingShape false; // 初始未拖动 lastMousePos mousePos; // 记录选中时的鼠标位置 update(); // 刷新画布高亮显示选中的图形 return; } // 未选中图形走原有绘图逻辑画点/线/圆 if (currentTool ! None) { // 原有绘图逻辑按下创建临时图形... } } // 2. 鼠标移动事件移动选中的图形 void mouseMoveEvent(QMouseEvent *event) override { QPointF mousePos event-pos(); // 优先处理图形拖动 if (selectedShape ! nullptr event-buttons() Qt::LeftButton) { if (!isDraggingShape) { isDraggingShape true; // 开始拖动 } // 计算鼠标移动的偏移量 QPointF delta mousePos - lastMousePos; // 根据图形类型移动坐标 moveShape(selectedShape, delta); // 记录新的鼠标位置 lastMousePos mousePos; // 局部重绘复用之前的性能优化 update(getShapeBoundingRect(selectedShape).adjusted(-5, -5, 5, 5)); return; } // 未拖动图形走原有逻辑临时图形/平移画布 if (tempShape ! nullptr) { // 原有临时图形拖动逻辑... } else if (m_isDragging) { // 原有平移画布逻辑... } } // 3. 鼠标释放事件结束拖动 void mouseReleaseEvent(QMouseEvent *event) override { Q_UNUSED(event); isDraggingShape false; // 结束拖动 // 原有临时图形保存逻辑... } // 新增移动图形的核心函数按类型处理坐标 void moveShape(BaseShape* shape, const QPointF delta) { if (shape nullptr) return; switch (shape-type) { case Point: { PointShape* point dynamic_castPointShape*(shape); point-pos delta; break; } case Line: { LineShape* line dynamic_castLineShape*(shape); line-startPos delta; line-endPos delta; break; } case Circle: { CircleShape* circle dynamic_castCircleShape*(shape); circle-center delta; break; } default: break; } }步骤 4绘图时高亮选中的图形修改drawShape或分类型的绘图函数选中的图形用 “红色 加粗” 显示void drawLine(QPainter* painter, LineShape* line) { QPen pen; if (line-isSelected) { // 选中状态红色、线宽2、虚线 pen QPen(Qt::red, line-penWidth 2, Qt::DashLine); } else { pen QPen(line-color, line-penWidth); } painter-setPen(pen); painter-setRenderHint(QPainter::Antialiasing, false); painter-drawLine(line-startPos, line-endPos); } // 圆和点的绘图函数做类似修改...步骤 5实现删除功能按 Delete 键删除选中图形在CanvasWidget中重写键盘事件void keyPressEvent(QKeyEvent *event) override { if (event-key() Qt::Key_Delete selectedShape ! nullptr) { // 根据类型删除图形分桶存储的优势 switch (selectedShape-type) { case Point: { PointShape* point dynamic_castPointShape*(selectedShape); pointShapes.removeOne(point); delete point; break; } case Line: { LineShape* line dynamic_castLineShape*(selectedShape); lineShapes.removeOne(line); delete line; break; } case Circle: { CircleShape* circle dynamic_castCircleShape*(selectedShape); circleShapes.removeOne(circle); delete circle; break; } default: break; } selectedShape nullptr; update(); // 刷新画布 } }4. 测试验证编译运行后测试核心功能点击画好的直线 / 圆 / 点图形会变成红色虚线选中高亮选中后拖动鼠标图形会跟随移动且只有图形区域刷新不卡顿选中图形后按 Delete 键图形被删除未选中图形时仍能正常画点 / 线 / 圆。三、第二阶段进阶编辑缩放、旋转、复制粘贴完成基础编辑后可进一步实现 “图形变换” 功能 —— 这是 CAD 编辑的核心能力新手重点掌握 “坐标变换” 逻辑复用 Qt 的QTransform简化计算。1. 核心目标选中图形后鼠标滚轮缩放图形区别于画布缩放选中图形后按快捷键如 R旋转图形支持复制CtrlC、粘贴CtrlV选中的图形。2. 关键实操以图形缩放为例// CanvasWidget新增缩放选中的图形 void scaleSelectedShape(qreal scale) { if (selectedShape nullptr) return; switch (selectedShape-type) { case Line: { LineShape* line dynamic_castLineShape*(selectedShape); // 以起点为中心缩放直线 QPointF center line-startPos; line-endPos center (line-endPos - center) * scale; break; } case Circle: { CircleShape* circle dynamic_castCircleShape*(selectedShape); circle-radius * scale; // 缩放半径 break; } default: break; } // 局部重绘 update(getShapeBoundingRect(selectedShape).adjusted(-5, -5, 5, 5)); } // 重写滚轮事件区分“画布缩放”和“图形缩放” void wheelEvent(QWheelEvent *event) override { if (selectedShape ! nullptr) { // 有选中图形缩放图形 qreal delta event-angleDelta().y() 0 ? 1.1 : 0.9; scaleSelectedShape(delta); } else { // 无选中图形缩放画布原有逻辑 qreal delta event-angleDelta().y() 0 ? 1.1 : 0.9; scaleFactor * delta; scaleFactor qBound(0.1, scaleFactor, 10.0); // 更新变换矩阵... update(); } }四、第三阶段辅助功能图层、标注、文件读写当编辑功能稳定后添加 “提升实用性” 的辅助功能 —— 这些功能不涉及复杂几何但能让你的 CAD 框架更接近真实软件。1. 图层管理核心是 “分类显示 / 隐藏”新增Layer结构体包含图层名称、是否可见、颜色等属性每个图形关联一个图层修改BaseShape增加QString layerName属性菜单栏添加 “图层管理” 窗口实现 “显示 / 隐藏图层”“修改图层颜色”绘图时只绘制 “可见图层” 的图形。2. 尺寸标注CAD 的核心辅助功能新增DimensionShape图形类型存储标注的起点、终点、文字位置、标注值实现 “长度标注”直线长度、“半径标注”圆的半径通过几何计算得到长度 / 半径绘图时显示文字标注跟随图形变化比如移动直线后标注值自动更新。3. 文件读写保存 / 打开绘图成果用 Qt 的QJsonDocument将图形数据类型、坐标、颜色、图层保存为 JSON 文件新手易上手比 DXF 简单打开文件时读取 JSON 数据重建图形列表并绘制菜单栏添加 “新建 / 打开 / 保存” 功能关联文件操作函数。五、新手避坑指南关键提醒不要同时推进多个功能比如先做完 “选中 移动 删除”再做缩放旋转最后做图层标注 —— 贪多会导致代码混乱排查问题困难复用已有代码和优化技巧比如局部重绘、分桶存储、QTransform等不要重复造轮子优先实现 “简化版”再优化精度比如命中测试先做 “扩大范围” 的简化版能满足基本操作即可后续再优化 “精准命中”及时测试和重构每新增一个功能都要测试性能比如 100 个图形时是否卡顿代码冗余时及时重构比如提取通用的图形操作函数。六、后续进阶方向当你完成以上所有功能后当你掌握了 “2D 绘图 编辑 辅助功能” 后可根据兴趣选择进阶方向2D 进阶添加约束功能比如平行、垂直、等长约束、批量绘图阵列、镜像3D 入门学习 OpenGL/Qt 3D 模块实现简单的 3D 建模拉伸、旋转 2D 图形成 3D 模型兼容性支持读取 / 保存 DXF 格式用 dxflib 库兼容 AutoCAD 文件性能极致优化引入空间索引比如 R 树提升大量图形的命中测试效率。总结作为新手下一步的核心是 “从画图到编辑”—— 先实现图形选中、移动、删除再逐步添加缩放、旋转、图层、标注等功能。这条路线的核心是 “增量开发、先易后难”每个阶段都能看到明确的成果既不会因难度过高放弃也不会因功能单一失去动力。记住CAD 开发是一个 “积少成多” 的过程哪怕每天只实现一个小功能比如今天做选中明天做移动坚持下来你的框架会越来越完善。如果在开发过程中遇到具体问题比如旋转图形的坐标计算可以聚焦单个问题深入研究不用急于求成