Qt 6.5实战:用QGraphicsView手撸一个简易的Visio图形绘制工具(附完整源码)

张开发
2026/4/17 16:59:39 15 分钟阅读

分享文章

Qt 6.5实战:用QGraphicsView手撸一个简易的Visio图形绘制工具(附完整源码)
Qt 6.5实战构建企业级Visio风格绘图工具在工业设计、软件架构和流程规划领域可视化绘图工具扮演着关键角色。传统商业解决方案往往价格昂贵且定制化能力有限而基于Qt 6.5的QGraphicsView框架开发者完全可以打造出媲美Visio的专业级绘图应用。本文将深入探讨如何利用现代C和Qt框架构建一个支持高阶功能的图形设计工具。1. 核心架构设计1.1 图形元素工厂模式专业绘图工具需要支持多种图形类型的动态创建。采用工厂方法模式可以优雅地解决这个问题class ShapeFactory { public: enum ShapeType { Rectangle, Ellipse, FlowChart, UMLComponent }; static QGraphicsItem* createShape(ShapeType type, QPointF pos) { switch(type) { case Rectangle: return new SmartRectangle(pos); case Ellipse: return new SmartEllipse(pos); //...其他图形类型 } } };这种设计允许后续轻松扩展新的图形类型而无需修改现有客户端代码。1.2 智能图形项基类所有可交互图形项都应继承自一个精心设计的基类class SmartGraphicsItem : public QGraphicsObject { Q_OBJECT public: explicit SmartGraphicsItem(QGraphicsItem* parent nullptr); // 必须实现的纯虚函数 virtual int type() const override 0; virtual QRectF boundingRect() const override 0; virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override 0; // 公共功能 void setBorderStyle(Qt::PenStyle style); void setFillColor(const QColor color); void addConnectionPort(); signals: void itemSelected(SmartGraphicsItem*); void positionChanged(); protected: // 事件处理 void mousePressEvent(QGraphicsSceneMouseEvent*) override; void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; void contextMenuEvent(QGraphicsSceneContextMenuEvent*) override; private: QListConnectionPort* m_ports; QPen m_pen; QBrush m_brush; };2. 专业级交互功能实现2.1 智能对齐与吸附系统工业级绘图工具需要实现以下辅助功能网格吸附按住Shift键时自动对齐到网格对象对齐拖动时自动与其他图形边缘/中心对齐动态参考线显示与其他对象的距离提示实现代码片段void GraphicsScene::snapToGrid(QGraphicsItem* item) { const int gridSize 20; QPointF pos item-pos(); qreal xV qRound(pos.x()/gridSize)*gridSize; qreal yV qRound(pos.y()/gridSize)*gridSize; if(qAbs(pos.x()-xV) gridSize/3) item-setX(xV); if(qAbs(pos.y()-yV) gridSize/3) item-setY(yV); }2.2 连接线系统专业绘图工具的核心是对象间的连接系统class ConnectionLine : public QGraphicsPathItem { public: ConnectionLine(QGraphicsItem* start, QGraphicsItem* end); void updatePath() { QPainterPath path; path.moveTo(mapFromItem(m_startItem, m_startItem-boundingRect().center())); path.lineTo(mapFromItem(m_endItem, m_endItem-boundingRect().center())); setPath(path); } private: QGraphicsItem* m_startItem; QGraphicsItem* m_endItem; };配合连接端口实现class ConnectionPort : public QGraphicsEllipseItem { public: enum { Type UserType 2 }; ConnectionPort(QGraphicsItem* parent) : QGraphicsEllipseItem(-5, -5, 10, 10, parent) { setBrush(Qt::green); setFlag(ItemSendsScenePositionChanges); } QVariant itemChange(GraphicsItemChange change, const QVariant value) override { if (change ItemScenePositionHasChanged) { foreach(ConnectionLine* line, m_lines) line-updatePath(); } return value; } private: QListConnectionLine* m_lines; };3. 高级功能实现3.1 属性编辑系统通过Qt的属性系统实现动态属性编辑void setupPropertyEditor(QGraphicsItem* item) { QObject* obj dynamic_castQObject*(item); if(!obj) return; QFormLayout* layout new QFormLayout; const QMetaObject* meta obj-metaObject(); for(int i0; imeta-propertyCount(); i) { QMetaProperty prop meta-property(i); if(prop.isDesignable()) { QWidget* editor createEditorForProperty(prop, obj); layout-addRow(prop.name(), editor); } } m_propertyDialog-setLayout(layout); }3.2 撤销/重做系统基于命令模式实现完整的撤销栈class MoveCommand : public QUndoCommand { public: MoveCommand(QGraphicsItem* item, const QPointF oldPos) : m_item(item), m_oldPos(oldPos), m_newPos(item-pos()) {} void undo() override { m_item-setPos(m_oldPos); } void redo() override { m_item-setPos(m_newPos); } private: QGraphicsItem* m_item; QPointF m_oldPos; QPointF m_newPos; }; // 使用方式 void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { if(m_movingItem) { QPointF delta event-scenePos() - event-lastScenePos(); m_movingItem-moveBy(delta.x(), delta.y()); } } void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { if(m_movingItem) { m_undoStack-push(new MoveCommand(m_movingItem, m_originalPosition)); m_movingItem nullptr; } }4. 性能优化技巧4.1 延迟绘制技术对于复杂场景采用按需绘制策略void SmartGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { if(option-levelOfDetail 0.5) { // 粗略绘制 painter-drawRect(boundingRect()); } else { // 完整绘制 drawDetailed(painter); } }4.2 场景分区管理使用空间索引加速场景查询void OptimizedScene::drawItems(QPainter* painter, int numItems, QGraphicsItem* items[], const QStyleOptionGraphicsItem options[]) { QRectF viewRect painter-worldTransform().inverted() .mapRect(QRect(0, 0, painter-device()-width(), painter-device()-height())); // 只绘制可见区域内的项目 QListQGraphicsItem* visibleItems items(viewRect); QGraphicsScene::drawItems(painter, visibleItems.size(), visibleItems.data(), options); }5. 项目结构与源码组织建议采用如下模块化结构/VisioStyleApp │── /core # 核心框架 │ ├── SmartItem # 智能图形项基类 │ ├── Connection # 连接系统 │ └── Undo # 撤销栈实现 │── /shapes # 图形类型实现 │ ├── Basic # 基础图形 │ ├── FlowChart # 流程图元素 │ └── UML # UML元素 │── /ui # 用户界面 │ ├── Toolbox # 工具箱面板 │ ├── PropertyEditor # 属性编辑器 │ └── MainWindow # 主窗口布局 │── /utils # 实用工具 │ ├── Serialization # 序列化功能 │ └── Algorithms # 几何算法关键构建配置CMake示例qt_add_executable(VisioStyleApp core/SmartItem.cpp core/Connection.cpp shapes/Basic/Rectangle.cpp ui/MainWindow.cpp # ...其他源文件 ) target_link_libraries(VisioStyleApp PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui )6. 扩展功能展望现代绘图工具还可以集成以下高级特性版本控制集成自动保存绘图历史版本团队协作基于WebSocket的实时协作编辑AI辅助设计自动布局建议和错误检查插件系统支持第三方扩展图形类型实现插件系统的接口示例class ShapePluginInterface { public: virtual ~ShapePluginInterface() default; virtual QStringList supportedShapes() const 0; virtual QGraphicsItem* createShape(const QString type) 0; }; Q_DECLARE_INTERFACE(ShapePluginInterface, com.yourcompany.VisioStyleApp.ShapePlugin/1.0)通过Qt的元对象系统和插件架构可以构建出真正企业级的可视化设计工具。

更多文章