文章目录

  • 本系列目录
  • 前言
  • 一、图元基类的定义
    • 1、图元信息基类结构体
    • 2、图元位置
    • 3、父子对象关系
  • 二、自定义图元实现
    • 1、自定义图元基类(FlowchartGraphicsItem)与Qt原生图元基类(QGraphicsRectItem)相互转化关系
    • 2、流程图元、判断图元
    • 3、自循环图元
    • 4、线图元
  • 总结

本系列目录

Qt (高仿Visio)流程图组件开发(一) 效果展示及基本开发框架构思
Qt (高仿Visio)流程图组件开发(二) 基本图元绘制 图元间连线绘制
Qt (高仿Visio)流程图组件开发(三) 图元基类如何定义,流程图多种图元类型实现
Qt (高仿Visio)流程图组件开发(四) 流程图 图元对齐 磁吸线功能
Qt (高仿Visio)流程图组件开发(五) 流程图 双击编辑图元内容实现
Qt (高仿Visio)流程图组件开发(六) 流程图 线图元 如何绘制曲线 连接线移除视口后无法显示
Qt (高仿Visio)流程图组件开发(七) 流程图 简单操作界面搭建
Qt (高仿Visio)流程图组件开发(八) 流程图 鼠标拖动图元到场景(QGraphicsScene)创建
Qt (高仿Visio)流程图组件开发(九) 流程图 代码展示


前言

  本文主要讲解一些基本流程图图元的实现,例如:流程图元、自循环图元、判断图元、线图元,这里只实现这四种,其他图元类型的实现大同小异。同时也会讲解如何从这些图元中抽离出通用部分,定义图元基类。只是经验分享,描述内容并不绝对,如有误差欢迎指正。


一、图元基类的定义

1、图元信息基类结构体

  首先,所有图元都具有一些信息(内容、id、提示信息等等),抽离出通用的部分可以声明在一个结构体基类中,并维护在图元基类中。如下为基类结构体:

enum class ItemType
{Null = 0,     // 空Nomal,          // 代表全局常态Link,          // 连接线Rect,         // 流程Condition,     // 判定Circulation,   // 自循环
};struct FlowchartItemType
{ItemType   type = ItemType::Null;
};enum class  FlowchartCursor
{ArrowCursor = 0,DrawLinkCursor,SizeAllCurSor,OpenHandCursor,ClosedHandCursor,
};// 图元样式信息
struct FlowchartStyleBase
{QPen                           pen_;                   // 基本画笔样式--背景相关QBrush                           brush_;                 // 基本画刷样式--背景相关QPen                         text_pen_;              // 文本画笔样式QFont                          font_;                  // 字体样式FlowchartStyleBase(){pen_ = QPen();pen_.setColor(QColor(65, 113, 156));pen_.setWidth(1);brush_ = QBrush(QColor(89, 152, 209));text_pen_ = QPen();;text_pen_.setColor(QColor(254, 255, 255));text_pen_.setWidth(1);font_ = QFont("Microsoft YaHei", 12, 2);}
};// 图元数据信息
struct FlowchartContentBase
{QString                            id_;                                        // 图元idQString                          content_;                                   // 图元内容QString                          tooltip_;                                   // 图元提示信息FlowchartContentBase(){id_ = QUuid::createUuid().toString();content_ = "Text";tooltip_ = "Tooltip";}
};// 图元父子结构关系信息
struct FlowchartStructuralData
{FlowchartStructuralData(){}QVector<QString>              father_ids_;                    // 父节点id集合QVector<QString>                children_ids_;                  // 子节点id集合
};// 图元结构体基类
struct FlowchartInforBase
{double                         position_x_, position_y_, width_, height_;FlowchartStyleBase                item_style_;FlowchartContentBase            item_content_;FlowchartItemType             item_type_;FlowchartStructuralData          item_structural_;FlowchartInforBase(){position_x_ = 0.0;position_y_ = 0.0;width_ = 160.0;height_ = 40.0;item_style_ = FlowchartStyleBase();item_content_ = FlowchartContentBase();item_structural_ = FlowchartStructuralData();};FlowchartInforBase(double _x, double _y, double _width = 160.0, double _height = 40.0){position_x_ = _x;position_y_ = _y;width_ = _width;height_ = _height;item_style_ = FlowchartStyleBase();item_content_ = FlowchartContentBase();item_structural_ = FlowchartStructuralData();};
};
typedef std::vector<FlowchartInforBase*>  FlowchartInforBases;

