From: APTX Date: Tue, 13 Dec 2011 03:13:11 +0000 (+0100) Subject: Almost there. X-Git-Url: https://gitweb.aptx.org/?a=commitdiff_plain;h=1f1fef346cbf2d25a7463ebf5e38a133f9404575;p=graph.git Almost there. --- 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