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