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/hpp-manipulation-graph.hh" |
33 |
|
|
|
34 |
|
|
#include <QGVEdge.h> |
35 |
|
|
#include <QGVNode.h> |
36 |
|
|
#include <QtGui/qtextdocument.h> |
37 |
|
|
#include <assert.h> |
38 |
|
|
|
39 |
|
|
#include <QDebug> |
40 |
|
|
#include <QDir> |
41 |
|
|
#include <QInputDialog> |
42 |
|
|
#include <QLayout> |
43 |
|
|
#include <QMap> |
44 |
|
|
#include <QMenu> |
45 |
|
|
#include <QMessageBox> |
46 |
|
|
#include <QPushButton> |
47 |
|
|
#include <QTemporaryFile> |
48 |
|
|
#include <QTimer> |
49 |
|
|
#include <iostream> |
50 |
|
|
#include <limits> |
51 |
|
|
|
52 |
|
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) |
53 |
|
|
#define ESCAPE(q) Qt::escape(q) |
54 |
|
|
#else |
55 |
|
|
#define ESCAPE(q) q.toHtmlEscaped() |
56 |
|
|
#endif |
57 |
|
|
|
58 |
|
|
namespace hpp { |
59 |
|
|
namespace plot { |
60 |
|
|
namespace { |
61 |
|
✗ |
void initConfigProjStat(::hpp::ConfigProjStat& p) { |
62 |
|
✗ |
p.success = 0; |
63 |
|
✗ |
p.error = 0; |
64 |
|
✗ |
p.nbObs = 0; |
65 |
|
|
} |
66 |
|
|
} // namespace |
67 |
|
✗ |
GraphAction::GraphAction(HppManipulationGraphWidget* parent) |
68 |
|
✗ |
: QAction(parent), gw_(parent) { |
69 |
|
✗ |
connect(this, SIGNAL(triggered()), SLOT(transferSignal())); |
70 |
|
|
} |
71 |
|
|
|
72 |
|
✗ |
void GraphAction::transferSignal() { |
73 |
|
|
hpp::ID id; |
74 |
|
✗ |
if (gw_->selectionID(id)) emit activated(id); |
75 |
|
|
} |
76 |
|
|
|
77 |
|
✗ |
HppManipulationGraphWidget::HppManipulationGraphWidget( |
78 |
|
✗ |
corbaServer::manipulation::Client* hpp_, QWidget* parent) |
79 |
|
|
: GraphWidget("Manipulation graph", parent), |
80 |
|
✗ |
manip_(hpp_), |
81 |
|
✗ |
showWaypoints_(new QPushButton(QIcon::fromTheme("view-refresh"), |
82 |
|
✗ |
"&Show waypoints", buttonBox_)), |
83 |
|
✗ |
statButton_(new QPushButton(QIcon::fromTheme("view-refresh"), |
84 |
|
✗ |
"&Statistics", buttonBox_)), |
85 |
|
✗ |
updateStatsTimer_(new QTimer(this)), |
86 |
|
✗ |
currentId_(-1), |
87 |
|
✗ |
showNodeId_(-1), |
88 |
|
✗ |
showEdgeId_(-1) { |
89 |
|
✗ |
graphInfo_.id = -1; |
90 |
|
✗ |
statButton_->setCheckable(true); |
91 |
|
✗ |
showWaypoints_->setCheckable(true); |
92 |
|
✗ |
showWaypoints_->setChecked(false); |
93 |
|
✗ |
buttonBox_->layout()->addWidget(statButton_); |
94 |
|
✗ |
buttonBox_->layout()->addWidget(showWaypoints_); |
95 |
|
✗ |
updateStatsTimer_->setInterval(1000); |
96 |
|
✗ |
updateStatsTimer_->setSingleShot(false); |
97 |
|
|
|
98 |
|
✗ |
connect(updateStatsTimer_, SIGNAL(timeout()), SLOT(updateStatistics())); |
99 |
|
✗ |
connect(statButton_, SIGNAL(clicked(bool)), SLOT(startStopUpdateStats(bool))); |
100 |
|
✗ |
connect(scene_, SIGNAL(selectionChanged()), SLOT(selectionChanged())); |
101 |
|
|
} |
102 |
|
|
|
103 |
|
✗ |
HppManipulationGraphWidget::~HppManipulationGraphWidget() { |
104 |
|
✗ |
qDeleteAll(nodeContextMenuActions_); |
105 |
|
✗ |
qDeleteAll(edgeContextMenuActions_); |
106 |
|
✗ |
delete updateStatsTimer_; |
107 |
|
|
} |
108 |
|
|
|
109 |
|
✗ |
void HppManipulationGraphWidget::addNodeContextMenuAction(GraphAction* action) { |
110 |
|
✗ |
nodeContextMenuActions_.append(action); |
111 |
|
|
} |
112 |
|
|
|
113 |
|
✗ |
void HppManipulationGraphWidget::addEdgeContextMenuAction(GraphAction* action) { |
114 |
|
✗ |
edgeContextMenuActions_.append(action); |
115 |
|
|
} |
116 |
|
|
|
117 |
|
✗ |
void HppManipulationGraphWidget::client( |
118 |
|
|
corbaServer::manipulation::Client* hpp) { |
119 |
|
✗ |
manip_ = hpp; |
120 |
|
|
} |
121 |
|
|
|
122 |
|
✗ |
bool hpp::plot::HppManipulationGraphWidget::selectionID(ID& id) { |
123 |
|
✗ |
id = currentId_; |
124 |
|
✗ |
return currentId_ != -1; |
125 |
|
|
} |
126 |
|
|
|
127 |
|
✗ |
void HppManipulationGraphWidget::fillScene() { |
128 |
|
✗ |
if (manip_ == NULL) return; |
129 |
|
✗ |
hpp::GraphComp_var graph = new hpp::GraphComp; |
130 |
|
✗ |
hpp::GraphElements_var elmts = new hpp::GraphElements; |
131 |
|
|
try { |
132 |
|
✗ |
manip_->graph()->getGraph(graph.out(), elmts.out()); |
133 |
|
|
|
134 |
|
✗ |
graphName_ = graph->name; |
135 |
|
✗ |
scene_->setGraphAttribute("label", QString(graph->name)); |
136 |
|
|
|
137 |
|
✗ |
scene_->setGraphAttribute("splines", "spline"); |
138 |
|
|
// scene_->setGraphAttribute("rankdir", "LR"); |
139 |
|
✗ |
scene_->setGraphAttribute("outputorder", "edgesfirst"); |
140 |
|
✗ |
scene_->setGraphAttribute("nodesep", "0.5"); |
141 |
|
✗ |
scene_->setGraphAttribute("esep", "0.8"); |
142 |
|
✗ |
scene_->setGraphAttribute("sep", "1"); |
143 |
|
|
|
144 |
|
✗ |
scene_->setNodeAttribute("shape", "circle"); |
145 |
|
✗ |
scene_->setNodeAttribute("style", "filled"); |
146 |
|
✗ |
scene_->setNodeAttribute("fillcolor", "white"); |
147 |
|
|
// scene_->setNodeAttribute("height", "1.2"); |
148 |
|
|
// scene_->setEdgeAttribute("minlen", "3"); |
149 |
|
|
|
150 |
|
✗ |
graphInfo_.id = graph->id; |
151 |
|
✗ |
graphInfo_.constraintStr = getConstraints(graphInfo_.id); |
152 |
|
|
|
153 |
|
|
// Add the nodes |
154 |
|
✗ |
nodes_.clear(); |
155 |
|
✗ |
edges_.clear(); |
156 |
|
✗ |
bool hideW = !showWaypoints_->isChecked(); |
157 |
|
✗ |
QMap<hpp::ID, bool> nodeIsWaypoint; |
158 |
|
✗ |
QMap<hpp::ID, bool> edgeVisible; |
159 |
|
|
|
160 |
|
|
// initialize node counters. |
161 |
|
✗ |
for (std::size_t i = 0; i < elmts->nodes.length(); ++i) { |
162 |
|
✗ |
if (elmts->nodes[(CORBA::ULong)i].id > graph->id) { |
163 |
|
✗ |
::hpp::ID id = elmts->nodes[(CORBA::ULong)i].id; |
164 |
|
✗ |
nodeIsWaypoint[id] = false; |
165 |
|
|
} |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
// Find what edge are visible and count the nodes accordingly. |
169 |
|
✗ |
for (std::size_t i = 0; i < elmts->edges.length(); ++i) { |
170 |
|
✗ |
if (elmts->edges[(CORBA::ULong)i].id > graph->id) { |
171 |
|
✗ |
::hpp::ID id = elmts->edges[(CORBA::ULong)i].id; |
172 |
|
✗ |
::CORBA::Long weight = manip_->graph()->getWeight(id); |
173 |
|
|
|
174 |
|
✗ |
for (std::size_t k = 0; |
175 |
|
✗ |
k < elmts->edges[(CORBA::ULong)i].waypoints.length(); ++k) |
176 |
|
✗ |
nodeIsWaypoint[elmts->edges[(CORBA::ULong)i] |
177 |
|
✗ |
.waypoints[(CORBA::ULong)k]] = true; |
178 |
|
|
|
179 |
|
|
bool hasWaypoints = |
180 |
|
✗ |
elmts->edges[(CORBA::ULong)i].waypoints.length() > 0; |
181 |
|
|
// If show Waypoint and this is not a waypoint edge |
182 |
|
|
// or hide Waypoint and this is not a transition inside a |
183 |
|
|
// WaypointEdge |
184 |
|
✗ |
edgeVisible[id] = (!hideW && !hasWaypoints) || (hideW && weight >= 0); |
185 |
|
|
} |
186 |
|
|
} |
187 |
|
|
|
188 |
|
✗ |
for (std::size_t i = 0; i < elmts->nodes.length(); ++i) { |
189 |
|
✗ |
if (elmts->nodes[(CORBA::ULong)i].id > graph->id) { |
190 |
|
✗ |
Q_ASSERT(nodeIsWaypoint.contains(elmts->nodes[(CORBA::ULong)i].id)); |
191 |
|
|
|
192 |
|
✗ |
if (hideW && nodeIsWaypoint[elmts->nodes[(CORBA::ULong)i].id]) { |
193 |
|
✗ |
qDebug() << "Ignoring node" << elmts->nodes[(CORBA::ULong)i].name; |
194 |
|
✗ |
continue; |
195 |
|
|
} |
196 |
|
✗ |
QString nodeName(elmts->nodes[(CORBA::ULong)i].name); |
197 |
|
✗ |
nodeName.replace(" : ", "\n"); |
198 |
|
✗ |
QGVNode* n = scene_->addNode(nodeName); |
199 |
|
✗ |
if (i == 0) scene_->setRootNode(n); |
200 |
|
✗ |
NodeInfo ni; |
201 |
|
✗ |
ni.id = elmts->nodes[(CORBA::ULong)i].id; |
202 |
|
✗ |
ni.node = n; |
203 |
|
✗ |
ni.constraintStr = getConstraints(ni.id); |
204 |
|
✗ |
nodeInfos_[n] = ni; |
205 |
|
✗ |
n->setFlag(QGraphicsItem::ItemIsMovable, true); |
206 |
|
✗ |
n->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); |
207 |
|
✗ |
nodes_[elmts->nodes[(CORBA::ULong)i].id] = n; |
208 |
|
|
|
209 |
|
✗ |
if (nodeIsWaypoint[ni.id]) n->setAttribute("shape", "hexagon"); |
210 |
|
|
} |
211 |
|
|
} |
212 |
|
✗ |
for (std::size_t i = 0; i < elmts->edges.length(); ++i) { |
213 |
|
✗ |
if (elmts->edges[(CORBA::ULong)i].id > graph->id) { |
214 |
|
✗ |
Q_ASSERT(edgeVisible.contains(elmts->edges[(CORBA::ULong)i].id) && |
215 |
|
|
nodeIsWaypoint.contains(elmts->edges[(CORBA::ULong)i].start) && |
216 |
|
|
nodeIsWaypoint.contains(elmts->edges[(CORBA::ULong)i].end)); |
217 |
|
✗ |
if (!edgeVisible[elmts->edges[(CORBA::ULong)i].id]) { |
218 |
|
✗ |
qDebug() << "Ignoring edge" << elmts->edges[(CORBA::ULong)i].name; |
219 |
|
✗ |
continue; |
220 |
|
|
} |
221 |
|
✗ |
EdgeInfo ei; |
222 |
|
|
QGVEdge* e = |
223 |
|
✗ |
scene_->addEdge(nodes_[elmts->edges[(CORBA::ULong)i].start], |
224 |
|
✗ |
nodes_[elmts->edges[(CORBA::ULong)i].end], ""); |
225 |
|
✗ |
ei.name = QString::fromLocal8Bit(elmts->edges[(CORBA::ULong)i].name); |
226 |
|
✗ |
ei.id = elmts->edges[(CORBA::ULong)i].id; |
227 |
|
✗ |
CORBA::String_var cnname = manip_->graph()->getContainingNode(ei.id); |
228 |
|
✗ |
ei.containingNodeName = QString::fromLocal8Bit((char*)cnname); |
229 |
|
✗ |
ei.edge = e; |
230 |
|
✗ |
updateWeight(ei, true); |
231 |
|
|
|
232 |
|
✗ |
if (elmts->edges[(CORBA::ULong)i].waypoints.length() > 0) { |
233 |
|
|
ei.constraintStr = |
234 |
|
✗ |
tr("<p><h4>Waypoint transition</h4>" |
235 |
|
|
"This transition has %1 waypoints.<br/>" |
236 |
|
|
"To see the constraints of the transition inside,<br/>" |
237 |
|
|
"re-draw the graph after enabling \"Show waypoints\"</p>") |
238 |
|
✗ |
.arg(elmts->edges[(CORBA::ULong)i].waypoints.length()); |
239 |
|
✗ |
ei.shortStr = ""; |
240 |
|
|
} else { |
241 |
|
✗ |
ei.constraintStr = getConstraints(ei.id); |
242 |
|
|
|
243 |
|
✗ |
if (manip_->graph()->isShort(ei.id)) ei.shortStr = "<h4>Short</h4>"; |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
// If this is a transition inside a WaypointEdge |
247 |
|
✗ |
if (ei.weight < 0) { |
248 |
|
✗ |
e->setAttribute("weight", "3"); |
249 |
|
✗ |
if (elmts->edges[(CORBA::ULong)i].start >= |
250 |
|
✗ |
elmts->edges[(CORBA::ULong)i].end) |
251 |
|
✗ |
e->setAttribute("constraint", "false"); |
252 |
|
|
} |
253 |
|
|
|
254 |
|
✗ |
edgeInfos_[e] = ei; |
255 |
|
✗ |
edges_[ei.id] = e; |
256 |
|
|
} |
257 |
|
|
} |
258 |
|
✗ |
} catch (const hpp::Error& e) { |
259 |
|
✗ |
qDebug() << e.msg; |
260 |
|
|
} |
261 |
|
|
} |
262 |
|
|
|
263 |
|
✗ |
void HppManipulationGraphWidget::updateStatistics() { |
264 |
|
✗ |
if (manip_ == NULL) { |
265 |
|
✗ |
updateStatsTimer_->stop(); |
266 |
|
✗ |
statButton_->setChecked(false); |
267 |
|
✗ |
return; |
268 |
|
|
} |
269 |
|
|
try { |
270 |
|
✗ |
foreach (QGraphicsItem* elmt, scene_->items()) { |
271 |
|
✗ |
QGVNode* node = dynamic_cast<QGVNode*>(elmt); |
272 |
|
✗ |
if (node) { |
273 |
|
✗ |
NodeInfo& ni = nodeInfos_[node]; |
274 |
|
✗ |
manip_->graph()->getConfigProjectorStats(ni.id, ni.configStat, |
275 |
|
✗ |
ni.pathStat); |
276 |
|
✗ |
ni.freq = manip_->graph()->getFrequencyOfNodeInRoadmap( |
277 |
|
|
ni.id, ni.freqPerCC.out()); |
278 |
|
✗ |
float sr = (ni.configStat.nbObs > 0) ? (float)ni.configStat.success / |
279 |
|
✗ |
(float)ni.configStat.nbObs |
280 |
|
|
: 0.f / 0.f; |
281 |
|
|
QString colorcode = |
282 |
|
✗ |
(ni.configStat.nbObs > 0) |
283 |
|
✗ |
? QColor(255, (int)(sr * 255), (int)(sr * 255)).name() |
284 |
|
✗ |
: "white"; |
285 |
|
✗ |
const QString& fillcolor = node->getAttribute("fillcolor"); |
286 |
|
✗ |
if (!(fillcolor == colorcode)) { |
287 |
|
✗ |
node->setAttribute("fillcolor", colorcode); |
288 |
|
✗ |
node->updateLayout(); |
289 |
|
|
} |
290 |
|
✗ |
continue; |
291 |
|
|
} |
292 |
|
✗ |
QGVEdge* edge = dynamic_cast<QGVEdge*>(elmt); |
293 |
|
✗ |
if (edge) { |
294 |
|
✗ |
EdgeInfo& ei = edgeInfos_[edge]; |
295 |
|
✗ |
manip_->graph()->getConfigProjectorStats(ei.id, ei.configStat, |
296 |
|
✗ |
ei.pathStat); |
297 |
|
✗ |
manip_->graph()->getEdgeStat(ei.id, ei.errors.out(), ei.freqs.out()); |
298 |
|
✗ |
float sr = (ei.configStat.nbObs > 0) ? (float)ei.configStat.success / |
299 |
|
✗ |
(float)ei.configStat.nbObs |
300 |
|
|
: 0.f / 0.f; |
301 |
|
✗ |
QString colorcode = (ei.configStat.nbObs > 0) |
302 |
|
✗ |
? QColor(255 - (int)(sr * 255), 0, 0).name() |
303 |
|
✗ |
: ""; |
304 |
|
✗ |
const QString& color = edge->getAttribute("color"); |
305 |
|
✗ |
if (!(color == colorcode)) { |
306 |
|
✗ |
edge->setAttribute("color", colorcode); |
307 |
|
✗ |
edge->updateLayout(); |
308 |
|
|
} |
309 |
|
✗ |
continue; |
310 |
|
|
} |
311 |
|
|
} |
312 |
|
✗ |
scene_->update(); |
313 |
|
✗ |
selectionChanged(); |
314 |
|
✗ |
} catch (const CORBA::Exception&) { |
315 |
|
✗ |
updateStatsTimer_->stop(); |
316 |
|
✗ |
statButton_->setChecked(false); |
317 |
|
✗ |
throw; |
318 |
|
|
} |
319 |
|
|
} |
320 |
|
|
|
321 |
|
✗ |
void HppManipulationGraphWidget::showNodeOfConfiguration( |
322 |
|
|
const hpp::floatSeq& cfg) { |
323 |
|
|
static bool lastlog = false; |
324 |
|
✗ |
if (manip_ == NULL) return; |
325 |
|
✗ |
if (showNodeId_ >= 0) { |
326 |
|
|
// Do unselect |
327 |
|
✗ |
nodes_[showNodeId_]->setAttribute("fillcolor", "white"); |
328 |
|
✗ |
nodes_[showNodeId_]->updateLayout(); |
329 |
|
|
} |
330 |
|
|
try { |
331 |
|
✗ |
manip_->graph()->getNode(cfg, showNodeId_); |
332 |
|
|
// Do select |
333 |
|
✗ |
if (nodes_.contains(showNodeId_)) { |
334 |
|
✗ |
nodes_[showNodeId_]->setAttribute("fillcolor", "green"); |
335 |
|
✗ |
nodes_[showNodeId_]->updateLayout(); |
336 |
|
✗ |
scene_->update(); |
337 |
|
|
} else { |
338 |
|
✗ |
qDebug() << "Node" << showNodeId_ |
339 |
|
✗ |
<< "does not exist. Refer the graph may solve the issue."; |
340 |
|
✗ |
showNodeId_ = -1; |
341 |
|
|
} |
342 |
|
✗ |
lastlog = false; |
343 |
|
✗ |
} catch (const hpp::Error& e) { |
344 |
|
✗ |
if (!lastlog) |
345 |
|
✗ |
qDebug() << "HppManipulationGraphWidget::showNodeOfConfiguration" |
346 |
|
✗ |
<< e.msg; |
347 |
|
✗ |
lastlog = true; |
348 |
|
|
} |
349 |
|
|
} |
350 |
|
|
|
351 |
|
✗ |
void HppManipulationGraphWidget::showEdge(const hpp::ID& edgeId) { |
352 |
|
✗ |
if (showEdgeId_ >= 0) { |
353 |
|
|
// Do unselect |
354 |
|
✗ |
edges_[showEdgeId_]->setAttribute("color", ""); |
355 |
|
✗ |
edges_[showEdgeId_]->updateLayout(); |
356 |
|
|
} |
357 |
|
✗ |
showEdgeId_ = edgeId; |
358 |
|
|
// Do select |
359 |
|
✗ |
if (edges_.contains(showEdgeId_)) { |
360 |
|
✗ |
edges_[showEdgeId_]->setAttribute("color", "green"); |
361 |
|
✗ |
edges_[showEdgeId_]->updateLayout(); |
362 |
|
✗ |
scene_->update(); |
363 |
|
|
} else { |
364 |
|
✗ |
showEdgeId_ = -1; |
365 |
|
|
} |
366 |
|
|
} |
367 |
|
|
|
368 |
|
✗ |
void HppManipulationGraphWidget::nodeContextMenu(QGVNode* node) { |
369 |
|
✗ |
const NodeInfo& ni = nodeInfos_[node]; |
370 |
|
✗ |
hpp::ID id = currentId_; |
371 |
|
✗ |
currentId_ = ni.id; |
372 |
|
|
|
373 |
|
✗ |
QMenu cm("Node context menu", this); |
374 |
|
✗ |
foreach (GraphAction* action, nodeContextMenuActions_) { |
375 |
|
✗ |
cm.addAction(action); |
376 |
|
|
} |
377 |
|
✗ |
cm.exec(QCursor::pos()); |
378 |
|
|
|
379 |
|
✗ |
currentId_ = id; |
380 |
|
|
} |
381 |
|
|
|
382 |
|
✗ |
void HppManipulationGraphWidget::nodeDoubleClick(QGVNode* node) { |
383 |
|
✗ |
const NodeInfo& ni = nodeInfos_[node]; |
384 |
|
✗ |
displayNodeConstraint(ni.id); |
385 |
|
|
} |
386 |
|
|
|
387 |
|
✗ |
void HppManipulationGraphWidget::displayNodeConstraint(hpp::ID id) { |
388 |
|
✗ |
if (manip_ == NULL) return; |
389 |
|
✗ |
CORBA::String_var str; |
390 |
|
✗ |
manip_->graph()->displayNodeConstraints(id, str.out()); |
391 |
|
✗ |
QString nodeStr(str); |
392 |
|
✗ |
constraintInfo_->setText(nodeStr); |
393 |
|
|
} |
394 |
|
|
|
395 |
|
✗ |
void HppManipulationGraphWidget::displayEdgeConstraint(hpp::ID id) { |
396 |
|
✗ |
if (manip_ == NULL) return; |
397 |
|
✗ |
CORBA::String_var str; |
398 |
|
✗ |
manip_->graph()->displayEdgeConstraints(id, str.out()); |
399 |
|
✗ |
QString nodeStr(str); |
400 |
|
✗ |
constraintInfo_->setText(nodeStr); |
401 |
|
|
} |
402 |
|
|
|
403 |
|
✗ |
void HppManipulationGraphWidget::displayEdgeTargetConstraint(hpp::ID id) { |
404 |
|
✗ |
if (manip_ == NULL) return; |
405 |
|
✗ |
CORBA::String_var str; |
406 |
|
✗ |
manip_->graph()->displayEdgeTargetConstraints(id, str.out()); |
407 |
|
✗ |
QString nodeStr(str); |
408 |
|
✗ |
constraintInfo_->setText(nodeStr); |
409 |
|
|
} |
410 |
|
|
|
411 |
|
✗ |
void HppManipulationGraphWidget::edgeContextMenu(QGVEdge* edge) { |
412 |
|
✗ |
const EdgeInfo& ei = edgeInfos_[edge]; |
413 |
|
✗ |
hpp::ID id = currentId_; |
414 |
|
✗ |
currentId_ = ei.id; |
415 |
|
|
|
416 |
|
✗ |
QMenu cm("Edge context menu", this); |
417 |
|
✗ |
foreach (GraphAction* action, edgeContextMenuActions_) { |
418 |
|
✗ |
cm.addAction(action); |
419 |
|
|
} |
420 |
|
✗ |
cm.exec(QCursor::pos()); |
421 |
|
|
|
422 |
|
✗ |
currentId_ = id; |
423 |
|
|
} |
424 |
|
|
|
425 |
|
✗ |
void HppManipulationGraphWidget::edgeDoubleClick(QGVEdge* edge) { |
426 |
|
✗ |
EdgeInfo& ei = edgeInfos_[edge]; |
427 |
|
|
bool ok; |
428 |
|
✗ |
::CORBA::Long w = QInputDialog::getInt( |
429 |
|
✗ |
this, "Update edge weight", tr("Edge %1 weight").arg(ei.name), ei.weight, |
430 |
|
|
0, std::numeric_limits<int>::max(), 1, &ok); |
431 |
|
✗ |
if (ok) { |
432 |
|
✗ |
updateWeight(ei, w); |
433 |
|
✗ |
edge->updateLayout(); |
434 |
|
|
} |
435 |
|
|
} |
436 |
|
|
|
437 |
|
✗ |
void HppManipulationGraphWidget::selectionChanged() { |
438 |
|
✗ |
QList<QGraphicsItem*> items = scene_->selectedItems(); |
439 |
|
✗ |
currentId_ = -1; |
440 |
|
|
|
441 |
|
✗ |
QString type, name; |
442 |
|
|
::hpp::ID id; |
443 |
|
✗ |
QString end; |
444 |
|
✗ |
QString constraints; |
445 |
|
✗ |
QString weight; |
446 |
|
|
|
447 |
|
✗ |
if (items.size() == 0) { |
448 |
|
✗ |
if (graphInfo_.id < 0) { |
449 |
|
✗ |
elmtInfo_->setText("No info"); |
450 |
|
✗ |
return; |
451 |
|
|
} |
452 |
|
✗ |
type = "Graph"; |
453 |
|
✗ |
id = graphInfo_.id; |
454 |
|
✗ |
constraints = graphInfo_.constraintStr; |
455 |
|
✗ |
} else if (items.size() == 1) { |
456 |
|
✗ |
QGVNode* node = dynamic_cast<QGVNode*>(items.first()); |
457 |
|
✗ |
QGVEdge* edge = dynamic_cast<QGVEdge*>(items.first()); |
458 |
|
✗ |
if (node) { |
459 |
|
✗ |
type = "Node"; |
460 |
|
✗ |
name = node->label(); |
461 |
|
✗ |
const NodeInfo& ni = nodeInfos_[node]; |
462 |
|
✗ |
id = ni.id; |
463 |
|
✗ |
currentId_ = id; |
464 |
|
✗ |
constraints = ni.constraintStr; |
465 |
|
✗ |
end = QString("<p><h4>Nb node in roadmap:</h4> %1</p>").arg(ni.freq); |
466 |
|
✗ |
end.append("<p><h4>Nb node in roadmap per connected component</h4>\n"); |
467 |
|
✗ |
for (std::size_t i = 0; i < ni.freqPerCC->length(); ++i) { |
468 |
|
✗ |
end.append(QString(" %1,").arg(ni.freqPerCC.in()[(CORBA::ULong)i])); |
469 |
|
|
} |
470 |
|
✗ |
end.append("</p>"); |
471 |
|
✗ |
} else if (edge) { |
472 |
|
✗ |
type = "Edge"; |
473 |
|
✗ |
const EdgeInfo& ei = edgeInfos_[edge]; |
474 |
|
✗ |
name = ei.name; |
475 |
|
✗ |
id = ei.id; |
476 |
|
✗ |
currentId_ = id; |
477 |
|
✗ |
weight = QString("<li>Weight: %1</li>").arg(ei.weight); |
478 |
|
✗ |
end = "<p>Extension results<ul>"; |
479 |
|
✗ |
for (std::size_t i = 0; |
480 |
|
✗ |
i < std::min(ei.errors->length(), ei.freqs->length()); ++i) { |
481 |
|
✗ |
end.append(QString("<li>%1: %2</li>") |
482 |
|
✗ |
.arg(QString(ei.errors.in()[(CORBA::ULong)i])) |
483 |
|
✗ |
.arg(ei.freqs.in()[(CORBA::ULong)i])); |
484 |
|
|
} |
485 |
|
✗ |
end.append("</ul></p>"); |
486 |
|
✗ |
end.append(QString("<p><h4>Containing node</h4>\n%1</p>") |
487 |
|
✗ |
.arg(ei.containingNodeName)); |
488 |
|
✗ |
constraints = ei.constraintStr; |
489 |
|
|
} else { |
490 |
|
✗ |
return; |
491 |
|
|
} |
492 |
|
|
} |
493 |
|
✗ |
elmtInfo_->setText(QString("<h4>%1 %2</h4><ul>" |
494 |
|
|
"<li>Id: %3</li>" |
495 |
|
|
"%4" |
496 |
|
|
"</ul>%5%6") |
497 |
|
✗ |
.arg(type) |
498 |
|
✗ |
.arg(ESCAPE(name)) |
499 |
|
✗ |
.arg(id) |
500 |
|
✗ |
.arg(weight) |
501 |
|
✗ |
.arg(end) |
502 |
|
✗ |
.arg(constraints)); |
503 |
|
|
} |
504 |
|
|
|
505 |
|
✗ |
void HppManipulationGraphWidget::startStopUpdateStats(bool start) { |
506 |
|
✗ |
if (start) |
507 |
|
✗ |
updateStatsTimer_->start(); |
508 |
|
|
else |
509 |
|
✗ |
updateStatsTimer_->stop(); |
510 |
|
|
} |
511 |
|
|
|
512 |
|
✗ |
HppManipulationGraphWidget::NodeInfo::NodeInfo() |
513 |
|
✗ |
: id(-1), freq(0), freqPerCC(new ::hpp::intSeq()) { |
514 |
|
✗ |
initConfigProjStat(configStat); |
515 |
|
✗ |
initConfigProjStat(pathStat); |
516 |
|
|
} |
517 |
|
|
|
518 |
|
✗ |
HppManipulationGraphWidget::EdgeInfo::EdgeInfo() : id(-1), edge(NULL) { |
519 |
|
✗ |
initConfigProjStat(configStat); |
520 |
|
✗ |
initConfigProjStat(pathStat); |
521 |
|
✗ |
errors = new Names_t(); |
522 |
|
✗ |
freqs = new intSeq(); |
523 |
|
|
} |
524 |
|
|
|
525 |
|
✗ |
void HppManipulationGraphWidget::updateWeight(EdgeInfo& ei, bool get) { |
526 |
|
✗ |
if (manip_ == NULL) return; |
527 |
|
✗ |
if (get) ei.weight = manip_->graph()->getWeight(ei.id); |
528 |
|
✗ |
if (ei.edge == NULL) return; |
529 |
|
✗ |
if (ei.weight <= 0) { |
530 |
|
✗ |
ei.edge->setAttribute("style", "dashed"); |
531 |
|
✗ |
ei.edge->setAttribute("penwidth", "1"); |
532 |
|
|
} else { |
533 |
|
✗ |
ei.edge->setAttribute("style", "filled"); |
534 |
|
✗ |
ei.edge->setAttribute("penwidth", QString::number(1 + (ei.weight - 1 / 5))); |
535 |
|
|
} |
536 |
|
|
} |
537 |
|
|
|
538 |
|
✗ |
void HppManipulationGraphWidget::updateWeight(EdgeInfo& ei, |
539 |
|
|
const ::CORBA::Long w) { |
540 |
|
✗ |
if (manip_ == NULL) return; |
541 |
|
✗ |
manip_->graph()->setWeight(ei.id, w); |
542 |
|
✗ |
ei.weight = w; |
543 |
|
✗ |
updateWeight(ei, false); |
544 |
|
|
} |
545 |
|
|
|
546 |
|
✗ |
QString HppManipulationGraphWidget::getConstraints(hpp::ID id) { |
547 |
|
✗ |
assert(manip_ != NULL); |
548 |
|
✗ |
QString ret; |
549 |
|
✗ |
hpp::Names_t_var c = new hpp::Names_t; |
550 |
|
✗ |
manip_->graph()->getNumericalConstraints(id, c); |
551 |
|
✗ |
ret.append("<p><h4>Applied constraints</h4>"); |
552 |
|
✗ |
if (c->length() > 0) { |
553 |
|
✗ |
ret.append("<ul>"); |
554 |
|
✗ |
for (unsigned i = 0; i < c->length(); i++) { |
555 |
|
✗ |
ret.append(QString("<li>%1</li>").arg(c[i].in())); |
556 |
|
|
} |
557 |
|
✗ |
ret.append("</ul></p>"); |
558 |
|
|
} else |
559 |
|
✗ |
ret.append("No constraints applied</p>"); |
560 |
|
✗ |
return ret; |
561 |
|
|
} |
562 |
|
|
} // namespace plot |
563 |
|
|
} // namespace hpp |
564 |
|
|
|