Qt (高仿Visio)流程图组件开发(三) 图元基类如何定义,流程图多种图元类型实现
文章目錄
- 本系列目錄
- 前言
- 一、圖元基類的定義
- 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),如果有特殊的需求可以添加在結構體聲明中,這樣是為了保證在開發自定義圖元類過程中需要保存一些特殊的數據。
??其次,流程圖元類(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); }??最后,代碼完整實現如上,針對其中幾個要點進行解釋。
3、自循環圖元
??這里單獨把自循環列出是因為其是一個較為典型的特殊圖元,自循環圖元自帶一條指向自己首尾的曲線,這里的實現基本與上文相同,只是多出了一個設置自循環連線的接口,多維護了一個連線對象。
// 添加自循環連線void SetCirculationLink(FlowcharGraphicsLink* _item); private:FlowcharGraphicsLink *graphics_link_;??玩家想實現一些特殊的圖元也可以通過這種方式擴展。
4、線圖元
??也是一個特殊圖元,只不過涉及箭頭繪制、曲線的計算、特殊接口定義等等,這里將其單獨寫出,之后會有對該類的詳細講解
總結
本文主要講解圖元基類的定義,及一些簡單自定義圖元如何去實現。
本文只是經驗分享,描述內容并不絕對,如有誤差歡迎指正。
如果此文幫助到你( ?? ω ?? )?,動動小手點個贊可好O(∩_∩)O。
原創文章,轉載請標明本文出處。總結
以上是生活随笔為你收集整理的Qt (高仿Visio)流程图组件开发(三) 图元基类如何定义,流程图多种图元类型实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SLAM学习笔记(二十)LIO-SAM流
- 下一篇: 使用HP LaserJet Pro MF