2、图元位置

  所有的图元都需要获取其在场景上的位置,用来创建连线、判断弹出框位置等等。

 virtual QPointF GetCenterPoint(){ return center_point_; };virtual QPointF GetLeftPoint(){ return left_point_; };virtual QPointF GetRightPoint(){ return right_point_; };virtual QPointF GetTopPoint(){ return top_point_; };virtual QPointF GetBottomPoint(){ return bottom_point_; };

3、父子对象关系

  基类中还应该存有父子对象,相关连线对象,提供添加父子对象节点、清空依赖关系,获取父子对象集合等接口,减少实现一个自定义图元对象时需要实现的内容。接口定义如下:

 // 获取图元idQString GetItemId();// 获取图元类型ItemType GetItemType();// 添加子节点void AddChild(FlowchartGraphicsItem* _item, FlowcharGraphicsLink* _link_item);// 删除子节点及其连线void DelChild(QString _id);// 添加父节点void AddFather(FlowchartGraphicsItem* _item, FlowcharGraphicsLink* _link_item);// 删除父节点连线及其连线void DelFather(QString _id);// 清空图元依赖关系void ItemClear();// 获取子对象集合QMap<QString, FlowchartGraphicsItem*> GetChildrenItems();// 获取父对象集合QMap<QString, FlowchartGraphicsItem*> GetFatherItems();

二、自定义图元实现

  有了图元基类后,所有的自定义图元只需要继承图元基类,实现虚函数接口,绘制需要的图形样式即可。

1、自定义图元基类(FlowchartGraphicsItem)与Qt原生图元基类(QGraphicsRectItem)相互转化关系

  因为自定义的图元基类为c++类,所以在调用Qt相关接口时,需要转化为原生基类来使用,转化关系如下:

 // 对象转化 qt原生对象->图元基类对象FlowchartGraphicsItem* QGraphToFlow(QGraphicsItem* _item);// 对象转化  图元基类对象->qt原生对象QGraphicsItem* FlowToQGraph(FlowchartGraphicsItem* _item);// 对象转化  qt原生对象->图元基类对象FlowchartGraphicsItem* FlowchartScene::QGraphToFlow(QGraphicsItem* _item){if (_item == nullptr)return nullptr;FlowchartGraphicsRectItem* item = (FlowchartGraphicsRectItem*)_item;return (FlowchartGraphicsItem*)item;}// 对象转化  图元基类对象->qt原生对象QGraphicsItem* FlowchartScene::FlowToQGraph(FlowchartGraphicsItem* _item){if (_item == nullptr)return nullptr;FlowchartGraphicsRectItem* item = (FlowchartGraphicsRectItem*)_item;return (QGraphicsItem*)item;}

2、流程图元、判断图元

  流程图元与判断图元示意图如下:

  流程图元与判断图元包括之后的自定义流程图实现都大同小异,现已流程图元为例进行详细讲解。讲解内容大都集中在代码注释中。流程图元类(FlowchartGraphicsRectItem)
  首先,需要创建一个属于该图元的结构体(FlowchartItemRectInfo),继承之前定义的基类结构体(FlowchartInforBase),如果有特殊的需求可以添加在结构体声明中,这样是为了保证在开发自定义图元类过程中需要保存一些特殊的数据。

// 流程矩形结构体
struct FlowchartItemRectInfo : FlowchartInforBase
{FlowchartItemRectInfo() :FlowchartInforBase(){};FlowchartItemRectInfo(double _x, double _y, double _width = 160.0, double _height = 40.0):FlowchartInforBase(_x, _y, _width, _height){};
};

  其次,流程图元类(FlowchartGraphicsRectItem)需要继承QObject、QGraphicsRectItem、FlowchartGraphicsItem,QObject类不必多说,继承QGraphicsRectItem是因为要实现的流程图元为一矩形图元,所以直接继承现有的矩形图元类即可,包括之后的自定义图元也是根据图元需求来继承适合的类,FlowchartGraphicsItem类为我们的自定义结构体基类,类的父子关系、信息等都由该基类维护。

