目录
一、简介
二、自定义图元
2.1图元设计
2.2 端口QNEPort
2.3节点块QNEBlock
一、简介
前期文章:
流程图拖拽视觉编程--概述_Jason~shen的博客-CSDN博客
流程图拖拽视觉编程-流程编辑器_Jason~shen的博客-CSDN博客
本期内容:
本期将继续介绍流程编辑器模块的实现方法,前面介绍了视图的实现,现在介绍自定义图元的实现,即流程图中每个节点块、端口和连接线。

二、自定义图元
该部分基于QGraphicsItem 图元类实现,该类提供了一些基本的图形元件,可在此基础上自定义图元。
2.1图元设计
开始:圆角矩形 + 1个输出端口

结束:圆角矩形 + 1个输入端口

程序块:矩形 + 1个输入端口 + 1个输出端口

条件:菱形 + 1个输入端口 + 1个为真端口 + 1个为假端口
 
 
循环:菱形 + 1个输入端口 + 2个循环体端口 (左+下)+ 1个退出循环端口(右)

流程线:带箭头的直线或者折线

2.2 端口QNEPort
继承QGraphicsPathItem,设置为圆形
QPainterPath p;
p.addEllipse(-radius_, -radius_, 2 * radius_, 2 * radius_);
setPath(p);#ifndef QNEPORT_H
#define QNEPORT_H
#include <QGraphicsPathItem>
#include "floweditor_global.h"
class QNEBlock;
class QNEConnection;
/* 端口 */
class FLOWEDITORSHARED_EXPORT QNEPort : public QGraphicsPathItem
{
public:
    enum { Type = QGraphicsItem::UserType + 1 };
    enum E_Direction {TOP = 0, BOTTOM = 1, LEFT = 2, RIGHT = 3};
    QNEPort(QGraphicsItem *parent = 0);
    ~QNEPort();
    void setDirection(const E_Direction &dir);
    E_Direction direction();
    void setOutput(bool b);
    bool isOutput() const;
    void setNEBlock(QNEBlock *);
    QNEBlock *block() const;
    int radius();
    QVector<QNEConnection *> &connections();
    bool isConnected(QNEPort *);
    int type() const
    {
        return Type;
    }
protected:
    QVariant itemChange(GraphicsItemChange change, const QVariant &value);
private:
    QNEBlock *m_block;
    int radius_;
    bool m_isOutput;
    E_Direction m_directtion;
    QVector<QNEConnection *> m_connections;
};
#endif // QNEPORT_H
#include "NEPort.h"
#include <QGraphicsScene>
#include <QFontMetrics>
#include <QPen>
#include <QDebug>
#include "NEConnection.h"
QNEPort::QNEPort(QGraphicsItem *parent):
    QGraphicsPathItem(parent), m_isOutput(false)
{
    radius_ = 5;
    QPainterPath p;
    p.addEllipse(-radius_, -radius_, 2 * radius_, 2 * radius_);
    setPath(p);
    setPen(QPen(Qt::darkRed));
    setBrush(Qt::red);
    setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
}
QNEPort::~QNEPort()
{
    foreach(QNEConnection *conn, m_connections)
    {
        delete conn;
    }
}
void QNEPort::setDirection(const QNEPort::E_Direction &dir)
{
    m_directtion = dir;
}
QNEPort::E_Direction QNEPort::direction()
{
    return m_directtion;
}
void QNEPort::setOutput(bool b)
{
    m_isOutput = b;
}
bool QNEPort::isOutput() const
{
    return m_isOutput;
}
void QNEPort::setNEBlock(QNEBlock *b)
{
    m_block = b;
}
QNEBlock *QNEPort::block() const
{
    return m_block;
}
int QNEPort::radius()
{
    return radius_;
}
QVector<QNEConnection *> &QNEPort::connections()
{
    return m_connections;
}
bool QNEPort::isConnected(QNEPort *other)
{
    qDebug() << "m_connections" << m_connections.size();
    foreach(QNEConnection *conn, m_connections)
        if (conn->port1() == other || conn->port2() == other)
        {
            return true;
        }
    return false;
}
QVariant QNEPort::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if (change == GraphicsItemChange::ItemScenePositionHasChanged)
    {
        foreach(QNEConnection *conn, m_connections)
        {
            conn->updatePosFromPorts();
            conn->updatePath();
        }
    }
    return value;
}
2.3节点块QNEBlock
继承QGraphicsPathItem
#ifndef QNEBLOCK_H
#define QNEBLOCK_H
#include <QFont>
#include <QGraphicsPathItem>
#include "floweditor_global.h"
#include "TypeDefine.h"
class QNEPort;
class QNEConnection;
USE_NAMESPACE_FLOWEDITOR
class FLOWEDITORSHARED_EXPORT QNEBlock : public QGraphicsPathItem
{
public:
    enum { Type = QGraphicsItem::UserType + 3 };
    QNEBlock(QGraphicsItem *parent = 0);
    void setUuid(const QString &uuid);
    QString uuid() const;
    void setName(const QString &name);
    QString name() const;
    void setNodeType(const E_NodeType &type);
    E_NodeType nodeType() const;
    void initBlockByType();
    void setData(const QVariant &data);
    QVariant data() const;
    void setPixmap(const QPixmap &pm);
    void setInitData(const E_NodeType type);
    QMap<QString, QStringList> getNodeStrMap();
    /* 保存加载 */
    void save(T_NodeInfo &);
    void load(const T_NodeInfo &);
    /* 克隆 */
    QNEBlock *clone(int max_uuid);
    QNEBlock *clone(QString uuid_str);
    /* 获得相连的block */
    QNEBlock *getNextBlock(int dir);
    /* 端口 */
    QVector<QNEPort *> ports();
    QNEPort *port(int direction);
    int type() const
    {
        return Type;
    }
    inline int width() const
    {
        return m_width;
    }
    inline int height() const
    {
        return m_height;
    }
    inline QPointF center() const
    {
        return QPointF(m_width / 2, m_height / 2);
    }
protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    QVariant itemChange(GraphicsItemChange change, const QVariant &value);
private:
    int m_width;
    int m_height;
    E_NodeType m_nodeType;
    QVariant m_data;         /**<  携带数据 */
    QString m_uuid;          /**<  唯一识别码 */
    QString m_name;          /**<  名称 */
    QList<QNEPort *> m_nePortList;
    QFont m_font;
    QPixmap m_pixmap;
    T_NodeInfo m_ni;
    bool m_bIsDrawPixmap;
};
#endif // QNEBLOCK_H
设置路径:QPainterPath, 根据类型添加不同的形状,添加圆角矩形addRoundedRect、添加菱形addPolygon
添加端口:QNEBlock和QNEPort绑定为父子关系new QNEPort(this),QNEBlock位置移动时QNEPort也会跟随移动
void QNEBlock::initBlockByType()
{
    QPainterPath p;
    if(E_NodeType::Start == m_nodeType)
    {
        /* 圆角矩形 */
        p.addRoundedRect(0, 0, m_width, m_height, m_height / 2, m_height / 2);
        // 下
        QNEPort *port = new QNEPort(this);
        port->setPos(m_width / 2, m_height);
        port->setNEBlock(this);
        port->setOutput(true);
        port->setToolTip("Out");
        port->setDirection(QNEPort::BOTTOM);
    }
    else if(E_NodeType::End == m_nodeType)
    {
        /* 圆角矩形 */
        p.addRoundedRect(0, 0, m_width, m_height, m_height / 2, m_height / 2);
        // 上
        QNEPort *port = new QNEPort(this);
        port->setPos(m_width / 2, 0);
        port->setNEBlock(this);
        port->setOutput(false);
        port->setToolTip("In");
        port->setDirection(QNEPort::TOP);
    }
    else if(E_NodeType::Judge == m_nodeType)
    {
        /* 菱形 */
        QPolygonF polygonf;
        polygonf << QPointF(0, m_height / 2)
                 << QPointF(m_width / 2, m_height)
                 << QPointF(m_width, m_height / 2)
                 << QPointF(m_width / 2, 0)
                 << QPointF(0, m_height / 2);
        p.addPolygon(polygonf);
        // 上
        QNEPort *port = new QNEPort(this);
        port->setPos(m_width / 2, 0);
        port->setNEBlock(this);
        port->setOutput(false);
        port->setToolTip("In");
        port->setDirection(QNEPort::TOP);
        // 右
        port = new QNEPort(this);
        port->setPos(m_width, m_height / 2);
        port->setNEBlock(this);
        port->setOutput(true);
        port->setToolTip("Out");
        port->setDirection(QNEPort::RIGHT);
        // 下
        port = new QNEPort(this);
        port->setPos(m_width / 2, m_height);
        port->setNEBlock(this);
        port->setOutput(true);
        port->setToolTip("Out");
        port->setDirection(QNEPort::BOTTOM);
    }
    else if(E_NodeType::Loop == m_nodeType)
    {
        /* 菱形 */
        QPolygonF polygonf;
        polygonf << QPointF(0, m_height / 2)
                 << QPointF(m_width / 2, m_height)
                 << QPointF(m_width, m_height / 2)
                 << QPointF(m_width / 2, 0)
                 << QPointF(0, m_height / 2);
        p.addPolygon(polygonf);
        // 上
        QNEPort *port = new QNEPort(this);
        port->setPos(m_width / 2, 0);
        port->setNEBlock(this);
        port->setOutput(false);
        port->setToolTip("In");
        port->setDirection(QNEPort::TOP);
        // 右
        port = new QNEPort(this);
        port->setPos(m_width, m_height / 2);
        port->setNEBlock(this);
        port->setOutput(true);
        port->setToolTip("Out");
        port->setDirection(QNEPort::RIGHT);
        // 下
        port = new QNEPort(this);
        port->setPos(m_width / 2, m_height);
        port->setNEBlock(this);
        port->setOutput(true);
        port->setToolTip("Out");
        port->setDirection(QNEPort::BOTTOM);
        // 左
        port = new QNEPort(this);
        port->setPos(0, m_height / 2);
        port->setNEBlock(this);
        port->setOutput(false);
        port->setToolTip("In");
        port->setDirection(QNEPort::LEFT);
    }
    else
    {
        /* 圆角矩形 */
        p.addRoundedRect(0, 0, m_width, m_height, 5, 5);
        // 上
        QNEPort *port = new QNEPort(this);
        port->setPos(m_width / 2, 0);
        port->setNEBlock(this);
        port->setOutput(false);
        port->setToolTip("In");
        port->setDirection(QNEPort::TOP);
        // 下
        port = new QNEPort(this);
        port->setPos(m_width / 2, m_height);
        port->setNEBlock(this);
        port->setOutput(true);
        port->setToolTip("Out");
        port->setDirection(QNEPort::BOTTOM);
        m_bIsDrawPixmap = true;
    }
    setPath(p);
}重写绘图函数paint(), 选中时端口显示port->show(),未选中时端口隐藏port->hide(),绘制路径drawPath、图片drawPixmap、文字drawText
void QNEBlock::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)
    if (isSelected())
    {
        painter->setPen(QPen(Qt::darkYellow));
        painter->setBrush(Qt::yellow);
        foreach(QGraphicsItem *port_, childItems())
        {
            if (port_->type() == QNEPort::Type)
            {
                port_->show();
            }
        }
    }
    else
    {
        painter->setPen(QPen(Qt::darkGreen));
        painter->setBrush(Qt::green);
        foreach(QGraphicsItem *port_, childItems())
        {
            if (port_->type() == QNEPort::Type)
            {
                port_->hide();
            }
        }
    }
    painter->drawPath(path());
    if(m_bIsDrawPixmap && !m_pixmap.isNull())
    {
        QRect rect(m_height * 0.25, m_height * 0.25, m_height * 0.5, m_height * 0.5);
        painter->drawPixmap(rect, m_pixmap);
    }
    if(!m_name.isEmpty())
    {
        painter->setPen(QPen(Qt::black));
        painter->setBrush(Qt::BrushStyle::NoBrush);
        painter->setFont(m_font);
        QTextOption option(Qt::AlignCenter);
        option.setWrapMode(QTextOption::WordWrap);
        painter->drawText(boundingRect(), m_uuid + m_name, option);
    }
}


















