├── .gitignore ├── README.md ├── src ├── NodeView.pri ├── groupitem.cpp ├── groupitem.h ├── nodeitem.cpp ├── nodeitem.h ├── nodeview.cpp ├── nodeview.h ├── portitem.cpp ├── portitem.h ├── ropeitem.cpp └── ropeitem.h └── test ├── NodeView.pro └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | NodeView.pro.user* 2 | build/ 3 | bin/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The class to create such nodes 2 | ![nodeview2](https://user-images.githubusercontent.com/13070282/31816923-1112b4b8-b58a-11e7-9792-5b7034516bd5.png) 3 | -------------------------------------------------------------------------------- /src/NodeView.pri: -------------------------------------------------------------------------------- 1 | INCLUDEPATH += $$PWD 2 | 3 | SOURCES += \ 4 | $$PWD/groupitem.cpp \ 5 | $$PWD/nodeitem.cpp \ 6 | $$PWD/nodeview.cpp \ 7 | $$PWD/portitem.cpp \ 8 | $$PWD/ropeitem.cpp 9 | 10 | HEADERS += \ 11 | $$PWD/groupitem.h \ 12 | $$PWD/nodeitem.h \ 13 | $$PWD/nodeview.h \ 14 | $$PWD/portitem.h \ 15 | $$PWD/ropeitem.h 16 | -------------------------------------------------------------------------------- /src/groupitem.cpp: -------------------------------------------------------------------------------- 1 | #include "groupitem.h" 2 | #include "nodeitem.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | GroupItem::GroupItem(QGraphicsItem *parent) : QGraphicsObject(parent), 14 | m_uuid(QUuid::createUuid()), 15 | m_size(QSizeF(200,200)), 16 | m_indentSize(QSizeF(10,10)), 17 | m_titleSize(QSizeF(0,0)), 18 | m_font(QFont("Calibri",10,QFont::Medium)), 19 | m_fontColor(QColor(Qt::black)), 20 | m_titleColor(QColor::fromRgb(207, 244, 241, 150)), 21 | m_backgroundColor(QColor::fromRgb(207, 244, 241, 100)), 22 | m_backgroundBrush(QBrush(m_backgroundColor)), 23 | m_isUserBrush(false), 24 | m_isSelected(false), 25 | m_isMove(false) 26 | { 27 | setZValue(-1); 28 | setFlags(ItemIsSelectable | ItemIsMovable | ItemSendsGeometryChanges); 29 | setCacheMode(DeviceCoordinateCache); 30 | setAcceptHoverEvents(true); 31 | 32 | updateSize(); 33 | } 34 | 35 | QRectF GroupItem::boundingRect() const 36 | { 37 | return QRectF(-m_indentSize.width(), -m_indentSize.height(), 38 | m_size.width() + m_indentSize.width() * 2, 39 | m_size.height() + m_indentSize.height() * 2); 40 | } 41 | 42 | QPainterPath GroupItem::shape() const 43 | { 44 | QPainterPath path; 45 | path.addRect(0, 0, m_size.width(), m_size.height()); 46 | 47 | return path; 48 | } 49 | 50 | void GroupItem::paint(QPainter *p, const QStyleOptionGraphicsItem *item, QWidget *widget) 51 | { 52 | Q_UNUSED(widget); 53 | 54 | bool isSelected = false; 55 | 56 | if (item->state & QStyle::State_Selected) isSelected = true; 57 | 58 | QPen pen; 59 | pen.setStyle(Qt::DashDotLine); 60 | pen.setWidth(1); 61 | pen.setStyle(Qt::SolidLine); 62 | pen.setCapStyle(Qt::RoundCap); 63 | pen.setJoinStyle(Qt::RoundJoin); 64 | pen.setBrush(QColor::fromRgb(0,0,0,0)); 65 | p->setPen(pen); 66 | 67 | p->setRenderHints(QPainter::Antialiasing | 68 | QPainter::SmoothPixmapTransform | 69 | QPainter::HighQualityAntialiasing); 70 | 71 | QLinearGradient gradient_0(0,m_size.height()/2,m_size.width(),m_size.height()/2); 72 | gradient_0.setColorAt(0,QColor::fromRgb(0,0,0,0)); 73 | gradient_0.setColorAt(0.1,QColor::fromRgb(0,0,0,130)); 74 | gradient_0.setColorAt(0.9,QColor::fromRgb(0,0,0,130)); 75 | gradient_0.setColorAt(1,QColor::fromRgb(0,0,0,0)); 76 | 77 | p->setBrush(gradient_0); 78 | p->drawRoundedRect(2,1,m_size.width()-4,m_size.height()-2, 79 | (qreal)m_size.width()/((qreal)m_size.width()/5.0), 80 | (qreal)m_size.height()/((qreal)m_size.height()/5.0)); 81 | 82 | if (isSelected) 83 | { 84 | pen.setWidth(2); 85 | pen.setBrush(QColor::fromRgb(200,80,20,255)); 86 | } 87 | else 88 | { 89 | pen.setWidth(1); 90 | pen.setBrush(QColor::fromRgb(0,0,0,170)); 91 | } 92 | 93 | if (m_isSelected != isSelected) 94 | { 95 | m_isSelected = isSelected; 96 | emit selected(m_isSelected); 97 | } 98 | 99 | p->setPen(pen); 100 | 101 | if (m_isUserBrush) 102 | p->setBrush(m_backgroundBrush); 103 | else 104 | { 105 | qreal gradientHeight = ((m_titleSize.height()) * 100 / m_size.height()) * 0.01; 106 | QLinearGradient gradient_1(m_size.width() / 2, 0, m_size.width() / 2, m_size.height()); 107 | gradient_1.setColorAt(0, m_backgroundColor); 108 | gradient_1.setColorAt(gradientHeight, m_titleColor); 109 | gradient_1.setColorAt(gradientHeight + 0.01, m_backgroundColor); 110 | 111 | p->setBrush(gradient_1); 112 | } 113 | p->drawRoundedRect(6, 1, m_size.width() - 12, m_size.height() - 2, 114 | (qreal)m_size.width() / ((qreal)m_size.width() / 5.0), 115 | (qreal)m_size.height() / ((qreal)m_size.height() / 5.0)); 116 | 117 | p->setPen(QPen(m_fontColor,2)); 118 | p->setFont(m_font); 119 | p->drawText(QRect(0, 0,m_size.width(),m_titleSize.height()), Qt::AlignCenter, m_title); 120 | } 121 | 122 | 123 | void GroupItem::mousePressEvent(QGraphicsSceneMouseEvent *e) 124 | { 125 | QGraphicsItem::mousePressEvent(e); 126 | 127 | foreach (NodeItem *node, m_nodeList) 128 | node->setSelected(true); 129 | 130 | update(); 131 | } 132 | 133 | void GroupItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) 134 | { 135 | m_isMove = true; 136 | QGraphicsItem::mouseMoveEvent(e); 137 | update(); 138 | } 139 | 140 | void GroupItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) 141 | { 142 | m_isMove = false; 143 | QGraphicsItem::mouseReleaseEvent(e); 144 | 145 | foreach (NodeItem *node, m_nodeList) 146 | node->setSelected(false); 147 | 148 | update(); 149 | } 150 | 151 | bool GroupItem::isHovered(const QPointF &point) 152 | { 153 | bool result = false; 154 | 155 | QPointF s_pos = mapToScene(QPointF(-6,0)); 156 | QPointF s_size = mapToScene(QPointF(m_size.width()+6,m_size.height())); 157 | 158 | if (point.x() >= s_pos.x() && 159 | point.y() >= s_pos.y() && 160 | point.x() <= s_size.x() && 161 | point.y() <= s_size.y()) 162 | result = true; 163 | 164 | return result; 165 | } 166 | 167 | void GroupItem::addNode(NodeItem *node) 168 | { 169 | if (m_nodeList.contains(node)) return; 170 | 171 | connect(node, &NodeItem::positionChanged, this, &GroupItem::updateSize); 172 | 173 | m_nodeList.append(node); 174 | updateSize(); 175 | } 176 | 177 | void GroupItem::setNodes(QList list) 178 | { 179 | m_nodeList.clear(); 180 | m_nodeList = list; 181 | 182 | foreach (NodeItem *node, m_nodeList) 183 | connect(node, &NodeItem::positionChanged, this, &GroupItem::updateSize); 184 | 185 | updateSize(); 186 | } 187 | 188 | void GroupItem::removeNode(NodeItem *node) 189 | { 190 | if (!m_nodeList.contains(node)) return; 191 | 192 | m_nodeList.removeOne(node); 193 | updateSize(); 194 | 195 | if(m_nodeList.isEmpty()) 196 | emit becameEmpty(this); 197 | } 198 | 199 | void GroupItem::clearNodes() 200 | { 201 | m_nodeList.clear(); 202 | updateSize(); 203 | } 204 | 205 | void GroupItem::updateSize() 206 | { 207 | if (m_isMove) return; 208 | if (m_nodeList.count() < 1) return; 209 | 210 | NodeItem *firstNode = m_nodeList.at(0); 211 | qreal left = firstNode->x(); 212 | qreal top = firstNode->y(); 213 | qreal right = firstNode->x() + firstNode->width(); 214 | qreal bottom = firstNode->y() + firstNode->height(); 215 | 216 | foreach (NodeItem *node, m_nodeList) 217 | { 218 | if (node->x() < left) left = node->x(); 219 | if (node->y() < top) top = node->y(); 220 | 221 | if(node->x() + node->width() > right) 222 | right = node->x() + node->width(); 223 | 224 | if(node->y() + node->height() > bottom) 225 | bottom = node->y() + node->height(); 226 | } 227 | 228 | setPos(left - m_indentSize.width(), 229 | top - m_indentSize.height() - m_titleSize.height()); 230 | 231 | qreal width = qAbs(right - left); 232 | qreal height = qAbs(bottom - top); 233 | m_size = QSizeF(width + m_indentSize.width() * 2, 234 | height + m_indentSize.height() * 2 + m_titleSize.height()); 235 | 236 | prepareGeometryChange(); 237 | update(); 238 | } 239 | 240 | void GroupItem::setTitle(const QString &title) 241 | { 242 | m_title = title; 243 | 244 | QFontMetrics fm(m_font); 245 | #if QT_VERSION > 0x050906 246 | m_titleSize = QSizeF(fm.horizontalAdvance(m_title),fm.height() + 2); 247 | #else 248 | m_titleSize = QSizeF(fm.width(m_title),fm.height() + 2); 249 | #endif 250 | 251 | updateSize(); 252 | } 253 | 254 | void GroupItem::clearTitle() 255 | { 256 | m_title.clear(); 257 | m_titleSize = QSizeF(0,0); 258 | updateSize(); 259 | } 260 | 261 | void GroupItem::setFont(const QFont &font) 262 | { 263 | m_font = font; 264 | update(); 265 | } 266 | 267 | void GroupItem::setFontColor(const QColor &color) 268 | { 269 | m_fontColor = color; 270 | update(); 271 | } 272 | 273 | void GroupItem::setTitleColor(const QColor &color) 274 | { 275 | m_titleColor = color; 276 | update(); 277 | } 278 | 279 | void GroupItem::setBackgroundColor(const QColor &color) 280 | { 281 | m_backgroundColor = color; 282 | update(); 283 | } 284 | 285 | void GroupItem::setBackgroundBrush(const QBrush &brush) 286 | { 287 | m_isUserBrush = true; 288 | m_backgroundBrush = brush; 289 | update(); 290 | } 291 | -------------------------------------------------------------------------------- /src/groupitem.h: -------------------------------------------------------------------------------- 1 | #ifndef GROUPITEM_H 2 | #define GROUPITEM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "nodeitem.h" 10 | 11 | class GroupItem : public QGraphicsObject 12 | { 13 | Q_OBJECT 14 | public: 15 | GroupItem(QGraphicsItem *parent = Q_NULLPTR); 16 | 17 | private: 18 | 19 | QString m_title; 20 | QUuid m_uuid; 21 | QSizeF m_size; 22 | QSizeF m_indentSize; 23 | QSizeF m_titleSize; 24 | QFont m_font; 25 | QColor m_fontColor; 26 | QColor m_titleColor; 27 | QColor m_backgroundColor; 28 | QBrush m_backgroundBrush; 29 | bool m_isUserBrush; 30 | bool m_isSelected; 31 | bool m_isMove; 32 | 33 | QList m_nodeList; 34 | 35 | QRectF boundingRect() const; 36 | QPainterPath shape() const; 37 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget); 38 | 39 | void mousePressEvent(QGraphicsSceneMouseEvent *e); 40 | void mouseMoveEvent(QGraphicsSceneMouseEvent *e); 41 | void mouseReleaseEvent(QGraphicsSceneMouseEvent *e); 42 | 43 | signals: 44 | void selected(bool state); 45 | void becameEmpty(GroupItem *group); 46 | 47 | public slots: 48 | bool isHovered(const QPointF &point); 49 | 50 | void addNode(NodeItem *node); 51 | void setNodes(QList list); 52 | void removeNode(NodeItem *node); 53 | void clearNodes(); 54 | 55 | QList nodeList() const {return m_nodeList;} 56 | 57 | void updateSize(); 58 | 59 | QUuid uuid(){return m_uuid;} 60 | void setUuid(const QUuid &uuid){m_uuid = uuid;} 61 | 62 | QString title(){return m_title;} 63 | void setTitle(const QString &title); 64 | void clearTitle(); 65 | 66 | void setFont(const QFont &font); 67 | void setFontColor(const QColor &color); 68 | void setTitleColor(const QColor &color); 69 | void setBackgroundColor(const QColor &color); 70 | void setBackgroundBrush(const QBrush &brush); 71 | }; 72 | 73 | #endif // GROUPITEM_H 74 | -------------------------------------------------------------------------------- /src/nodeitem.cpp: -------------------------------------------------------------------------------- 1 | #include "nodeitem.h" 2 | #include "nodeview.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | NodeItem::NodeItem(QWidget *widget, QGraphicsItem *parent) : QGraphicsObject(parent), 13 | m_widget(widget), 14 | m_uuid(QUuid::createUuid()), 15 | m_size(QSizeF(200,200)), 16 | m_indentSize(QSizeF(14,2)), 17 | m_titleSize(QSizeF(0,0)), 18 | m_font(QFont("Calibri",10,QFont::Medium)), 19 | m_fontColor(QColor(Qt::black)), 20 | m_titleColor(QColor::fromRgb(160,160,160,210)), 21 | m_backgroundColor(QColor::fromRgb(190,190,190,210)), 22 | m_backgroundBrush(QBrush(m_backgroundColor)), 23 | m_isUserBrush(false), 24 | m_isSelected(false) 25 | { 26 | widget->setAttribute(Qt::WA_NoSystemBackground); 27 | QGraphicsProxyWidget *proxyWidget = new QGraphicsProxyWidget(this); 28 | proxyWidget->setWidget(widget); 29 | 30 | setZValue(1); 31 | setFlags(ItemIsSelectable | ItemIsMovable | ItemSendsGeometryChanges); 32 | setCacheMode(DeviceCoordinateCache); 33 | setAcceptHoverEvents(true); 34 | 35 | updateSize(); 36 | } 37 | 38 | QRectF NodeItem::boundingRect() const 39 | { 40 | return QRectF(0, 0, m_size.width(), m_size.height()); 41 | } 42 | 43 | QPainterPath NodeItem::shape() const 44 | { 45 | QPainterPath path; 46 | path.addRect(m_indentSize.width(),m_indentSize.height(),m_size.width()-m_indentSize.width()*2,m_size.height()-m_indentSize.height()*2); 47 | return path; 48 | } 49 | 50 | void NodeItem::paint(QPainter *p, const QStyleOptionGraphicsItem *item, QWidget *widget) 51 | { 52 | Q_UNUSED(widget); 53 | 54 | bool isSelected = false; 55 | 56 | if (item->state & QStyle::State_Selected) isSelected = true; 57 | 58 | QPen pen; 59 | pen.setStyle(Qt::DashDotLine); 60 | pen.setWidth(1); 61 | pen.setStyle(Qt::SolidLine); 62 | pen.setCapStyle(Qt::RoundCap); 63 | pen.setJoinStyle(Qt::RoundJoin); 64 | pen.setBrush(QColor::fromRgb(0,0,0,0)); 65 | p->setPen(pen); 66 | 67 | p->setRenderHints(QPainter::Antialiasing | 68 | QPainter::SmoothPixmapTransform | 69 | QPainter::HighQualityAntialiasing); 70 | 71 | QLinearGradient gradient_0(0,m_size.height()/2,m_size.width(),m_size.height()/2); 72 | gradient_0.setColorAt(0,QColor::fromRgb(0,0,0,0)); 73 | gradient_0.setColorAt(0.1,QColor::fromRgb(0,0,0,130)); 74 | gradient_0.setColorAt(0.9,QColor::fromRgb(0,0,0,130)); 75 | gradient_0.setColorAt(1,QColor::fromRgb(0,0,0,0)); 76 | 77 | p->setBrush(gradient_0); 78 | p->drawRoundedRect(2,1,m_size.width()-4,m_size.height()-2, 79 | (qreal)m_size.width()/((qreal)m_size.width()/5.0), 80 | (qreal)m_size.height()/((qreal)m_size.height()/5.0)); 81 | 82 | if (isSelected) 83 | { 84 | pen.setWidth(2); 85 | pen.setBrush(QColor::fromRgb(200,80,20,255)); 86 | setZValue(2); 87 | } 88 | else 89 | { 90 | pen.setWidth(1); 91 | pen.setBrush(QColor::fromRgb(0,0,0,170)); 92 | setZValue(1); 93 | } 94 | 95 | if (m_isSelected != isSelected) 96 | { 97 | m_isSelected = isSelected; 98 | emit selected(m_isSelected); 99 | } 100 | 101 | p->setPen(pen); 102 | 103 | if (m_isUserBrush) 104 | p->setBrush(m_backgroundBrush); 105 | else 106 | { 107 | qreal gradientHeight = ((m_titleSize.height() + m_indentSize.height()) * 100 / m_size.height()) * 0.01; 108 | QLinearGradient gradient_1(m_size.width() / 2, 0, m_size.width() / 2, m_size.height()); 109 | gradient_1.setColorAt(0, m_backgroundColor); 110 | gradient_1.setColorAt(gradientHeight, m_titleColor); 111 | gradient_1.setColorAt(gradientHeight + 0.01, m_backgroundColor); 112 | 113 | p->setBrush(gradient_1); 114 | } 115 | p->drawRoundedRect(6, 1, m_size.width() - 12, m_size.height() - 2, 116 | (qreal)m_size.width() / ((qreal)m_size.width() / 5.0), 117 | (qreal)m_size.height() / ((qreal)m_size.height() / 5.0)); 118 | 119 | p->setPen(QPen(m_fontColor,2)); 120 | p->setFont(m_font); 121 | p->drawText(QRect(m_indentSize.width(), m_indentSize.height(), 122 | m_size.width() - m_indentSize.width() * 2, 123 | m_titleSize.height()), Qt::AlignCenter, m_title); 124 | } 125 | 126 | QVariant NodeItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) 127 | { 128 | if (change == ItemPositionHasChanged) 129 | { 130 | emit positionChanged(); 131 | } 132 | else if (change == ItemVisibleHasChanged) 133 | { 134 | 135 | } 136 | 137 | return QGraphicsItem::itemChange(change, value); 138 | } 139 | 140 | bool NodeItem::isHovered(const QPointF &point) 141 | { 142 | bool result = false; 143 | 144 | QPointF s_pos = mapToScene(QPointF(-6,0)); 145 | QPointF s_size = mapToScene(QPointF(m_size.width()+6,m_size.height())); 146 | 147 | if (point.x() >= s_pos.x() && 148 | point.y() >= s_pos.y() && 149 | point.x() <= s_size.x() && 150 | point.y() <= s_size.y()) 151 | result = true; 152 | 153 | return result; 154 | } 155 | 156 | PortItem *NodeItem::isHoveredPort(const QPointF &point) 157 | { 158 | PortItem *result = Q_NULLPTR; 159 | 160 | foreach (PortItem *port, m_portList) 161 | { 162 | if (port->isHovered(point)) 163 | { 164 | result = port; 165 | break; 166 | } 167 | } 168 | 169 | return result; 170 | } 171 | 172 | void NodeItem::updateSize() 173 | { 174 | m_size = QSizeF(m_widget->width() + m_indentSize.width() * 2, 175 | m_widget->height() + m_indentSize.height() * 2 + m_titleSize.height()); 176 | 177 | m_widget->move(m_indentSize.width(), m_indentSize.height() + m_titleSize.height()); 178 | 179 | foreach (PortItem *port, m_portList) 180 | { 181 | if (port->portType() == PortItem::TypeIn) 182 | port->setPos(0, (port->posY() + m_indentSize.height() + 183 | m_titleSize.height()) - port->size().height() / 2); 184 | else 185 | port->setPos(m_size.width() - port->size().width(), 186 | (port->posY() + m_indentSize.height() + 187 | m_titleSize.height()) - port->size().height() / 2); 188 | } 189 | 190 | prepareGeometryChange(); 191 | update(); 192 | } 193 | 194 | void NodeItem::addPortIn(PortItem *port) 195 | { 196 | if (!port) return; 197 | if (m_portList.contains(port)) return; 198 | 199 | port->setParentItem(this); 200 | connect(this, &NodeItem::positionChanged, port, &PortItem::calculatePosition); 201 | m_portList.append(port); 202 | 203 | updateSize(); 204 | } 205 | 206 | PortItem *NodeItem::createPortIn(int posY, QColor color) 207 | { 208 | PortItem *port = new PortItem(PortItem::TypeIn,posY,m_portList.count(),color,this); 209 | connect(this, &NodeItem::positionChanged, port, &PortItem::calculatePosition); 210 | m_portList.append(port); 211 | 212 | updateSize(); 213 | 214 | return port; 215 | } 216 | 217 | PortItem *NodeItem::createPortIn(int posY, QColor color, uint num) 218 | { 219 | PortItem *port = new PortItem(PortItem::TypeIn,posY,m_portList.count(),color,this); 220 | connect(this, &NodeItem::positionChanged, port, &PortItem::calculatePosition); 221 | port->setNumber(num); 222 | m_portList.append(port); 223 | 224 | updateSize(); 225 | 226 | return port; 227 | } 228 | 229 | void NodeItem::addPortOut(PortItem *port) 230 | { 231 | if (!port) return; 232 | if (m_portList.contains(port)) return; 233 | 234 | port->setParentItem(this); 235 | connect(this, &NodeItem::positionChanged, port, &PortItem::calculatePosition); 236 | m_portList.append(port); 237 | 238 | updateSize(); 239 | } 240 | 241 | PortItem *NodeItem::createPortOut(int posY, QColor color) 242 | { 243 | PortItem *port = new PortItem(PortItem::TypeOut,posY,m_portList.count(),color,this); 244 | connect(this, &NodeItem::positionChanged, port, &PortItem::calculatePosition); 245 | m_portList.append(port); 246 | 247 | updateSize(); 248 | 249 | return port; 250 | } 251 | 252 | PortItem *NodeItem::createPortOut(int posY, QColor color, uint num) 253 | { 254 | PortItem *port = new PortItem(PortItem::TypeOut,posY,m_portList.count(),color,this); 255 | connect(this, &NodeItem::positionChanged, port, &PortItem::calculatePosition); 256 | port->setNumber(num); 257 | m_portList.append(port); 258 | 259 | updateSize(); 260 | 261 | return port; 262 | } 263 | 264 | PortItem *NodeItem::portAt(uint num) 265 | { 266 | PortItem *result = Q_NULLPTR; 267 | 268 | foreach (PortItem *port, m_portList) 269 | { 270 | if (port->number() == num) 271 | { 272 | result = port; 273 | break; 274 | } 275 | } 276 | 277 | return result; 278 | } 279 | 280 | void NodeItem::removePortAt(uint num) 281 | { 282 | removePort(portAt(num)); 283 | } 284 | 285 | void NodeItem::removePort(PortItem *port) 286 | { 287 | if (!port) return; 288 | if (!m_portList.contains(port)) return; 289 | 290 | disconnect(this, &NodeItem::positionChanged, port, &PortItem::calculatePosition); 291 | 292 | if (scene()->views().count() > 0) 293 | { 294 | if (scene()->views().at(0)->inherits("NodeView")) 295 | { 296 | 297 | NodeView *nView = (NodeView*)scene()->views().at(0); 298 | nView->removePortConnections(port); 299 | 300 | m_portList.removeOne(port); 301 | port->disconnect(); 302 | port->deleteLater(); 303 | } 304 | } 305 | } 306 | 307 | void NodeItem::setTitle(const QString &title) 308 | { 309 | m_title = title; 310 | 311 | QFontMetrics fm(m_font); 312 | #if QT_VERSION > 0x050906 313 | m_titleSize = QSizeF(fm.horizontalAdvance(m_title),fm.height() + 2); 314 | #else 315 | m_titleSize = QSizeF(fm.width(m_title),fm.height() + 2); 316 | #endif 317 | 318 | updateSize(); 319 | } 320 | 321 | void NodeItem::clearTitle() 322 | { 323 | m_title.clear(); 324 | m_titleSize = QSizeF(0,0); 325 | updateSize(); 326 | } 327 | 328 | void NodeItem::setFont(const QFont &font) 329 | { 330 | m_font = font; 331 | update(); 332 | } 333 | 334 | void NodeItem::setFontColor(const QColor &color) 335 | { 336 | m_fontColor = color; 337 | update(); 338 | } 339 | 340 | void NodeItem::setTitleColor(const QColor &color) 341 | { 342 | m_titleColor = color; 343 | update(); 344 | } 345 | 346 | void NodeItem::setBackgroundColor(const QColor &color) 347 | { 348 | m_backgroundColor = color; 349 | update(); 350 | } 351 | 352 | void NodeItem::setBackgroundBrush(const QBrush &brush) 353 | { 354 | m_isUserBrush = true; 355 | m_backgroundBrush = brush; 356 | update(); 357 | } 358 | -------------------------------------------------------------------------------- /src/nodeitem.h: -------------------------------------------------------------------------------- 1 | #ifndef NODEITEM_H 2 | #define NODEITEM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "portitem.h" 9 | 10 | class NodeItem : public QGraphicsObject 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit NodeItem(QWidget *widget, QGraphicsItem *parent = Q_NULLPTR); 15 | 16 | private: 17 | 18 | QWidget *m_widget; 19 | QUuid m_uuid; 20 | QSizeF m_size; 21 | QSizeF m_indentSize; 22 | QSizeF m_titleSize; 23 | QFont m_font; 24 | QColor m_fontColor; 25 | QColor m_titleColor; 26 | QColor m_backgroundColor; 27 | QBrush m_backgroundBrush; 28 | bool m_isUserBrush; 29 | bool m_isSelected; 30 | 31 | QList m_portList; 32 | QString m_title; 33 | 34 | QRectF boundingRect() const; 35 | QPainterPath shape() const; 36 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget); 37 | 38 | protected: 39 | QVariant itemChange(GraphicsItemChange change, const QVariant &value); 40 | 41 | signals: 42 | void selected(bool state); 43 | void positionChanged(); 44 | 45 | public slots: 46 | bool isHovered(const QPointF &point); 47 | PortItem *isHoveredPort(const QPointF &point); 48 | 49 | void updateSize(); 50 | 51 | QWidget *widget(){return m_widget;} 52 | 53 | void addPortIn(PortItem *port); 54 | PortItem *createPortIn(int posY, QColor color); 55 | PortItem *createPortIn(int posY, QColor color, uint num); 56 | 57 | void addPortOut(PortItem *port); 58 | PortItem *createPortOut(int posY, QColor color); 59 | PortItem *createPortOut(int posY, QColor color, uint num); 60 | 61 | QList portList(){return m_portList;} 62 | PortItem* portAt(uint num); 63 | 64 | void removePortAt(uint num); 65 | void removePort(PortItem *port); 66 | 67 | QUuid uuid(){return m_uuid;} 68 | void setUuid(const QUuid &uuid){m_uuid = uuid;} 69 | 70 | QString title(){return m_title;} 71 | void setTitle(const QString &title); 72 | void clearTitle(); 73 | 74 | qreal width(){return m_size.width();} 75 | qreal height(){return m_size.height();} 76 | QSizeF size(){return m_size;} 77 | 78 | void setFont(const QFont &font); 79 | void setFontColor(const QColor &color); 80 | void setTitleColor(const QColor &color); 81 | void setBackgroundColor(const QColor &color); 82 | void setBackgroundBrush(const QBrush &brush); 83 | }; 84 | 85 | #endif // NODEITEM_H 86 | -------------------------------------------------------------------------------- /src/nodeview.cpp: -------------------------------------------------------------------------------- 1 | #include "nodeview.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | NodeView::NodeView(QWidget *parent) : QGraphicsView(parent), 11 | m_scene(new QGraphicsScene), 12 | m_scenePos(QPointF(0,0)), 13 | m_pressPos(QPointF(0,0)), 14 | m_moveScene(false), 15 | m_currentScale(1.0), 16 | m_activeRope(0), 17 | m_isCheckingColor(true), 18 | m_isOnlyOneInputConnection(true), 19 | m_ropeFlexion(100.0), 20 | m_isConnectionDragable(true) 21 | { 22 | setRenderHint(QPainter::Antialiasing); 23 | setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); 24 | setBackgroundBrush(QBrush(QColor::fromRgb(80,80,80))); 25 | 26 | setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 27 | setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 28 | setTransformationAnchor(QGraphicsView::AnchorUnderMouse); 29 | setCacheMode(QGraphicsView::CacheBackground); 30 | 31 | m_scene->setSceneRect(m_scene->itemsBoundingRect()); 32 | setScene(m_scene); 33 | } 34 | 35 | NodeView::~NodeView() 36 | { 37 | delete m_scene; 38 | } 39 | 40 | void NodeView::wheelEvent(QWheelEvent *e) 41 | { 42 | qreal scaleFactor = 1.15; 43 | 44 | if (e->delta() > 0) 45 | { 46 | if(m_currentScale < scaleFactor) 47 | { 48 | scale(scaleFactor,scaleFactor); 49 | m_currentScale *= scaleFactor; 50 | } 51 | } 52 | else if(m_currentScale > 0.1) 53 | { 54 | scale(1/scaleFactor, 1/scaleFactor); 55 | m_currentScale /= scaleFactor; 56 | } 57 | 58 | update(); 59 | } 60 | 61 | void NodeView::drawBackground(QPainter *painter, const QRectF &r) 62 | { 63 | QGraphicsView::drawBackground(painter, r); 64 | 65 | QPen pfine(QColor::fromRgb(50,50,50), 0.6); 66 | 67 | painter->setPen(pfine); 68 | drawGrid(painter,15); 69 | 70 | QPen p(QColor::fromRgb(50,50,50), 2.0); 71 | 72 | painter->setPen(p); 73 | drawGrid(painter,150); 74 | } 75 | 76 | void NodeView::drawGrid(QPainter *painter, double gridStep) 77 | { 78 | QRect windowRect = rect(); 79 | QPointF tl = mapToScene(windowRect.topLeft()); 80 | QPointF br = mapToScene(windowRect.bottomRight()); 81 | 82 | double left = qFloor(tl.x() / gridStep - 0.5); 83 | double right = qFloor(br.x() / gridStep + 1.0); 84 | double bottom = qFloor(tl.y() / gridStep - 0.5); 85 | double top = qFloor(br.y() / gridStep + 1.0); 86 | 87 | for (int xi = int(left); xi <= int(right); ++xi) 88 | { 89 | QLineF line(xi * gridStep, bottom * gridStep, 90 | xi * gridStep, top * gridStep ); 91 | 92 | painter->drawLine(line); 93 | } 94 | 95 | for (int yi = int(bottom); yi <= int(top); ++yi) 96 | { 97 | QLineF line(left * gridStep, yi * gridStep, 98 | right * gridStep, yi * gridStep ); 99 | painter->drawLine(line); 100 | } 101 | } 102 | 103 | void NodeView::mousePressEvent(QMouseEvent *e) 104 | { 105 | QMouseEvent fake(e->type(), e->pos(), Qt::LeftButton, Qt::LeftButton, e->modifiers()); 106 | 107 | m_scenePos = mapToScene(e->pos()); 108 | m_pressPos = m_scenePos; 109 | 110 | if (e->button() == Qt::MiddleButton) 111 | { 112 | setDragMode(QGraphicsView::ScrollHandDrag); 113 | setInteractive(false); 114 | 115 | e = &fake; 116 | 117 | m_moveScene = true; 118 | } 119 | else if (e->button() == Qt::LeftButton) 120 | { 121 | setDragMode(QGraphicsView::RubberBandDrag); 122 | 123 | if (m_isConnectionDragable) 124 | checkRopeCreation(mapToScene(e->pos())); 125 | } 126 | else if (e->button() == Qt::RightButton) 127 | { 128 | NodeItem *node = checkNodeHit(mapToScene(e->pos())); 129 | 130 | if (node) 131 | emit calledMenuNode(node); 132 | else emit calledMenuView(); 133 | } 134 | 135 | update(); 136 | QGraphicsView::mousePressEvent(e); 137 | } 138 | 139 | void NodeView::mouseMoveEvent(QMouseEvent *e) 140 | { 141 | m_scenePos = mapToScene(e->pos()); 142 | PortItem *hitPort = checkPortHit(m_scenePos); 143 | 144 | if (m_activeRope) 145 | { 146 | if (hitPort) 147 | m_activeRope->setMousePoint(hitPort->portPos()); 148 | else m_activeRope->setMousePoint(m_scenePos); 149 | } 150 | 151 | if (m_moveScene) 152 | { 153 | QPointF difference = m_pressPos - m_scenePos; 154 | setSceneRect(sceneRect().translated(difference.x(), difference.y())); 155 | } 156 | 157 | update(); 158 | QGraphicsView::mouseMoveEvent(e); 159 | } 160 | 161 | void NodeView::mouseReleaseEvent(QMouseEvent *e) 162 | { 163 | QMouseEvent fake(e->type(), e->pos(), Qt::LeftButton, Qt::LeftButton, e->modifiers()); 164 | 165 | if (e->button() == Qt::LeftButton) 166 | { 167 | checkNodesSelected(); 168 | checkRopeConnection(mapToScene(e->pos())); 169 | } 170 | else if (e->button() == Qt::MiddleButton) 171 | { 172 | setDragMode(QGraphicsView::NoDrag); 173 | setInteractive(true); 174 | 175 | e = &fake; 176 | } 177 | 178 | m_moveScene = false; 179 | update(); 180 | QGraphicsView::mouseReleaseEvent(e); 181 | } 182 | 183 | void NodeView::checkRopeCreation(const QPointF &point) 184 | { 185 | NodeItem *hitNode = checkNodeHit(point); 186 | 187 | if (!hitNode) 188 | return; 189 | 190 | PortItem *hitPort = hitNode->isHoveredPort(point); 191 | 192 | if (!hitPort) 193 | return; 194 | 195 | createRopeHitPort(hitPort); 196 | } 197 | 198 | void NodeView::checkRopeConnection(const QPointF &point) 199 | { 200 | if (!m_activeRope) 201 | return; 202 | 203 | NodeItem *hitNode = checkNodeHit(point); 204 | 205 | if (!hitNode) 206 | { 207 | removeActiveRope(); 208 | return; 209 | } 210 | 211 | PortItem *port = hitNode->isHoveredPort(point); 212 | 213 | if (port) 214 | createConnectionToPort(port); 215 | else removeActiveRope(); 216 | } 217 | 218 | void NodeView::checkNodesSelected() 219 | { 220 | QList selectedNodeList; 221 | 222 | foreach (NodeItem *node, m_nodeList) 223 | if (node->isSelected()) 224 | selectedNodeList.append(node); 225 | 226 | if (m_selectedNodes != selectedNodeList) 227 | { 228 | m_selectedNodes = selectedNodeList; 229 | emit selectionsChanged(); 230 | } 231 | } 232 | 233 | NodeItem *NodeView::checkNodeHit(const QPointF &point) 234 | { 235 | NodeItem *hitNode = Q_NULLPTR; 236 | 237 | foreach (NodeItem *node, m_nodeList) 238 | if (node->isHovered(point)) 239 | { 240 | hitNode = node; 241 | break; 242 | } 243 | 244 | return hitNode; 245 | } 246 | 247 | PortItem *NodeView::checkPortHit(const QPointF &point) 248 | { 249 | PortItem *hitPort = Q_NULLPTR; 250 | 251 | NodeItem *hitNode = checkNodeHit(point); 252 | 253 | if (hitNode) 254 | hitPort = hitNode->isHoveredPort(point); 255 | 256 | return hitPort; 257 | } 258 | 259 | void NodeView::createRopeHitPort(PortItem *port) 260 | { 261 | if (!port) 262 | return; 263 | 264 | setDragMode(QGraphicsView::NoDrag); 265 | 266 | if (port->portType() == PortItem::TypeIn) 267 | { 268 | if (isPortFree(port)) 269 | { 270 | m_activeRope = new RopeItem(0,port); 271 | m_activeRope->setFlexion(m_ropeFlexion); 272 | m_scene->addItem(m_activeRope); 273 | m_ropeList.append(m_activeRope); 274 | } 275 | else 276 | { 277 | m_activeRope = getRopeWithPort(port); 278 | m_activeRope->removePortIn(); 279 | m_activeRope->setMousePoint(m_scenePos); 280 | } 281 | } 282 | else 283 | { 284 | m_activeRope = new RopeItem(port,0); 285 | m_activeRope->setFlexion(m_ropeFlexion); 286 | m_scene->addItem(m_activeRope); 287 | m_ropeList.append(m_activeRope); 288 | } 289 | } 290 | 291 | void NodeView::createConnectionToPort(PortItem *port) 292 | { 293 | if (!m_activeRope) 294 | return; 295 | 296 | bool colorPermission = false; 297 | bool severalConnectionPermission = false; 298 | 299 | if (m_isCheckingColor && m_activeRope->color() == port->color()) 300 | colorPermission = true; 301 | else if (!m_isCheckingColor) 302 | colorPermission = true; 303 | 304 | if (m_isOnlyOneInputConnection && isPortFree(port)) 305 | severalConnectionPermission = true; 306 | else if (!m_isOnlyOneInputConnection) 307 | severalConnectionPermission = true; 308 | 309 | if (m_activeRope->portIn() && 310 | !m_activeRope->portOut() && 311 | port->portType() == PortItem::TypeOut && 312 | colorPermission) 313 | { 314 | m_activeRope->setPortOut(port); 315 | } 316 | 317 | if (m_activeRope->portOut() && 318 | !m_activeRope->portIn() && 319 | port->portType() == PortItem::TypeIn && 320 | colorPermission && 321 | severalConnectionPermission) 322 | { 323 | m_activeRope->setPortIn(port); 324 | } 325 | 326 | if (m_activeRope->isConnected()) 327 | m_activeRope = Q_NULLPTR; 328 | else removeActiveRope(); 329 | } 330 | 331 | void NodeView::removeActiveRope() 332 | { 333 | if (!m_activeRope) 334 | return; 335 | 336 | m_activeRope->removePortIn(); 337 | m_activeRope->removePortOut(); 338 | 339 | m_ropeList.removeOne(m_activeRope); 340 | m_activeRope->disconnect(); 341 | m_activeRope->deleteLater(); 342 | m_activeRope = Q_NULLPTR; 343 | } 344 | 345 | RopeItem *NodeView::getRopeWithPort(PortItem *port) 346 | { 347 | RopeItem *result = Q_NULLPTR; 348 | 349 | foreach (RopeItem *rope, m_ropeList) 350 | { 351 | if (rope->portIn() == port) 352 | { 353 | result = rope; 354 | break; 355 | } 356 | else if (rope->portOut() == port) 357 | { 358 | result = rope; 359 | break; 360 | } 361 | } 362 | 363 | return result; 364 | } 365 | 366 | void NodeView::addNode(NodeItem *node) 367 | { 368 | node->setPos(m_scenePos); 369 | m_scene->addItem(node); 370 | m_nodeList.append(node); 371 | } 372 | 373 | NodeItem *NodeView::createNode(QWidget *widget) 374 | { 375 | NodeItem *node = new NodeItem(widget); 376 | addNode(node); 377 | 378 | return node; 379 | } 380 | 381 | void NodeView::removeNode(NodeItem *node) 382 | { 383 | foreach (PortItem *port, node->portList()) 384 | { 385 | QList repesWithNode; 386 | 387 | foreach (RopeItem *rope, m_ropeList) 388 | { 389 | if (rope->portIn() == port) 390 | repesWithNode.append(rope); 391 | else if (rope->portOut() == port) 392 | repesWithNode.append(rope); 393 | } 394 | 395 | foreach (RopeItem *rope, repesWithNode) 396 | { 397 | rope->removePortIn(); 398 | rope->removePortOut(); 399 | 400 | m_ropeList.removeOne(rope); 401 | rope->disconnect(); 402 | rope->deleteLater(); 403 | } 404 | 405 | port->disconnect(); 406 | port->deleteLater(); 407 | } 408 | 409 | foreach(GroupItem *group, m_groupList) 410 | group->removeNode(node); 411 | 412 | m_nodeList.removeOne(node); 413 | m_selectedNodes.removeOne(node); 414 | node->disconnect(); 415 | node->deleteLater(); 416 | } 417 | 418 | NodeItem *NodeView::nodeAt(const QUuid &uuid) 419 | { 420 | NodeItem *result = Q_NULLPTR; 421 | 422 | foreach (NodeItem *node, m_nodeList) 423 | { 424 | if (node->uuid() == uuid) 425 | { 426 | result = node; 427 | break; 428 | } 429 | } 430 | 431 | return result; 432 | } 433 | 434 | void NodeView::addGroup(GroupItem *group) 435 | { 436 | group->setPos(m_scenePos); 437 | m_scene->addItem(group); 438 | m_groupList.append(group); 439 | connect(group, &GroupItem::becameEmpty, this, &NodeView::removeGroup); 440 | } 441 | 442 | GroupItem *NodeView::createGroup(QList list) 443 | { 444 | GroupItem *group = new GroupItem; 445 | group->setNodes(list); 446 | addGroup(group); 447 | 448 | return group; 449 | } 450 | 451 | GroupItem *NodeView::getItemGroup(NodeItem *node) 452 | { 453 | GroupItem *result = Q_NULLPTR; 454 | 455 | foreach (GroupItem *group, m_groupList) 456 | { 457 | if(group->nodeList().contains(node)) 458 | { 459 | result = group; 460 | break; 461 | } 462 | } 463 | 464 | return result; 465 | } 466 | 467 | void NodeView::removeGroup(GroupItem *group) 468 | { 469 | if (!m_groupList.contains(group)) return; 470 | 471 | m_groupList.removeOne(group); 472 | 473 | group->disconnect(); 474 | group->deleteLater(); 475 | } 476 | 477 | bool NodeView::createConnection(PortItem *portOut, PortItem *portIn) 478 | { 479 | bool result = false; 480 | 481 | if (isPortFree(portIn)) 482 | { 483 | RopeItem *rope = new RopeItem(portOut,portIn); 484 | rope->setFlexion(m_ropeFlexion); 485 | m_scene->addItem(rope); 486 | m_ropeList.append(rope); 487 | result = true; 488 | } 489 | 490 | return result; 491 | } 492 | 493 | void NodeView::removePortConnections(PortItem *port) 494 | { 495 | QList ropesToRemove; 496 | foreach (RopeItem *rope, m_ropeList) 497 | if (rope->portIn() == port || rope->portOut() == port) 498 | ropesToRemove.append(rope); 499 | 500 | foreach (RopeItem *rope, ropesToRemove) 501 | { 502 | m_ropeList.removeOne(rope); 503 | rope->disconnect(); 504 | rope->deleteLater(); 505 | } 506 | } 507 | 508 | void NodeView::clearView() 509 | { 510 | foreach (NodeItem *node, m_nodeList) 511 | { 512 | if(node->widget()) 513 | node->widget()->deleteLater(); 514 | 515 | removeNode(node); 516 | } 517 | 518 | foreach (GroupItem *group, m_groupList) 519 | { 520 | group->disconnect(); 521 | group->deleteLater(); 522 | } 523 | 524 | m_groupList.clear(); 525 | m_nodeList.clear(); 526 | m_ropeList.clear(); 527 | m_selectedNodes.clear(); 528 | m_scene->clear(); 529 | } 530 | 531 | bool NodeView::isPortFree(PortItem *port) 532 | { 533 | bool result = true; 534 | foreach (RopeItem *rope, m_ropeList) 535 | { 536 | if (rope->portIn() == port || rope->portOut() == port) 537 | { 538 | result = false; 539 | break; 540 | } 541 | } 542 | return result; 543 | } 544 | 545 | void NodeView::setCheckingColor(bool state) 546 | { 547 | m_isCheckingColor = state; 548 | update(); 549 | } 550 | 551 | void NodeView::setOnlyOneInputConnection(bool state) 552 | { 553 | m_isOnlyOneInputConnection = state; 554 | update(); 555 | } 556 | 557 | void NodeView::setRopeFlexion(qreal value) 558 | { 559 | m_ropeFlexion = value; 560 | update(); 561 | } 562 | 563 | void NodeView::setConnectionDragable(bool state) 564 | { 565 | m_isConnectionDragable = state; 566 | } 567 | -------------------------------------------------------------------------------- /src/nodeview.h: -------------------------------------------------------------------------------- 1 | #ifndef NODEVIEW_H 2 | #define NODEVIEW_H 3 | 4 | #include 5 | #include "nodeitem.h" 6 | #include "ropeitem.h" 7 | #include "groupitem.h" 8 | 9 | class NodeView : public QGraphicsView 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit NodeView(QWidget *parent = Q_NULLPTR); 14 | ~NodeView(); 15 | 16 | private: 17 | QGraphicsScene *m_scene; 18 | QPointF m_scenePos; 19 | QPointF m_pressPos; 20 | bool m_moveScene; 21 | qreal m_currentScale; 22 | 23 | RopeItem *m_activeRope; 24 | QList m_nodeList; 25 | QList m_ropeList; 26 | QList m_groupList; 27 | 28 | QList m_selectedNodes; 29 | 30 | bool m_isCheckingColor; 31 | bool m_isOnlyOneInputConnection; 32 | qreal m_ropeFlexion; 33 | bool m_isConnectionDragable; 34 | 35 | void wheelEvent(QWheelEvent *event); 36 | void drawBackground(QPainter *painter, const QRectF &r); 37 | void drawGrid(QPainter *painter, double gridStep); 38 | 39 | void mousePressEvent(QMouseEvent *e); 40 | void mouseMoveEvent(QMouseEvent *e); 41 | void mouseReleaseEvent(QMouseEvent *e); 42 | 43 | void checkRopeCreation(const QPointF &point); 44 | void checkRopeConnection(const QPointF &point); 45 | void checkNodesSelected(); 46 | 47 | NodeItem *checkNodeHit(const QPointF &point); 48 | PortItem *checkPortHit(const QPointF &point); 49 | 50 | void createRopeHitPort(PortItem *port); 51 | void createConnectionToPort(PortItem *port); 52 | 53 | void removeActiveRope(); 54 | 55 | RopeItem *getRopeWithPort(PortItem *port); 56 | 57 | signals: 58 | void selectionsChanged(); 59 | void calledMenuNode(NodeItem *node); 60 | void calledMenuView(); 61 | 62 | public slots: 63 | void addNode(NodeItem *node); 64 | NodeItem *createNode(QWidget *widget); 65 | void removeNode(NodeItem *node); 66 | NodeItem *nodeAt(const QUuid &uuid); 67 | 68 | void addGroup(GroupItem *group); 69 | GroupItem *createGroup(QList list); 70 | GroupItem *getItemGroup(NodeItem* node); 71 | void removeGroup(GroupItem *group); 72 | 73 | bool createConnection(PortItem *portOut, PortItem *portIn); 74 | void removePortConnections(PortItem *port); 75 | 76 | void clearView(); 77 | 78 | QList nodeList(){return m_nodeList;} 79 | QList ropeList(){return m_ropeList;} 80 | QList groupList(){return m_groupList;} 81 | QList selectedNodeList(){return m_selectedNodes;} 82 | 83 | bool isPortFree(PortItem *port); 84 | 85 | void setCheckingColor(bool state = true); 86 | void setOnlyOneInputConnection(bool state = true); 87 | void setRopeFlexion(qreal value = 100.0); 88 | void setConnectionDragable(bool state = true); 89 | }; 90 | 91 | #endif // NODEVIEW_H 92 | -------------------------------------------------------------------------------- /src/portitem.cpp: -------------------------------------------------------------------------------- 1 | #include "portitem.h" 2 | #include "nodeitem.h" 3 | #include 4 | #include 5 | 6 | PortItem::PortItem(PortItem::PortTypes type, int posY, uint num, QColor color, QGraphicsItem *parent) : QGraphicsObject(parent), 7 | m_posY(posY), 8 | m_size(QSize(12,12)), 9 | m_color(color), 10 | m_number(num), 11 | m_ellipseRadius(5), 12 | m_isHovered(false), 13 | m_portPos(mapToScene(QPointF(0,0))), 14 | m_type(type) 15 | { 16 | setAcceptHoverEvents(true); 17 | setFlags(ItemIsSelectable); 18 | } 19 | 20 | QRectF PortItem::boundingRect() const 21 | { 22 | return QRectF(0, 0, m_size.width(), m_size.height()); 23 | } 24 | 25 | QPainterPath PortItem::shape() const 26 | { 27 | QPainterPath path; 28 | path.addRect(0, 0, m_size.width(), m_size.height()); 29 | return path; 30 | } 31 | 32 | void PortItem::paint(QPainter *p, const QStyleOptionGraphicsItem *item, QWidget *widget) 33 | { 34 | Q_UNUSED(widget); 35 | Q_UNUSED(item); 36 | 37 | p->setRenderHint(QPainter::Antialiasing); 38 | 39 | p->setBrush(QBrush(m_color)); 40 | 41 | if (m_isHovered) 42 | p->setPen(QPen(m_color,1)); 43 | else p->setPen(QPen(QColor::fromRgb(0,0,0,100),1)); 44 | 45 | QPointF portPos = QPointF(m_ellipseRadius,m_size.height()/2); 46 | p->drawEllipse(portPos,m_ellipseRadius,m_ellipseRadius); 47 | calculatePosition(); 48 | } 49 | 50 | QUuid PortItem::parentUuid() 51 | { 52 | QUuid result; 53 | 54 | NodeItem *parentNode = Q_NULLPTR; 55 | 56 | if (parentItem()) 57 | parentNode = (NodeItem*)parentItem(); 58 | 59 | if (parentNode) 60 | result = parentNode->uuid(); 61 | 62 | return result; 63 | } 64 | 65 | void PortItem::resize(qreal w, qreal h) 66 | { 67 | m_size = QSize(w,h); 68 | 69 | prepareGeometryChange(); 70 | update(); 71 | } 72 | 73 | void PortItem::setPosY(int posY) 74 | { 75 | m_posY = posY; 76 | setPos(pos().x(),(qreal)posY); 77 | update(); 78 | } 79 | 80 | void PortItem::setColor(const QColor &color) 81 | { 82 | m_color = color; 83 | update(); 84 | } 85 | 86 | bool PortItem::isHovered(const QPointF &point) 87 | { 88 | bool result = false; 89 | 90 | QPointF s_pos = mapToScene(QPointF(0,0)); 91 | QPointF s_size = mapToScene(QPointF(m_size.width(),m_size.height())); 92 | 93 | if (point.x() >= s_pos.x() && 94 | point.y() >= s_pos.y() && 95 | point.x() <= s_size.x() && 96 | point.y() <= s_size.y()) 97 | result = true; 98 | 99 | if (m_isHovered != result) 100 | { 101 | m_isHovered = result; 102 | update(); 103 | } 104 | 105 | return m_isHovered; 106 | } 107 | 108 | void PortItem::calculatePosition() 109 | { 110 | QPointF portPos = QPointF(m_ellipseRadius, m_size.height()/2); 111 | QPointF mapPortPos = mapToScene(portPos); 112 | 113 | if (mapPortPos != m_portPos) 114 | { 115 | m_portPos = mapPortPos; 116 | emit positionChanged(); 117 | } 118 | } 119 | 120 | void PortItem::setValue(const QVariant &value) 121 | { 122 | m_value = value; 123 | emit valueChanged(value); 124 | } 125 | -------------------------------------------------------------------------------- /src/portitem.h: -------------------------------------------------------------------------------- 1 | #ifndef PORTITEM_H 2 | #define PORTITEM_H 3 | 4 | #include 5 | #include 6 | 7 | class PortItem : public QGraphicsObject 8 | { 9 | Q_OBJECT 10 | public: 11 | 12 | enum PortTypes 13 | { 14 | TypeIn, 15 | TypeOut 16 | }; 17 | 18 | explicit PortItem(PortItem::PortTypes type, int posY, uint num, QColor color, QGraphicsItem *parent = Q_NULLPTR); 19 | 20 | private: 21 | 22 | int m_posY; 23 | QSizeF m_size; 24 | QColor m_color; 25 | uint m_number; 26 | 27 | qreal m_ellipseRadius; 28 | bool m_isHovered; 29 | QPointF m_portPos; 30 | QVariant m_value; 31 | PortTypes m_type; 32 | 33 | QRectF boundingRect() const; 34 | QPainterPath shape() const; 35 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget); 36 | 37 | protected: 38 | 39 | signals: 40 | void positionChanged(); 41 | void valueChanged(const QVariant &value); 42 | 43 | public slots: 44 | QUuid parentUuid(); 45 | 46 | QSizeF size(){return m_size;} 47 | void resize(qreal w, qreal h); 48 | 49 | int posY(){return m_posY;} 50 | void setPosY(int posY); 51 | 52 | QColor color(){return m_color;} 53 | void setColor(const QColor &color); 54 | 55 | bool isHovered(const QPointF &point); 56 | 57 | void calculatePosition(); 58 | QPointF portPos(){return m_portPos;} 59 | 60 | uint number(){return m_number;} 61 | void setNumber(uint num){m_number = num;} 62 | 63 | QVariant value(){return m_value;} 64 | void setValue(const QVariant &value); 65 | 66 | PortItem::PortTypes portType(){return m_type;} 67 | void setPortType(PortItem::PortTypes type){m_type = type;} 68 | }; 69 | 70 | #endif // PORTITEM_H 71 | -------------------------------------------------------------------------------- /src/ropeitem.cpp: -------------------------------------------------------------------------------- 1 | #include "ropeitem.h" 2 | #include 3 | #include 4 | #include 5 | 6 | qreal distance(const QPointF &a, const QPointF &b) 7 | { 8 | qreal dx = a.x() - b.x(); 9 | qreal dy = a.y() - b.y(); 10 | return qSqrt(dx * dx + dy * dy); 11 | } 12 | 13 | RopeItem::RopeItem(PortItem *portOut, PortItem *portIn, QGraphicsItem *parent) 14 | : QGraphicsObject(parent), 15 | m_portOut(portOut), 16 | m_portIn(portIn), 17 | m_size(QSize(20,20)), 18 | m_color(QColor(Qt::white)), 19 | m_isConnected(false), 20 | m_flexion(100.0) 21 | { 22 | setZValue(0); 23 | 24 | if (m_portIn) 25 | m_color = m_portIn->color(); 26 | 27 | if (m_portOut) 28 | m_color = m_portOut->color(); 29 | 30 | calculateRope(); 31 | createPortsConnection(); 32 | } 33 | 34 | QRectF RopeItem::boundingRect() const 35 | { 36 | return QRectF(0, 0, m_size.width(), m_size.height()); 37 | } 38 | 39 | QPainterPath RopeItem::shape() const 40 | { 41 | QPainterPath path; 42 | path.addRect(0, 0, m_size.width(), m_size.height()); 43 | return path; 44 | } 45 | 46 | void RopeItem::paint(QPainter *p, const QStyleOptionGraphicsItem *item, QWidget *widget) 47 | { 48 | Q_UNUSED(widget); 49 | Q_UNUSED(item); 50 | 51 | QPen pen; 52 | pen.setStyle(Qt::SolidLine); 53 | pen.setCapStyle(Qt::RoundCap); 54 | pen.setJoinStyle(Qt::RoundJoin); 55 | 56 | p->setRenderHints(QPainter::Antialiasing | 57 | QPainter::SmoothPixmapTransform | 58 | QPainter::HighQualityAntialiasing); 59 | 60 | pen.setBrush(Qt::black); 61 | pen.setWidth(4); 62 | p->setPen(pen); 63 | 64 | QPainterPath path; 65 | 66 | path.moveTo(m_pointIn); 67 | 68 | qreal flexion = distance(m_pointOut, m_pointIn) / 2; 69 | if (flexion > m_flexion) flexion = m_flexion; 70 | 71 | path.cubicTo(m_pointIn.x() - flexion, m_pointIn.y(), 72 | m_pointOut.x() + flexion, m_pointOut.y(), 73 | m_pointOut.x(), m_pointOut.y()); 74 | 75 | p->drawPath(path); 76 | 77 | pen.setBrush(m_color); 78 | pen.setWidth(3); 79 | p->setPen(pen); 80 | p->drawPath(path); 81 | } 82 | 83 | void RopeItem::createPortsConnection() 84 | { 85 | if (m_portIn && m_portOut && !m_isConnected) 86 | { 87 | connect(m_portIn,SIGNAL(positionChanged()),this,SLOT(calculateRope())); 88 | connect(m_portOut,SIGNAL(positionChanged()),this,SLOT(calculateRope())); 89 | connect(m_portOut,SIGNAL(valueChanged(QVariant)),m_portIn,SLOT(setValue(QVariant))); 90 | m_portIn->setValue(m_portOut->value()); 91 | m_isConnected = true; 92 | } 93 | } 94 | 95 | void RopeItem::disconnectFromPorts() 96 | { 97 | if (m_portIn && m_portOut && m_isConnected) 98 | { 99 | disconnect(m_portIn,SIGNAL(positionChanged()),this,SLOT(calculateRope())); 100 | disconnect(m_portOut,SIGNAL(positionChanged()),this,SLOT(calculateRope())); 101 | disconnect(m_portOut,SIGNAL(valueChanged(QVariant)),m_portIn,SLOT(setValue(QVariant))); 102 | m_portIn->setValue(QVariant()); 103 | m_isConnected = false; 104 | } 105 | } 106 | 107 | void RopeItem::setColor(QColor color) 108 | { 109 | m_color = color; 110 | update(); 111 | } 112 | 113 | void RopeItem::setFlexion(qreal value) 114 | { 115 | m_flexion = value; 116 | update(); 117 | } 118 | 119 | void RopeItem::setPortOut(PortItem *port) 120 | { 121 | m_portOut = port; 122 | calculateRope(); 123 | createPortsConnection(); 124 | } 125 | 126 | void RopeItem::removePortOut() 127 | { 128 | disconnectFromPorts(); 129 | m_portOut = Q_NULLPTR; 130 | calculateRope(); 131 | } 132 | 133 | void RopeItem::setPortIn(PortItem *port) 134 | { 135 | m_portIn = port; 136 | calculateRope(); 137 | createPortsConnection(); 138 | } 139 | 140 | void RopeItem::removePortIn() 141 | { 142 | disconnectFromPorts(); 143 | m_portIn = Q_NULLPTR; 144 | calculateRope(); 145 | } 146 | 147 | void RopeItem::setMousePoint(QPointF point) 148 | { 149 | m_mousePoint = point; 150 | calculateRope(); 151 | } 152 | 153 | void RopeItem::calculateRope() 154 | { 155 | QPointF portPosOut = m_mousePoint; 156 | QPointF portPosIn = m_mousePoint; 157 | 158 | if (m_portOut) 159 | { 160 | portPosOut = m_portOut->portPos(); 161 | if (portPosIn == QPointF(0,0)) 162 | portPosIn = portPosOut; 163 | } 164 | 165 | if (m_portIn) 166 | { 167 | portPosIn = m_portIn->portPos(); 168 | if (portPosOut == QPointF(0,0)) 169 | portPosOut = portPosIn; 170 | } 171 | 172 | qreal addition = 40; 173 | qreal x_pos = qMin(portPosOut.x(), portPosIn.x()) - addition; 174 | qreal y_pos = qMin(portPosOut.y(), portPosIn.y()) - addition; 175 | qreal w_size = qMax(portPosOut.x(), portPosIn.x()) - x_pos + addition; 176 | qreal h_size = qMax(portPosOut.y(), portPosIn.y()) - y_pos + addition; 177 | 178 | setPos(x_pos ,y_pos); 179 | m_size = QSize(w_size, h_size); 180 | 181 | qreal pointOutX = portPosOut.x() - x_pos; 182 | qreal pointOutY = portPosOut.y() - y_pos; 183 | 184 | qreal pointInX = portPosIn.x() - x_pos; 185 | qreal pointInY = portPosIn.y() - y_pos; 186 | 187 | m_pointOut = QPointF(pointOutX,pointOutY); 188 | m_pointIn = QPointF(pointInX,pointInY); 189 | 190 | prepareGeometryChange(); 191 | update(); 192 | } 193 | -------------------------------------------------------------------------------- /src/ropeitem.h: -------------------------------------------------------------------------------- 1 | #ifndef CONNECTION_H 2 | #define CONNECTION_H 3 | 4 | #include 5 | #include "portitem.h" 6 | 7 | class RopeItem : public QGraphicsObject 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit RopeItem(PortItem *portOut, PortItem *portIn, QGraphicsItem *parent = Q_NULLPTR); 12 | 13 | private: 14 | 15 | PortItem *m_portOut; 16 | PortItem *m_portIn; 17 | 18 | QPointF m_pointOut; 19 | QPointF m_pointIn; 20 | 21 | QPointF m_mousePoint; 22 | 23 | QSize m_size; 24 | QColor m_color; 25 | 26 | bool m_isConnected; 27 | qreal m_flexion; 28 | 29 | QRectF boundingRect() const; 30 | QPainterPath shape() const; 31 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget); 32 | 33 | void createPortsConnection(); 34 | void disconnectFromPorts(); 35 | 36 | signals: 37 | 38 | public slots: 39 | QColor color(){return m_color;} 40 | void setColor(QColor color); 41 | 42 | qreal flexion(){return m_flexion;} 43 | void setFlexion(qreal value); 44 | 45 | PortItem *portOut(){return m_portOut;} 46 | void setPortOut(PortItem *port); 47 | void removePortOut(); 48 | 49 | PortItem *portIn(){return m_portIn;} 50 | void setPortIn(PortItem *port); 51 | void removePortIn(); 52 | 53 | void setMousePoint(QPointF point); 54 | void calculateRope(); 55 | 56 | bool isConnected(){return m_isConnected;} 57 | }; 58 | 59 | #endif // CONNECTION_H 60 | -------------------------------------------------------------------------------- /test/NodeView.pro: -------------------------------------------------------------------------------- 1 | QT += core gui widgets 2 | 3 | TARGET = NodeView 4 | TEMPLATE = app 5 | 6 | SOURCES += main.cpp 7 | 8 | include(../src/NodeView.pri); -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include "nodeview.h" 2 | #include "nodeitem.h" 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QApplication a(argc, argv); 11 | NodeView view; 12 | view.resize(1000, 900); 13 | view.setRopeFlexion(50); 14 | view.show(); 15 | 16 | PortItem *portIn = Q_NULLPTR; 17 | 18 | QWidget *widget = new QWidget; 19 | widget->resize(150, 30); 20 | 21 | QLabel *label = new QLabel("Node:",widget); 22 | label->resize(60, 20); 23 | label->move(2, 5); 24 | 25 | QLineEdit *lineEdit = new QLineEdit("12345",widget); 26 | lineEdit->resize(105, 20); 27 | lineEdit->move(40, 5); 28 | 29 | NodeItem *node = view.createNode(widget); 30 | node->setTitle("Node Item"); 31 | PortItem *portOut = node->createPortOut(-8, QColor(Qt::cyan)); 32 | node->setPos(-250, 90); 33 | 34 | for (int i=0; i<5; ++i) 35 | { 36 | QWidget *widget = new QWidget; 37 | widget->resize(150, 30); 38 | 39 | QLabel *label = new QLabel("Node:",widget); 40 | label->resize(60, 20); 41 | label->move(2, 5); 42 | 43 | QLineEdit *lineEdit = new QLineEdit("12345",widget); 44 | lineEdit->resize(105, 20); 45 | lineEdit->move(40, 5); 46 | 47 | NodeItem *node = view.createNode(widget); 48 | node->setTitle("Node Item " + QString::number(i + 1)); 49 | node->setPos(i * 15, i * 60); 50 | 51 | portIn = node->createPortIn(-8, QColor(Qt::cyan)); 52 | view.createConnection(portOut, portIn); 53 | } 54 | 55 | GroupItem *group = view.createGroup(view.nodeList()); 56 | group->setTitle("Group item"); 57 | group->removeNode(node); 58 | 59 | return a.exec(); 60 | } 61 | --------------------------------------------------------------------------------