// 流程矩形
class FlowchartGraphicsRectItem: public QObject, public QGraphicsRectItem, public FlowchartGraphicsItem
{Q_OBJECTpublic:explicit FlowchartGraphicsRectItem(FlowchartItemRectInfo* _infor, QObject *parent = nullptr);~FlowchartGraphicsRectItem();// 修改文本内容virtual void SetText(QString _content, QString _tooltip = "") override;// 获取界面信息virtual FlowchartInforBase* GetItemInformation() override;/****************获取图元点--中心、左、右、上、下*******************/virtual QPointF GetCenterPoint() override { return mapToScene(center_point_); };virtual QPointF GetLeftPoint() override{ return mapToScene(left_point_); };virtual QPointF GetRightPoint() override{ return mapToScene(right_point_); };virtual QPointF GetTopPoint() override{ return mapToScene(top_point_); };virtual QPointF GetBottomPoint() override{ return mapToScene(bottom_point_); };private:// 绘制文本内容void DrawItemText(QPainter *_painter);public:virtual QRectF boundingRect() const;virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = Q_NULLPTR);private:// 流程矩形结构体方便取用FlowchartItemRectInfo*   item_infor_;};FlowchartGraphicsRectItem::FlowchartGraphicsRectItem(FlowchartItemRectInfo* _infor, QObject *parent): QObject(parent), FlowchartGraphicsItem(_infor, ItemType::Rect)
{// ! [1] 初始化图元位置大小item_infor_ = _infor;setRect({ QPointF(-item_infor_->width_ / 2, -item_infor_->height_ / 2), QSizeF(item_infor_->width_, item_infor_->height_) });setPos(0,0);center_point_ = pos();left_point_ = pos() + QPointF(-item_infor_->width_ / 2 - 3, 0);right_point_ = pos() + QPointF(item_infor_->width_ / 2 + 3, 0);top_point_ = pos() + QPointF(0, -item_infor_->height_ / 2 - 3);bottom_point_ = pos() + QPointF(0, item_infor_->height_ / 2 + 3);setPos(item_infor_->position_x_, item_infor_->position_y_);// ! [2] 图元相关设置setFlag(QGraphicsItem::ItemIsMovable, true);setFlag(QGraphicsItem::ItemIsSelectable, true);setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);// 设置图元层级setZValue(2);// ! [3] 图元信息设置this->setToolTip(item_infor_->item_content_.tooltip_);}FlowchartGraphicsRectItem::~FlowchartGraphicsRectItem()
{}void FlowchartGraphicsRectItem::DrawItemText(QPainter *_painter)
{_painter->save();/*[] 计算字体宽度 根据item大小*/_painter->setFont(item_infor_->item_style_.font_);QFontMetrics font_metrics = _painter->fontMetrics();QRectF rect = this->rect();QRectF resize_rect = rect;QString text = item_infor_->item_content_.content_;QString tremporart_text;resize_rect.setHeight(rect.height());resize_rect.setWidth(rect.width());tremporart_text = font_metrics.elidedText(text, Qt::ElideRight, resize_rect.width());_painter->setPen(item_infor_->item_style_.text_pen_);tremporart_text.replace(text, "");if (!tremporart_text.isEmpty())resize_rect.setWidth(rect.width());resize_rect.moveCenter(rect.center());_painter->drawText(resize_rect, Qt::AlignCenter | Qt::TextWrapAnywhere, text);_painter->restore();
}void FlowchartGraphicsRectItem::SetText(QString _content, QString _tooltip)
{item_infor_->item_content_.content_ = _content;if (_tooltip.compare("") == 0)return;item_infor_->item_content_.tooltip_ = _tooltip;this->setToolTip(item_infor_->item_content_.tooltip_);this->update();
}FlowchartInforBase* FlowchartGraphicsRectItem::GetItemInformation()
{// 位置信息更新item_infor_->position_x_ = this->scenePos().x();item_infor_->position_y_ = this->scenePos().y();item_infor_->width_ = this->boundingRect().width();item_infor_->height_ = this->boundingRect().height();// 父子关系更新FlowchartStructuralData  structural_data;QMap<QString, FlowchartGraphicsItem*> father_items = GetFatherItems();for (QMap<QString, FlowchartGraphicsItem*>::iterator iter = father_items.begin(); iter != father_items.end(); iter++){structural_data.father_ids_.push_back(iter.key());}QMap<QString, FlowchartGraphicsItem*> children_items = GetChildrenItems();for (QMap<QString, FlowchartGraphicsItem*>::iterator iter = children_items.begin(); iter != children_items.end(); iter++) {structural_data.children_ids_.push_back(iter.key());}item_infor_->item_structural_ = structural_data;item_base_info_ = (FlowchartInforBase*)item_infor_;return item_base_info_;
}QRectF FlowchartGraphicsRectItem::boundingRect() const
{QRectF rect = QGraphicsRectItem::boundingRect().adjusted(-3, -3, 3, 3);return rect;
}void FlowchartGraphicsRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget /*= Q_NULLPTR*/)
{painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);painter->save();painter->setPen(item_infor_->item_style_.pen_);painter->setBrush(item_infor_->item_style_.brush_);painter->drawRect(this->boundingRect());painter->restore();/*[] 绘制选中外框*/if (this->isSelected()) {/*[1]: 绘制外围的矩形框 */QPen selected_pen(QColor(147, 147, 147));selected_pen.setWidth(0.5);selected_pen.setStyle(Qt::DashLine);painter->setPen(selected_pen);painter->setBrush(Qt::NoBrush);painter->drawRect(this->boundingRect());const auto left_center_rect = QRectF((left_point_ - QPointF(2, 2)), QSizeF(5, 5));const auto right_center_rect = QRectF((right_point_ - QPointF(3, 3)), QSizeF(5, 5));const auto top_center_rect = QRectF((top_point_ - QPointF(2, 2)), QSizeF(5, 5));const auto bottom_center_rect = QRectF((bottom_point_ - QPointF(3, 3)), QSizeF(5, 5));/*[2]: 绘制四个方向的连接点 */painter->setPen(QColor(147, 147, 147));painter->setBrush(QColor(255, 255, 255));painter->drawRect(left_center_rect);painter->drawRect(right_center_rect);painter->drawRect(top_center_rect);painter->drawRect(bottom_center_rect);}DrawItemText(painter);
}

  最后,代码完整实现如上,针对其中几个要点进行解释。

  1. setPos(0,0);是为了设置默认位置,对之后的通过代码设置相对位置有很大作用。
  2. setFlag(QGraphicsItem::ItemIsMovable,true);设置运行鼠标拖动,这样就可以使用qt原生的图元移动功能。
  3. setFlag(QGraphicsItem::ItemIsSelectable,true);设置运行被选中,这样就可以通过this->isSelected()的方式来获取该图元是否被选中,以此来绘制自定义的选中态。
  4. setZValue(2);设置图元层级,目前设置这个主要是为了将连线与一般图元区分开,不然会将曲线的选中态遮盖(可以自行试验)。

