From 1f1fef346cbf2d25a7463ebf5e38a133f9404575 Mon Sep 17 00:00:00 2001 From: APTX Date: Tue, 13 Dec 2011 04:13:11 +0100 Subject: [PATCH] Almost there. --- colorlisteditor.cpp | 28 +++++ colorlisteditor.h | 24 ++++ defines.h | 12 ++ edge.cpp | 56 +++++++++ edge.h | 71 +++++++++++ edgemodel.cpp | 115 ++++++++++++++++++ edgemodel.h | 34 ++++++ graph.cpp | 285 ++++++++++++++++++++++++++++++++++++++++++++ graph.h | 81 +++++++++++++ graph.pro | 28 ++++- mainwindow.cpp | 111 +++++++++++++++++ mainwindow.h | 16 +++ mainwindow.ui | 184 ++++++++++++++++++++++++++-- node.cpp | 64 ++++++++++ node.h | 79 ++++++++++++ nodemodel.cpp | 112 +++++++++++++++++ nodemodel.h | 31 +++++ 17 files changed, 1317 insertions(+), 14 deletions(-) create mode 100644 colorlisteditor.cpp create mode 100644 colorlisteditor.h create mode 100644 defines.h create mode 100644 edge.cpp create mode 100644 edge.h create mode 100644 edgemodel.cpp create mode 100644 edgemodel.h create mode 100644 graph.cpp create mode 100644 graph.h create mode 100644 node.cpp create mode 100644 node.h create mode 100644 nodemodel.cpp create mode 100644 nodemodel.h diff --git a/colorlisteditor.cpp b/colorlisteditor.cpp new file mode 100644 index 0000000..b9e7a45 --- /dev/null +++ b/colorlisteditor.cpp @@ -0,0 +1,28 @@ +#include "colorlisteditor.h" + +ColorListEditor::ColorListEditor(QWidget *widget) : QComboBox(widget) +{ + populateList(); +} + +QColor ColorListEditor::color() const +{ + return qVariantValue(itemData(currentIndex(), Qt::DecorationRole)); +} + +void ColorListEditor::setColor(QColor color) +{ + setCurrentIndex(findData(color, int(Qt::DecorationRole))); +} + +void ColorListEditor::populateList() +{ + QStringList colorNames = QColor::colorNames(); + + for (int i = 0; i < colorNames.size(); ++i) { + QColor color(colorNames[i]); + + insertItem(i, colorNames[i]); + setItemData(i, color, Qt::DecorationRole); + } +} diff --git a/colorlisteditor.h b/colorlisteditor.h new file mode 100644 index 0000000..e023e85 --- /dev/null +++ b/colorlisteditor.h @@ -0,0 +1,24 @@ +#ifndef COLORLISTEDITOR_H +#define COLORLISTEDITOR_H + +#include + +class QColor; + +class ColorListEditor : public QComboBox +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor USER true) + +public: + ColorListEditor(QWidget *widget = 0); + +public: + QColor color() const; + void setColor(QColor c); + +private: + void populateList(); +}; + +#endif // COLORLISTEDITOR_H diff --git a/defines.h b/defines.h new file mode 100644 index 0000000..9b9c108 --- /dev/null +++ b/defines.h @@ -0,0 +1,12 @@ +#ifndef DEFINES_H +#define DEFINES_H + +#include + +class Node; +class Edge; + +typedef QList NodeList; +typedef QList EdgeList; + +#endif // DEFINES_H diff --git a/edge.cpp b/edge.cpp new file mode 100644 index 0000000..beb35b5 --- /dev/null +++ b/edge.cpp @@ -0,0 +1,56 @@ +#include "edge.h" + +#include +#include +#include +#include "node.h" + +Edge::Edge(Node *startNode, Node *endNode) : m_startNode(startNode), m_endNode(endNode), m_weight(1) +{ + +} + +void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + painter->setPen(m_color); + painter->drawLine(m_startNode->pos(), m_endNode->pos()); +// painter->drawText(boundingRect(), Qt::AlignCenter, QString::number(m_weight)); +} + +QRectF Edge::boundingRect() const +{ + QPointF tl(qMin(m_startNode->x(), m_endNode->x()), qMin(m_startNode->y(), m_endNode->y())); + QPointF br(qMax(m_startNode->x(), m_endNode->x()), qMax(m_startNode->y(), m_endNode->y())); + return QRectF(tl, br); +} + +QPainterPath Edge::shape() const +{ + QPainterPath p; + p.moveTo(m_startNode->pos()); + p.lineTo(m_endNode->pos()); + + QPainterPathStroker ps; + ps.setWidth(6); + return ps.createStroke(p); +} + +QDataStream &operator<<(QDataStream &s, const Edge &edge) +{ + s << edge.weight(); + s << edge.color(); + return s; +} + +QDataStream &operator>>(QDataStream &s, Edge &edge) +{ + int weight; + QColor color; + s >> weight; + s >> color; + edge.setWeight(weight); + edge.setColor(color); + return s; +} diff --git a/edge.h b/edge.h new file mode 100644 index 0000000..90b8cee --- /dev/null +++ b/edge.h @@ -0,0 +1,71 @@ +#ifndef EDGE_H +#define EDGE_H + +#include + +#include "defines.h" + +class Edge : public QGraphicsObject +{ + Q_OBJECT + Q_PROPERTY(int weight READ weight WRITE setWeight) + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(Node *startNode READ startNode) + Q_PROPERTY(Node *endNode READ endNode) + +public: + enum { Type = UserType + 2}; + + Edge(Node *startNode, Node *endNode); + + int type() const + { + return Type; + } + + int weight() const + { + return m_weight; + } + + QColor color() const + { + return m_color; + } + + Node *startNode() const + { + return m_startNode; + } + + Node *endNode() const + { + return m_endNode; + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + QRectF boundingRect() const; + QPainterPath shape() const; + +public slots: + void setWeight(int weight) + { + m_weight = weight; + } + + void setColor(QColor color) + { + m_color = color; + } + +private: + int m_weight; + QColor m_color; + Node *m_startNode; + Node *m_endNode; +}; + +QDataStream &operator<<(QDataStream &s, const Edge &edge); +QDataStream &operator>>(QDataStream &s, Edge &edge); + +#endif // EDGE_H diff --git a/edgemodel.cpp b/edgemodel.cpp new file mode 100644 index 0000000..974a9de --- /dev/null +++ b/edgemodel.cpp @@ -0,0 +1,115 @@ +#include "edgemodel.h" + +#include "graph.h" +#include "node.h" +#include "edge.h" + +EdgeModel::EdgeModel(Graph *graph, QObject *parent) : + QAbstractTableModel(parent), g(graph) +{ +} + +Qt::ItemFlags EdgeModel::flags(const QModelIndex &index) const +{ + if (index.column() >= 2) + return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; + return QAbstractTableModel::flags(index); +} + +QVariant EdgeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + if (orientation == Qt::Vertical) + return section + 1; + + switch (section) + { + case 0: + return "Start"; + case 1: + return "End"; + case 2: + return "Weight"; + case 3: + return "Color"; + default: + break; + } + return QVariant(); +} + +int EdgeModel::rowCount(const QModelIndex &) const +{ + return g->m_edgeList.count(); +} + +int EdgeModel::columnCount(const QModelIndex &) const +{ + return 4; +} + +QVariant EdgeModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole && role != Qt::EditRole) + return QVariant(); + + Edge *e = g->m_edgeList[index.row()]; + switch (index.column()) + { + case 0: + return e->startNode()->label(); + case 1: + return e->endNode()->label(); + case 2: + return e->weight(); + case 3: + return e->color(); + default: + break; + } + return QVariant(); +} + +bool EdgeModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::DisplayRole && role != Qt::EditRole) + return false; + + Edge *e = g->m_edgeList[index.row()]; + bool ok; + int weight; + switch (index.column()) + { + case 0: + case 1: + return false; + case 2: + weight = value.toInt(&ok); + if (!ok) + return false; + e->setWeight(qMax(0, weight)); + e->update(); + return true; + case 3: + e->setColor(value.value()); + e->update(); + return true; + default: + break; + } + return false; +} + +bool EdgeModel::removeRows(int row, int count, const QModelIndex &) +{ + for (int i = row; i < row + count; ++i) + g->removeEdge(g->m_edgeList.at(i)); + return true; +} + +void EdgeModel::edgeRemoved(int i) +{ + beginRemoveRows(QModelIndex(), i, i + 1); + endRemoveRows(); +} diff --git a/edgemodel.h b/edgemodel.h new file mode 100644 index 0000000..f49c89d --- /dev/null +++ b/edgemodel.h @@ -0,0 +1,34 @@ +#ifndef EDGEMODEL_H +#define EDGEMODEL_H + +#include + +#include "graph.h" + +class EdgeModel : public QAbstractTableModel +{ + friend Graph; + Q_OBJECT +public: + explicit EdgeModel(Graph *graph, QObject *parent = 0); + + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + + bool removeRows(int row, int count, const QModelIndex &parent); +signals: + +public slots: + +private: + void edgeRemoved(int i); + + Graph *g; +}; +#else +class EdgeModel; +#endif // EDGEMODEL_H diff --git a/graph.cpp b/graph.cpp new file mode 100644 index 0000000..3931c6a --- /dev/null +++ b/graph.cpp @@ -0,0 +1,285 @@ +#include "graph.h" + +#include "node.h" +#include "edge.h" + +#include +#include +#include +#include + +Graph::Graph(QObject *parent) : + QGraphicsScene(parent), m_nodeModel(new NodeModel(this, this)), + m_edgeModel(new EdgeModel(this, this)), m_mode(EditMode), lastNode(1), startNode(0) +{ + +} + +NodeList Graph::nodes() const +{ + return m_nodeList; +} + +EdgeList Graph::edges() const +{ + return m_edgeList; +} + +NodeModel *Graph::nodeModel() const +{ + return m_nodeModel; +} + +EdgeModel *Graph::edgeModel() const +{ + return m_edgeModel; +} + +void Graph::complete() +{ + for (int i = 0; i < m_nodeList.count(); ++i) + { + Node *startNode = m_nodeList[i]; + EdgeList edges = startNode->outgoingEdges(); + for (int j = 0; j < m_nodeList.count(); ++j) + { + if (i == j) + continue; + Node *endNode = m_nodeList[j]; + bool found = false; + for (int e = 0; e < edges.count(); ++e) + if (edges.at(e)->endNode() == endNode) + { + found = true; + break; + } + if (!found) + addEdge(startNode, endNode); + } + } +} + +Node *Graph::addNode(const QPointF &pos, const QString &label, const QColor &color) +{ + Node *node = new Node(); + node->setPos(pos); + if (label.isEmpty()) + node->setLabel(QString::number(lastNode++)); + else + node->setLabel(label); + node->setColor(color); + node->setZValue(1); + + m_nodeModel->beginInsertRows(QModelIndex(), m_nodeModel->rowCount(), m_nodeModel->rowCount()); + m_nodeList.append(node); + addItem(node); + m_nodeModel->endInsertRows(); + return node; +} + +Edge *Graph::addEdge(Node *start, Node *end, int weight, const QColor &color) +{ + if (start == 0 || end == 0) + return 0; + Edge *edge = new Edge(start, end); + edge->setWeight(weight); + edge->setColor(color); + + m_edgeModel->beginInsertRows(QModelIndex(), m_edgeModel->rowCount(), m_edgeModel->rowCount()); + start->m_outgoingEdges.append(edge); + end->m_incomingEdges.append(edge); + m_edgeList.append(edge); + addItem(edge); + m_edgeModel->endInsertRows(); + return edge; +} + +void Graph::removeNode(Node *node) +{ + if (!m_nodeList.contains(node)) + return; + + while (node->outgoingEdges().count()) + removeEdge(node->m_outgoingEdges.last()); + while (node->incomingEdges().count()) + removeEdge(node->m_incomingEdges.last()); + + int i = m_nodeList.indexOf(node); + m_nodeModel->beginRemoveRows(QModelIndex(), i, i); + removeItem(node); + m_nodeList.removeAt(i); + m_nodeModel->endRemoveRows(); + node->deleteLater(); +} + +void Graph::removeEdge(Edge *edge) +{ + if (!m_edgeList.contains(edge)) + return; + + int i = m_edgeList.indexOf(edge); + m_edgeModel->beginRemoveRows(QModelIndex(), i, i); + + removeItem(edge); + edge->startNode()->m_outgoingEdges.removeAll(edge); + edge->endNode()->m_incomingEdges.removeAll(edge); + m_edgeList.removeAt(i); + m_edgeModel->endRemoveRows(); + edge->deleteLater(); +} + +void Graph::clear() +{ + QGraphicsScene::clear(); + m_nodeList.clear(); + m_edgeList.clear(); + m_nodeModel->reset(); + m_edgeModel->reset(); + + lastNode = 1; + startNode = 0; +} + +void Graph::save(QIODevice *dev) const +{ + QDataStream s(dev); + QMap ids; + + s << QString("Graph") << (int)0xC00L; + s << QString("Node") << (int)0x80DE; + + s << m_nodeList.count(); + for (int i = 0; i < m_nodeList.count(); ++i) + { + s << *m_nodeList[i]; + ids[m_nodeList[i]] = i; + } + + s << QString("Edge") << (int)0xED4E; + + s << m_edgeList.count(); + for (int i = 0; i < m_edgeList.count(); ++i) + s << ids[m_edgeList.at(i)->startNode()] + << ids[m_edgeList.at(i)->endNode()] + << *m_edgeList[i]; +} + +bool Graph::load(QIODevice *dev) +{ + // TODO checks + QDataStream s(dev); + QString graph, node, edge; + int id, nid, eid; + s >> graph >> id >> node >> nid; + + clear(); + + int nodeCount; + s >> nodeCount; + + m_nodeList.reserve(nodeCount); + for (int i = 0; i < nodeCount; ++i) + { + Node *node = addNode(); + s >> *node; + } + + s >> edge >> eid; + + int edgeCount; + s >> edgeCount; + m_edgeList.reserve(edgeCount); + for (int i = 0; i < edgeCount; ++i) + { + int startNodeId; + int endNodeId; + s >> startNodeId >> endNodeId; + + Edge *edge = addEdge(m_nodeList[startNodeId], m_nodeList[endNodeId]); + s >> *edge; + } + + m_nodeModel->reset(); + m_edgeModel->reset(); + return false; +} + +void Graph::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if (m_mode == EditMode) + return QGraphicsScene::mousePressEvent(event); + event->ignore(); +} + +void Graph::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if (m_mode == EditMode) + { + QGraphicsScene::mouseMoveEvent(event); + m_nodeModel->dataChanged(m_nodeModel->index(0, 2), m_nodeModel->index(m_nodeList.count() - 1, 3)); + return; + } + event->ignore(); +} + +void Graph::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (m_mode == EditMode) + return QGraphicsScene::mouseReleaseEvent(event); + if (m_mode == AddNodeMode) + { + addNode(event->scenePos()); + event->accept(); + return; + } + if (m_mode == AddEdgeMode) + { + event->accept(); + QGraphicsItem *item = itemAt(event->scenePos()); + if (item == 0) + return; + Node *node = qgraphicsitem_cast(item); + if (node) + { + if (!startNode) + { + startNode = node; + } + else + { + if (startNode != node) + { + addEdge(startNode, node); + addEdge(node, startNode); + } + startNode = 0; + } + return; + } + } + if (m_mode == RemoveNode) + { + event->accept(); + QGraphicsItem *item = itemAt(event->scenePos()); + if (item == 0) + return; + Node *node = qgraphicsitem_cast(item); + if (node) + { + removeNode(node); + return; + } + Edge *edge = qgraphicsitem_cast(item); + if (edge) + { + removeEdge(edge); + return; + } + } + event->ignore(); +} + +void Graph::labelChanged() const +{ + m_edgeModel->dataChanged(m_edgeModel->index(0, 0), m_edgeModel->index(m_edgeList.count() - 1, 1)); +} diff --git a/graph.h b/graph.h new file mode 100644 index 0000000..5e2ec64 --- /dev/null +++ b/graph.h @@ -0,0 +1,81 @@ +#ifndef GRAPH_H +#define GRAPH_H + +#include + +#include "defines.h" + +#include "nodemodel.h" +#include "edgemodel.h" + +class Graph : public QGraphicsScene +{ + friend NodeModel; + friend EdgeModel; + Q_OBJECT + Q_PROPERTY(Mode mode READ mode WRITE setMode) + +public: + enum Mode { + EditMode, + AddNodeMode, + AddEdgeMode, + RemoveNode + }; + + explicit Graph(QObject *parent = 0); + + NodeList nodes() const; + EdgeList edges() const; + NodeModel *nodeModel() const; + EdgeModel *edgeModel() const; + + Mode mode() const + { + return m_mode; + } + +signals: + +public slots: + void complete(); + + Node *addNode(const QPointF &pos = QPointF(), const QString &label = QString(), const QColor &color = QColor()); + Edge *addEdge(Node *start, Node *end, int weight = 1, const QColor &color = QColor()); + + void removeNode(Node *node); + void removeEdge(Edge *edge); + void clear(); + + void setMode(Mode mode) + { + m_mode = mode; + } + + void save(QIODevice *dev) const; + bool load(QIODevice *dev); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + +private: + void labelChanged() const; + + NodeList m_nodeList; + EdgeList m_edgeList; + + NodeModel *m_nodeModel; + EdgeModel *m_edgeModel; + + Mode m_mode; + + int lastNode; + + // For adding edges + Node *startNode; +}; +#else +class Graph; +#endif // GRAPH_H diff --git a/graph.pro b/graph.pro index 4e2cd9d..4ae7def 100644 --- a/graph.pro +++ b/graph.pro @@ -11,8 +11,32 @@ TEMPLATE = app SOURCES += main.cpp\ - mainwindow.cpp + mainwindow.cpp \ + graph.cpp \ + node.cpp \ + edge.cpp \ + nodemodel.cpp \ + edgemodel.cpp \ + colorlisteditor.cpp -HEADERS += mainwindow.h +HEADERS += mainwindow.h \ + graph.h \ + node.h \ + edge.h \ + defines.h \ + nodemodel.h \ + edgemodel.h \ + colorlisteditor.h FORMS += mainwindow.ui + + + + + + + + + + + diff --git a/mainwindow.cpp b/mainwindow.cpp index 49d64fc..9631deb 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,14 +1,125 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include +#include +#include "colorlisteditor.h" + +#include "graph.h" +#include "node.h" +#include "edge.h" + +#include + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); + + modes = new QActionGroup(this); + modes->addAction(ui->actionEditMode); + modes->addAction(ui->actionAddNodeMode); + modes->addAction(ui->actionAddEdgeMode); + modes->addAction(ui->actionRemoveMode); + ui->actionEditMode->setChecked(true); + ui->modeToolBar->addActions(modes->actions()); + + QItemEditorCreatorBase *colorListCreator = new QStandardItemEditorCreator(); +// QItemEditorFactory::defaultFactory()->registerEditor(QVariant::Color, colorListCreator); + + graph = new Graph(this); + + connect(modes, SIGNAL(triggered(QAction*)), this, SLOT(modeChanged(QAction*))); + + Node *n1, *n2; + Edge *e; + n1 = graph->addNode(); + n1->setColor(QColor(255,0,0)); + n2 = graph->addNode(); + n2->setPos(100, 100); + n2->setColor(QColor(0,255,0)); + + e = graph->addEdge(n1, n2); + e->setColor(QColor(0,0,255)); + + ui->graphView->setScene(graph); + ui->nodeView->setModel(graph->nodeModel()); + ui->edgeView->setModel(graph->edgeModel()); } MainWindow::~MainWindow() { delete ui; } + +void MainWindow::modeChanged(QAction *action) +{ + if (action == ui->actionAddNodeMode) + graph->setMode(Graph::AddNodeMode); + else if (action == ui->actionAddEdgeMode) + graph->setMode(Graph::AddEdgeMode); + else if (action == ui->actionRemoveMode) + graph->setMode(Graph::RemoveNode); + else + graph->setMode(Graph::EditMode); +} + +void MainWindow::on_actionSave_triggered() +{ + QFile f("out.txt"); + if (!f.open(QIODevice::WriteOnly)) + { + qDebug() << "Cannot open file"; + return; + } + graph->save(&f); +} + +void MainWindow::on_actionOpen_triggered() +{ + QFile f("out.txt"); + if (!f.open(QIODevice::ReadOnly)) + { + qDebug() << "Cannot open file"; + return; + } + graph->load(&f); +} + +void MainWindow::on_actionCompleteCurrentGraph_triggered() +{ + graph->complete(); +} + +void MainWindow::on_actionGenerateGraph_triggered() +{ + graph->clear(); + + static const int N = 5; + + for (int i = 0; i < N; ++i) + { + for (int j = 0; j < N; ++j) + { + Node *n = graph->addNode(QPointF(j * 100, i * 100)); + if (j != 0) + { + Node *o = graph->nodes()[graph->nodes().count() - 2]; + graph->addEdge(o, n); + graph->addEdge(n, o); + } + if (i != 0) + { + Node *o = graph->nodes()[graph->nodes().count() - N - 1]; + graph->addEdge(o, n); + graph->addEdge(n, o); + } + } + } +} + +void MainWindow::on_actionClearAll_triggered() +{ + graph->clear(); +} diff --git a/mainwindow.h b/mainwindow.h index 112fda1..a790cf8 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -7,6 +7,9 @@ namespace Ui { class MainWindow; } +class Graph; +class QActionGroup; + class MainWindow : public QMainWindow { Q_OBJECT @@ -15,8 +18,21 @@ public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); +private slots: + void modeChanged(QAction *action); + void on_actionSave_triggered(); + void on_actionOpen_triggered(); + + void on_actionCompleteCurrentGraph_triggered(); + void on_actionGenerateGraph_triggered(); + void on_actionClearAll_triggered(); + private: Ui::MainWindow *ui; + + Graph *graph; + + QActionGroup *modes; }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index 6050363..406d8b9 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -1,24 +1,184 @@ + MainWindow - - + + 0 0 - 400 - 300 + 781 + 598 - - MainWindow + + PMC - - - - + + + + 0 + + + + + QFrame::NoFrame + + + + + + + + + 0 + 0 + 781 + 21 + + + + + File + + + + + + + + + Graph + + + + + + + + + + + + + TopToolBarArea + + + false + + + + + + Nodes + + + 2 + + + + + 0 + + + + + + + + + + Edges + + + 2 + + + + + 0 + + + + + + + + + + true + + + Edit Mode + + + Edit Mode + + + + + true + + + Add Node Mode + + + Add Node Mode + + + + + true + + + Add Edge Mode + + + Add Edge Mode + + + + + true + + + Remove Mode + + + Remove Mode + + + + + Open... + + + + + Save... + + + + + Quit + + + + + Clear all + + + + + Generate Graph + + + + + Complete Current Graph + + - - + diff --git a/node.cpp b/node.cpp new file mode 100644 index 0000000..ab17af3 --- /dev/null +++ b/node.cpp @@ -0,0 +1,64 @@ +#include "node.h" + +#include + +#include "edge.h" +#include "graph.h" + +Node::Node(QGraphicsObject *parent) : QGraphicsObject(parent) +{ + setFlag(QGraphicsItem::ItemIsMovable); +} + +void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + QBrush b(m_color); + painter->setBrush(b); + painter->drawEllipse(QRectF(-size/2, -size/2, size, size)); + painter->drawText(QPointF(size/2, size), m_label); +} + +QRectF Node::boundingRect() const +{ + return QRectF(-size/2, -size/2, size + m_label.length() * 9, size * 1.7); +} + +QPainterPath Node::shape() const +{ + QPainterPath p; + p.addEllipse(QRectF(-size/2, -size/2, size, size)); + return p; +} + +void Node::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + QGraphicsObject::mouseMoveEvent(event); + scene()->update(); +} + +const qreal Node::size = 20; + +QDataStream &operator<<(QDataStream &s, const Node &node) +{ + s << node.label(); + s << node.color(); + s << node.pos(); + return s; +} + +QDataStream &operator>>(QDataStream &s, Node &node) +{ + QString label; + QColor color; + QPointF pos; + s >> label; + s >> color; + s >> pos; + node.setLabel(label); + node.setColor(color); + node.setPos(pos); + return s; +} diff --git a/node.h b/node.h new file mode 100644 index 0000000..7b1be17 --- /dev/null +++ b/node.h @@ -0,0 +1,79 @@ +#ifndef NODE_H +#define NODE_H + +#include + +#include "graph.h" + +#include + +class Node : public QGraphicsObject +{ + friend Graph; + Q_OBJECT + Q_PROPERTY(QString label READ label WRITE setLabel) + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(EdgeList outgoingEdges READ outgoingEdges) + Q_PROPERTY(EdgeList incomingEdges READ incomingEdges) + +public: + enum { Type = UserType + 1}; + + Node(QGraphicsObject *parent = 0); + + int type() const + { + return Type; + } + + QString label() const + { + return m_label; + } + + QColor color() const + { + return m_color; + } + + EdgeList outgoingEdges() const + { + return m_outgoingEdges; + } + + EdgeList incomingEdges() const + { + return m_incomingEdges; + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + QRectF boundingRect() const; + QPainterPath shape() const; + + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + +public slots: + void setLabel(QString label) + { + m_label = label; + } + + void setColor(QColor color) + { + m_color = color; + } + +private: + QString m_label; + QColor m_color; + + EdgeList m_outgoingEdges; + EdgeList m_incomingEdges; + + static const qreal size; +}; + +QDataStream &operator<<(QDataStream &s, const Node &node); +QDataStream &operator>>(QDataStream &s, Node &node); + +#endif // NODE_H diff --git a/nodemodel.cpp b/nodemodel.cpp new file mode 100644 index 0000000..b2a2596 --- /dev/null +++ b/nodemodel.cpp @@ -0,0 +1,112 @@ +#include "nodemodel.h" + +#include + +#include "node.h" + +#include + +NodeModel::NodeModel(Graph *graph, QObject *parent) : + QAbstractTableModel(parent), g(graph) +{ +} + +Qt::ItemFlags NodeModel::flags(const QModelIndex &index) const +{ + return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; +} + +QVariant NodeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + if (orientation == Qt::Vertical) + return section + 1; + + switch (section) + { + case 0: + return "Label"; + case 1: + return "Color"; + case 2: + return "X"; + case 3: + return "Y"; + default: + break; + } + return QVariant(); +} + +int NodeModel::rowCount(const QModelIndex &) const +{ + return g->m_nodeList.count(); +} + +int NodeModel::columnCount(const QModelIndex &) const +{ + return 4; +} + +QVariant NodeModel::data(const QModelIndex &index, int role) const +{ + Node *n = g->m_nodeList[index.row()]; + if (role == Qt::DecorationRole && index.column() == 1) + return n->color(); + + if (role == Qt::DisplayRole || role == Qt::EditRole) + switch (index.column()) + { + case 0: + return n->label(); + case 1: + return n->color(); + case 2: + return n->x(); + case 3: + return n->y(); + default: + break; + } + return QVariant(); +} + +bool NodeModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::DisplayRole && role != Qt::EditRole) + return false; + + Node *n = g->m_nodeList[index.row()]; + bool ok; + qreal r; + switch (index.column()) + { + case 0: + n->setLabel(value.toString()); + n->update(); + g->labelChanged(); + return true; + case 1: + n->setColor(value.value()); + n->update(); + return true; + case 2: + r = value.toReal(&ok); + if (!ok) + return false; + n->setX(r); + n->scene()->update(); + return true; + case 3: + r = value.toReal(&ok); + if (!ok) + return false; + n->setY(r); + n->scene()->update(); + return true; + default: + break; + } + return false; +} diff --git a/nodemodel.h b/nodemodel.h new file mode 100644 index 0000000..1c6f20d --- /dev/null +++ b/nodemodel.h @@ -0,0 +1,31 @@ +#ifndef NODEMODEL_H +#define NODEMODEL_H + +#include + +#include "graph.h" + +class NodeModel : public QAbstractTableModel +{ + friend Graph; + Q_OBJECT +public: + explicit NodeModel(Graph *graph, QObject *parent = 0); + + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + +signals: + +public slots: + +private: + Graph *g; +}; +#else +class NodeModel; +#endif // NODEMODEL_H -- 2.52.0