GCC Code Coverage Report


Directory: ./
File: src/graph-widget.cc
Date: 2024-12-13 15:45:52
Exec Total Coverage
Lines: 0 149 0.0%
Branches: 0 442 0.0%

Line Branch Exec Source
1 // BSD 2-Clause License
2
3 // Copyright (c) 2015 - 2018, hpp-plot
4 // Authors: Heidy Dallard, Joseph Mirabel
5 // All rights reserved.
6
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13
14 // * Redistributions in binary form must reproduce the above copyright
15 // notice, this list of conditions and the following disclaimer in
16 // the documentation and/or other materials provided with the
17 // distribution.
18
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 // OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 #include "hpp/plot/graph-widget.hh"
33
34 #include <qmath.h>
35
36 #include <QDebug>
37 #include <QFileDialog>
38 #include <QGraphicsSceneDragDropEvent>
39 #include <QHBoxLayout>
40 #include <QMenu>
41 #include <QPushButton>
42 #include <QScrollBar>
43 #include <QSplitter>
44 #include <QVBoxLayout>
45 #include <QWheelEvent>
46
47 #include "QGVEdge.h"
48 #include "QGVNode.h"
49 #include "QGVScene.h"
50 #include "QGVSubGraph.h"
51
52 namespace hpp {
53 namespace plot {
54 GraphView::GraphView(QWidget *parent) : QGraphicsView(parent) {
55 setTransformationAnchor(AnchorUnderMouse);
56 setDragMode(ScrollHandDrag);
57 setBackgroundBrush(QBrush(Qt::lightGray, Qt::SolidPattern));
58 }
59
60 void GraphView::wheelEvent(QWheelEvent *event) {
61 if (event->modifiers() == Qt::ControlModifier) {
62 qreal scaleFactor = qPow(2.0, event->delta() / 240.0); // How fast we zoom
63 qreal factor = transform()
64 .scale(scaleFactor, scaleFactor)
65 .mapRect(QRectF(0, 0, 1, 1))
66 .width();
67 if (0.05 < factor && factor < 10) // Zoom factor limitation
68 scale(scaleFactor, scaleFactor);
69 }
70 }
71
72 GraphWidget::GraphWidget(QString name, QWidget *parent)
73 : QWidget(parent),
74 scene_(new QGVScene(name, 0)),
75 buttonBox_(new QWidget()),
76 elmtInfo_(new QTextEdit()),
77 loggingInfo_(new QTextEdit()),
78 constraintInfo_(new QTextEdit()),
79 view_(new GraphView(0)),
80 layoutShouldBeFreed_(false) {
81 view_->setScene(scene_);
82
83 elmtInfo_->setReadOnly(true);
84 loggingInfo_->setReadOnly(true);
85 elmtInfo_->setText("No info");
86 loggingInfo_->setText("No info");
87 elmtInfo_->setWordWrapMode(QTextOption::NoWrap);
88 loggingInfo_->setWordWrapMode(QTextOption::NoWrap);
89
90 view_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
91 view_->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
92 view_->setRenderHint(QPainter::Antialiasing);
93 view_->setRenderHint(QPainter::TextAntialiasing);
94
95 QSplitter *splitter = new QSplitter();
96 splitter->setOrientation(Qt::Horizontal);
97 QWidget *infoW = new QWidget();
98 QVBoxLayout *infoL = new QVBoxLayout();
99 infoL->addWidget(elmtInfo_);
100 // infoL->addWidget(loggingInfo_);
101 infoL->addWidget(constraintInfo_);
102 infoW->setLayout(infoL);
103 splitter->addWidget(infoW);
104 splitter->addWidget(view_);
105 QVBoxLayout *vl = new QVBoxLayout();
106 vl->addWidget(buttonBox_);
107 vl->addWidget(splitter);
108 splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
109 this->setLayout(vl);
110
111 elmtInfo_->resize(elmtInfo_->minimumSize());
112 // loggingInfo_->resize(loggingInfo_->minimumSize());
113 view_->resize(splitter->width() - elmtInfo_->width(), splitter->height());
114
115 connect(scene_, SIGNAL(nodeContextMenu(QGVNode *)),
116 SLOT(nodeContextMenu(QGVNode *)));
117 connect(scene_, SIGNAL(nodeDoubleClick(QGVNode *)),
118 SLOT(nodeDoubleClick(QGVNode *)));
119 connect(scene_, SIGNAL(edgeContextMenu(QGVEdge *)),
120 SLOT(edgeContextMenu(QGVEdge *)));
121 connect(scene_, SIGNAL(edgeDoubleClick(QGVEdge *)),
122 SLOT(edgeDoubleClick(QGVEdge *)));
123
124 QHBoxLayout *hLayout = new QHBoxLayout(buttonBox_);
125 hLayout->setMargin(0);
126 algList_ = new QComboBox(this);
127 algList_->addItems(QStringList() << "dot"
128 << "neato"
129 << "fdp"
130 << "sfdp"
131 << "twopi"
132 << "circo");
133 // << "patchwork" << "osage");
134 QPushButton *saveas = new QPushButton(QIcon::fromTheme("document-save-as"),
135 "&Save DOT file...", buttonBox_);
136 QPushButton *refresh =
137 new QPushButton(QIcon::fromTheme("view-refresh"), "&Refresh", buttonBox_);
138 QPushButton *update = new QPushButton(QIcon::fromTheme("view-refresh"),
139 "&Update edges", buttonBox_);
140 buttonBox_->setLayout(hLayout);
141 buttonBox_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
142 hLayout->setAlignment(buttonBox_, Qt::AlignRight);
143 hLayout->addSpacerItem(
144 new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
145 hLayout->addWidget(algList_);
146 hLayout->addWidget(saveas);
147 hLayout->addWidget(update);
148 hLayout->addWidget(refresh);
149 connect(saveas, SIGNAL(clicked()), this, SLOT(saveDotFile()));
150 connect(refresh, SIGNAL(clicked()), this, SLOT(updateGraph()));
151 connect(update, SIGNAL(clicked()), this, SLOT(updateEdges()));
152
153 connect(scene_, SIGNAL(nodeMouseRelease(QGVNode *)), this,
154 SLOT(updateEdges()));
155 }
156
157 GraphWidget::~GraphWidget() { delete scene_; }
158
159 void GraphWidget::updateGraph() {
160 // Layout scene
161 if (layoutShouldBeFreed_) scene_->freeLayout();
162 scene_->clear();
163 QRectF rect = view_->sceneRect();
164 rect.setWidth(0);
165 rect.setHeight(0);
166 view_->setSceneRect(rect);
167 fillScene();
168 scene_->applyLayout(algList_->currentText());
169 layoutShouldBeFreed_ = true;
170
171 scene_->setNodePositionAttribute();
172 scene_->setGraphAttribute("splines", "spline");
173 scene_->setGraphAttribute("overlap", "false");
174 scene_->setNodeAttribute("pin", "true");
175
176 // scene_->render("canon", "debug.dot");
177
178 // Fit in view
179 // view_->fitInView(scene_->sceneRect(), Qt::KeepAspectRatio);
180 }
181
182 void GraphWidget::updateEdges() {
183 // Layout scene
184 if (layoutShouldBeFreed_) scene_->freeLayout();
185 scene_->applyLayout("nop2");
186 layoutShouldBeFreed_ = true;
187 }
188
189 void GraphWidget::saveDotFile() {
190 QString filename = QFileDialog::getSaveFileName(
191 this, "Save DOT file", "./graph.dot", tr("DOT files (*.dot)"));
192 if (!filename.isNull()) {
193 scene_->writeGraph(filename);
194 }
195 }
196
197 void GraphWidget::nodeContextMenu(QGVNode *node) { Q_UNUSED(node) }
198
199 void GraphWidget::nodeDoubleClick(QGVNode *node) { Q_UNUSED(node) }
200
201 void hpp::plot::GraphWidget::edgeDoubleClick(QGVEdge *edge) { Q_UNUSED(edge) }
202
203 void hpp::plot::GraphWidget::edgeContextMenu(QGVEdge *edge) { Q_UNUSED(edge) }
204
205 void GraphWidget::fillScene() {
206 // Configure scene attributes
207 scene_->setGraphAttribute("label", "DEMO");
208
209 scene_->setGraphAttribute("splines", "ortho");
210 scene_->setGraphAttribute("rankdir", "LR");
211 // scene_->setGraphAttribute("concentrate", "true"); //Error !
212 scene_->setGraphAttribute("nodesep", "0.4");
213
214 scene_->setNodeAttribute("shape", "box");
215 scene_->setNodeAttribute("style", "filled");
216 scene_->setNodeAttribute("fillcolor", "white");
217 scene_->setNodeAttribute("height", "1.2");
218 scene_->setEdgeAttribute("minlen", "3");
219 // scene_->setEdgeAttribute("dir", "both");
220
221 // Add some nodes
222 QGVNode *node1 = scene_->addNode("BOX");
223 node1->setIcon(QIcon::fromTheme("view-refresh").pixmap(24, 24).toImage());
224 QGVNode *node2 = scene_->addNode("SERVER0");
225 node2->setIcon(QImage(":/icons/Gnome-Network-Transmit-64.png"));
226 node2->setFlag(QGraphicsItem::ItemIsMovable, true);
227 node2->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
228 QGVNode *node3 = scene_->addNode("SERVER1");
229 node3->setIcon(QImage(":/icons/Gnome-Network-Transmit-64.png"));
230 QGVNode *node4 = scene_->addNode("USER");
231 node4->setIcon(QImage(":/icons/Gnome-Stock-Person-64.png"));
232 QGVNode *node5 = scene_->addNode("SWITCH");
233 node5->setIcon(QImage(":/icons/Gnome-Network-Server-64.png"));
234
235 // Add some edges
236 scene_->addEdge(node1, node2, "TTL")->setAttribute("color", "red");
237 scene_->addEdge(node1, node2, "SERIAL");
238 scene_->addEdge(node1, node3, "RAZ")->setAttribute("color", "blue");
239 scene_->addEdge(node2, node3, "SECU");
240
241 scene_->addEdge(node2, node4, "STATUS")->setAttribute("color", "red");
242
243 scene_->addEdge(node4, node3, "ACK")->setAttribute("color", "red");
244
245 scene_->addEdge(node4, node2, "TBIT");
246 scene_->addEdge(node4, node2, "ETH");
247 scene_->addEdge(node4, node2, "RS232");
248
249 scene_->addEdge(node4, node5, "ETH1");
250 scene_->addEdge(node2, node5, "ETH2");
251
252 QGVSubGraph *sgraph = scene_->addSubGraph("SUB1");
253 sgraph->setAttribute("label", "OFFICE");
254
255 QGVNode *snode1 = sgraph->addNode("PC0152");
256 QGVNode *snode2 = sgraph->addNode("PC0153");
257
258 scene_->addEdge(snode1, snode2, "RT7");
259
260 scene_->addEdge(node3, snode1, "GB8");
261 scene_->addEdge(node3, snode2, "TS9");
262
263 QGVSubGraph *ssgraph = sgraph->addSubGraph("SUB2");
264 ssgraph->setAttribute("label", "DESK");
265 scene_->addEdge(snode1, ssgraph->addNode("PC0155"), "S10");
266 }
267 } // namespace plot
268 } // namespace hpp
269