3、自循环图元

  这里单独把自循环列出是因为其是一个较为典型的特殊图元,自循环图元自带一条指向自己首尾的曲线,这里的实现基本与上文相同,只是多出了一个设置自循环连线的接口,多维护了一个连线对象。

 // 添加自循环连线void SetCirculationLink(FlowcharGraphicsLink* _item);
private:FlowcharGraphicsLink                        *graphics_link_;

  玩家想实现一些特殊的图元也可以通过这种方式扩展。

4、线图元

  也是一个特殊图元,只不过涉及箭头绘制、曲线的计算、特殊接口定义等等,这里将其单独写出,之后会有对该类的详细讲解

总结

本文主要讲解图元基类的定义,及一些简单自定义图元如何去实现。
本文只是经验分享,描述内容并不绝对,如有误差欢迎指正。

如果此文帮助到你( •̀ ω •́ )✧,动动小手点个赞可好O(∩_∩)O。

原创文章,转载请标明本文出处。

Qt (高仿Visio)流程图组件开发(三) 图元基类如何定义,流程图多种图元类型实现相关推荐

  1. Qt高仿Excel表格组件-支持冻结列、冻结行、内容自适应和合并单元格

    目录 一.概述 二.效果展示 三.实现思路 1.冻结行.冻结列 2.行高自适应 3.蚂蚁线 四.测试代码 1.添加表格数据 2.设置冻结行.列 3.行高.列宽 4.单元格背景色 5.单元格文字 6.其 ...

  2. C++ Qt高仿QQ影音视频播放器 (三)

      本篇介绍中间视频播放控件的实现.   主要涉及到3个控件:打开文件按钮.右侧打开文件列表按钮.播放时间进度条按钮.播放效果如下:   正常播放时,时间进度条只有在鼠标悬浮到视频区域时才显示. 打开 ...

  3. Qt (高仿Visio)流程图组件开发(二) 基本图元绘制 图元间连线绘制

    文章目录 本系列目录 前言 一.如何绘制图元 二.两图元之间如何连线 三.如何实现线跟随图元移动 四.线的位置判断 总结 本系列目录 Qt (高仿Visio)流程图组件开发(一) 效果展示及基本开发框 ...

  4. Qt (高仿Visio)流程图组件开发(九) 流程图 代码展示

    文章目录 本系列目录 本系列目录 Qt (高仿Visio)流程图组件开发(一) 效果展示及基本开发框架构思 Qt (高仿Visio)流程图组件开发(二) 基本图元绘制 图元间连线绘制 Qt (高仿Vi ...

  5. [qt] 高仿360手机助手[含源码]

    [qt] 高仿360手机助手 实现功能如下: 1. 高仿真界面 2. 获取当前手机屏幕,以及可以截图 3. 获取手机短信/通讯录/应用信息/系统应用信息/视频/音乐. demo版地址: http:// ...

  6. 视频教程-微信小程序项目实战:高仿iOS计算器-微信开发

    微信小程序项目实战:高仿iOS计算器 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过1 ...

  7. C#制作高仿360安全卫士窗体(三)

    距上篇C#制作高仿360安全卫士窗体(二)也将近一个多月了,这个月事情还是像往常一样的多.不多我也乐在其中,毕竟我做的是我喜欢做的东西.今天特地抽空把怎么制作文本框写一下.同时也希望有爱好这些玩意的同 ...

  8. 面向对象编程、面向服务架构、基于组件开发三种编程模式的区别和适用领域

    面向对象编程(Object-Oreinted Programming) 是一种编程范式.指在设计程序时大量运用类实例对象的方式.OOP一旦在项目中被运用,就成了时刻要考虑的东西. 面向服务架构(Ser ...

  9. 自定义组件开发三 Graphics 绘制动态效果

    概述 上文https://blog.csdn.net/u011733020/article/details/80220513主要介绍了Graphics相关的api的绘图方法.绘制的都是静态的,这里使用 ...

最新文章

  1. 在使用Reference Source调试.Net 源代码时如何取消optimizations(代码优化)-翻译
  2. SSO单点登录、跨域重定向、跨域设置Cookie、京东单点登录实例分析
  3. Android之drawable state各个属性详解
  4. 使用Spring Boot开发Restful程序
  5. linux c 11 运行库,11.1.3 运行库与I/O
  6. yum安装ruby_centos 6.5 ruby环境安装
  7. 静态链接与动态链接的区别
  8. 洛谷 P1377 [TJOI2011]树的序 解题报告
  9. C#LeetCode刷题之#217-存在重复元素(Contains Duplicate)
  10. 通用 PE 工具箱1.9.6(XP内核)by Uepon(李培聪)
  11. 记一次进销存软件的破解
  12. C语言-输入任意多个数字,数字间用空格隔开,然后将数字求和。
  13. Vue源码学习之Vue对于闭包的使用
  14. xise php一句话木马,渗透利器 | 常见的WebShell管理工具
  15. 「excel小技巧」一秒快速求和多行数值
  16. python整数类型的输出格式_Python整数类型(int)详解
  17. 计算机出现假桌面怎么解决办法,win10桌面因为AppHangXProcB1一直出现假死的有效修复技巧...
  18. mac 下 python 批量删除 PDF 中的某些页面
  19. Terramaster铁威马 F4-422真机实测预告 3000元级万兆4盘位NAS
  20. win10蓝牙链接上的标准串行com口无法删除

热门文章

  1. Activiti6+SpringBoot---会签功能学习-1
  2. 普通青年,文艺青年,二逼青年之程序员版
  3. 对于上云的企业来说,做好安全合规究竟有多重要?
  4. 电子信息工程跨考计算机武大,我考研的一些经历吧——电气(武汉大学)
  5. 网易云音乐真实链接地址
  6. C语言解决世界杯小组赛问题
  7. what is Differential steering and skid steering ?
  8. 闭关修炼——five——Spring
  9. 计算机音乐念诗之王,念诗之王(电音版)
  10. 传感器的使用,高仿微信摇一摇,动画加声音