SemanticRelationViewerWidgetController.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * \package VisionX::gui-plugins::SemanticRelationViewerWidgetController
17 * \author Fabian Paus ( fabian dot paus at kit dot edu )
18 * \date 2019
19 * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <cfloat>
26
27#include <VirtualRobot/VirtualRobot.h>
28
30
33
34#include <SemanticObjectRelations/SupportAnalysis/SupportGraph.h>
35
36namespace armarx
37{
38 namespace
39 {
40
42 layoutGraph(semrel::AttributedGraph const& graph)
43 {
44 GraphvizLayout layout;
45 for (auto vertex : graph.vertices())
46 {
47 auto desc = vertex.descriptor();
48 auto& attrs = vertex.attrib().json;
49 std::string name = std::to_string(desc);
50
51 if (auto style = attrs.find("style"); style != attrs.end())
52 {
53 auto label = style->find("label");
54 if (label != style->end() && label->is_string())
55 {
56 name = label->get<std::string>();
57 }
58 }
59 else if (auto object = attrs.find("object"); object != attrs.end())
60 {
61 auto nameValue = object->find("name");
62 if (nameValue != object->end() && nameValue->is_string())
63 {
64 name = nameValue->get<std::string>() +
65 std::to_string(object->at("id").get<int>());
66 }
67 nameValue = object->find("instance");
68 if (nameValue != object->end() && nameValue->is_string())
69 {
70 name = nameValue->get<std::string>();
71 }
72 }
73 layout.addNode(desc, name);
74 }
75 for (auto edge : graph.edges())
76 {
77 auto& attrs = edge.attrib().json;
78 std::string name;
79 if (auto style = attrs.find("style"); style != attrs.end())
80 {
81 auto label = style->find("label");
82 if (label != style->end() && label->is_string())
83 {
84 name = label->get<std::string>();
85 }
86 }
87 layout.addEdge(edge.sourceDescriptor(), edge.targetDescriptor(), name);
88 }
89 return layout.finish();
90 }
91
92 } // namespace
93
95 {
96 setlocale(LC_ALL, "en_US.UTF-8");
97
98 widget.setupUi(getWidget());
99 timer = new QTimer(this);
100 widget.graphView->setScene(&graphicsScene);
101 widget.graphView->setRenderHints(QPainter::Antialiasing |
102 QPainter::HighQualityAntialiasing);
103 int propWidth = 250;
104 int graphWidth = 750;
105 widget.splitter->setSizes({graphWidth, propWidth});
106
107 connect(timer, SIGNAL(timeout()), this, SLOT(onUpdateGraphs()));
108 connect(widget.updatePushButton, SIGNAL(clicked()), this, SLOT(onUpdateGraphs()));
109 connect(widget.autoUpdateCheckBox,
110 SIGNAL(stateChanged(int)),
111 this,
112 SLOT(onAutoUpdateChanged()));
113 connect(widget.idComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onUpdateGraphs()));
114
115 connect(widget.zoomSpinBox,
116 QOverload<double>::of(&QDoubleSpinBox::valueChanged),
117 this,
118 &SemanticRelationViewerWidgetController::onZoomChanged);
119 }
120
124
125 void
129
130 void
134
135 static const std::string STORAGE_NAME_KEY = "SemanticGraphStorage";
136
137 QPointer<QDialog>
139 {
140 if (!configDialog)
141 {
142 configDialog = new SimpleConfigDialog(parent);
143 configDialog->addProxyFinder<armarx::semantic::GraphStorageInterfacePrx>(
144 {STORAGE_NAME_KEY, "SemanticGraphStorage", "SemanticGraphStorage"});
145 }
146 return qobject_cast<SimpleConfigDialog*>(configDialog);
147 }
148
149 void
153
154 template <typename T>
155 void
156 setProxy(ManagedIceObject* object, T& proxy, std::string const& name)
157 {
158 object->usingProxy(name);
159 proxy = object->getProxy<T>(name);
160 }
161
162 void
164 {
165 std::string storageName = configDialog->getProxyName(STORAGE_NAME_KEY);
166 setProxy(this, storage, storageName);
167 }
168
169 void
174
175 void
177 {
178 graphMap.clear();
179 semantic::data::GraphMap allGraphs = storage->getAll();
180 for (auto& pair : allGraphs)
181 {
182 std::string const& id = pair.first;
183 graphMap[id] = semantic::fromIce(pair.second);
184 }
185
186 std::string selectedID = widget.idComboBox->currentText().toStdString();
187
188 widget.idComboBox->blockSignals(true);
189 widget.idComboBox->clear();
190 widget.idComboBox->blockSignals(false);
191
192 if (allGraphs.empty())
193 {
194 return;
195 }
196
197 widget.idComboBox->blockSignals(true);
198 for (auto& pair : allGraphs)
199 {
200 std::string const& id = pair.first;
201 widget.idComboBox->addItem(QString::fromStdString(id));
202 }
203
204 int selectedIndex = widget.idComboBox->findText(QString::fromStdString(selectedID));
205 if (selectedIndex < 0)
206 {
207 selectedIndex = 0;
208 }
209 widget.idComboBox->setCurrentIndex(selectedIndex);
210 widget.idComboBox->blockSignals(false);
211
212 selectedID = widget.idComboBox->currentText().toStdString();
213
214 if (graphMap.count(selectedID))
215 {
216 semrel::AttributedGraph const& graph = graphMap[selectedID];
217
218 GraphvizLayoutedGraph layoutedGraph = layoutGraph(graph);
219 drawGraph(&graphicsScene, layoutedGraph, graph);
220 widget.graphView->viewport()->update();
221
222 setPropertyView(nlohmann::json::object());
223 }
224 else
225 {
226 std::vector<std::string> ids;
227 for (auto& entry : graphMap)
228 {
229 ids.push_back(entry.first);
230 }
231 ARMARX_WARNING << "Could not find graph with ID: " << selectedID
232 << "\nExisting IDs: " << ids;
233 }
234 }
235
236 void
238 {
239 if (widget.autoUpdateCheckBox->checkState() == Qt::Checked)
240 {
241 timer->start(20);
242 }
243 else
244 {
245 timer->stop();
246 }
247 }
248
249 void
251 {
252 std::string id = widget.idComboBox->currentText().toStdString();
253 if (graphMap.count(id) == 0)
254 {
255 ARMARX_WARNING << "No graph with ID: " << id;
256 return;
257 }
258 auto& graph = graphMap[id];
259 auto vertex = graph.vertex(selected->descriptor);
260 ARMARX_VERBOSE << "Vertex[" << selected->descriptor
261 << "]: " << vertex.attrib().json.dump(4);
262
263 setPropertyView(vertex.attrib().json);
264 highlightSelected(selected);
265 }
266
267 void
269 {
270 std::string id = widget.idComboBox->currentText().toStdString();
271 if (graphMap.count(id) == 0)
272 {
273 return;
274 }
275 auto& graph = graphMap[id];
276
277 auto edge = graph.edge(graph.vertex(selected->sourceDescriptor),
278 graph.vertex(selected->targetDescriptor));
279 ARMARX_VERBOSE << "Edge: " << edge.attrib().json.dump(4);
280
281 edge.attrib().json.is_null();
282 setPropertyView(edge.attrib().json);
283 highlightSelected(selected);
284 }
285
286 void
288 {
289 std::string id = widget.idComboBox->currentText().toStdString();
290 if (graphMap.count(id) == 0)
291 {
292 ARMARX_WARNING << "No graph with ID: " << id;
293 return;
294 }
295
296 auto& graph = graphMap[id];
297 setPropertyView(graph.attrib().json);
298 highlightSelected(selected);
299 }
300
301 void
302 SemanticRelationViewerWidgetController::drawGraph(QGraphicsScene* graphicsScene,
303 GraphvizLayoutedGraph const& layoutedGraph,
304 semrel::AttributedGraph const& graph)
305 {
306 graphicsScene->clear();
307
308 float minX = FLT_MAX;
309 float minY = FLT_MAX;
310
311 for (auto& pair : layoutedGraph.nodes)
312 {
313 int id = pair.first;
314 GraphvizLayoutedNode const& layoutedNode = pair.second;
315
317 ellipse->setRect(
318 layoutedNode.posX, layoutedNode.posY, layoutedNode.width, layoutedNode.height);
319 ellipse->text = QString::fromStdString(layoutedNode.label);
320 ellipse->descriptor = id;
321
322 minX = std::min(layoutedNode.posX, minX);
323 minY = std::min(layoutedNode.posY, minY);
324
325 // Get optional style information
326 auto vertex = graph.vertex(semrel::ShapeID{id});
327 auto& json = vertex.attrib().json;
328 if (json.count("style"))
329 {
330 auto& style = json["style"];
331 auto setColor = [&](std::string const& colorName, QColor* output)
332 {
333 if (style.count(colorName))
334 {
335 Eigen::Vector4i scolor = style[colorName];
336 QColor qcolor(scolor(0), scolor(1), scolor(2), scolor(3));
337 *output = qcolor;
338 }
339 };
340 setColor("fill-color", &ellipse->fillColor);
341 setColor("border-color", &ellipse->borderColor);
342 setColor("font-color", &ellipse->fontColor);
343 }
344
345 bool connected =
346 QObject::connect(ellipse,
348 this,
350 if (!connected)
351 {
352 ARMARX_WARNING << "Failed to connect Qt slots";
353 }
354
355 graphicsScene->addItem(ellipse);
356 }
357
358
359 for (auto& pair : layoutedGraph.edges)
360 {
361 int sourceID = pair.first.first;
362 int targetID = pair.first.second;
363 GraphvizLayoutedEdge const& layoutedEdge = pair.second;
364
365 QPainterPath path;
366 if (layoutedEdge.startPoint)
367 {
368 path.lineTo(*layoutedEdge.startPoint);
369 }
370 path.addPath(SplinePath::simplePath(layoutedEdge.controlPoints));
371 if (layoutedEdge.endPoint)
372 {
373 path.lineTo(*layoutedEdge.endPoint);
374 }
375
376 for (int i = 0; i <= 10; ++i)
377 {
378 double t = 0.1 * i;
379 QPointF point = path.pointAtPercent(t);
380
381 minX = std::min(float(point.x()), minX);
382 minY = std::min(float(point.y()), minY);
383 }
384
385 SemanticGraphEdgeItem* item = new SemanticGraphEdgeItem();
386 item->sourceDescriptor = sourceID;
387 item->targetDescriptor = targetID;
388 item->setPath(path);
389
390 // Get optional style information
391 auto sourceVertex = graph.vertex(item->sourceDescriptor);
392 auto targetVertex = graph.vertex(item->targetDescriptor);
393 auto edge = graph.edge(sourceVertex, targetVertex);
394 auto& json = edge.attrib().json;
395 if (json.count("style"))
396 {
397 auto& style = json["style"];
398 if (style.count("color"))
399 {
400 Eigen::Vector4i scolor = style["color"];
401 QColor qcolor(scolor(0), scolor(1), scolor(2), scolor(3));
402 item->color = qcolor;
403 }
404 }
405 if (layoutedEdge.label.size() > 0)
406 {
407 item->label = QString::fromStdString(layoutedEdge.label);
408 item->labelPosition.setX(double(layoutedEdge.labelPosX));
409 item->labelPosition.setY(double(layoutedEdge.labelPosY));
410 }
411
412 bool connected =
413 QObject::connect(item,
415 this,
417 if (!connected)
418 {
419 ARMARX_WARNING << "Failed to connect Qt slots";
420 }
421
422 graphicsScene->addItem(item);
423 }
424
425 SemanticGraphGlobalItem* global = new SemanticGraphGlobalItem();
426 global->setRect(minX - 60.0f, minY - 40.0f, 100.0f, 30.0f);
427 bool connected =
428 QObject::connect(global,
430 this,
432 if (!connected)
433 {
434 ARMARX_WARNING << "Failed to connect Qt slots";
435 }
436 graphicsScene->addItem(global);
437 }
438
439 void
440 SemanticRelationViewerWidgetController::setPropertyView(nlohmann::json const& attrs)
441 {
442 propertyModel.setRoot(attrs);
443
444 widget.propertiesView->setModel(&propertyModel);
445 widget.propertiesView->expandAll();
446 widget.propertiesView->resizeColumnToContents(0);
447 }
448
449 void
450 SemanticRelationViewerWidgetController::highlightSelected(QGraphicsItem* selected)
451 {
452 for (QGraphicsItem* item : graphicsScene.items())
453 {
454 if (auto* edge = dynamic_cast<SemanticGraphEdgeItem*>(item))
455 {
456 edge->selected = (edge == selected);
457 }
458 else if (auto* vertex = dynamic_cast<SemanticGraphVertexItem*>(item))
459 {
460 vertex->selected = (vertex == selected);
461 }
462 else if (auto* global = dynamic_cast<SemanticGraphGlobalItem*>(item))
463 {
464 global->selected = (global == selected);
465 }
466 }
467 graphicsScene.update();
468 }
469
470 void
471 SemanticRelationViewerWidgetController::onZoomChanged(double newZoom)
472 {
473 double inverseScale = 1.0 / graphicsSceneScale;
474 double zoom = inverseScale * newZoom;
475 widget.graphView->scale(zoom, zoom);
476 graphicsSceneScale = newZoom;
477 }
478} // namespace armarx
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
GraphvizLayoutedGraph finish(std::string const &savePNG="")
The ManagedIceObject is the base class for all ArmarX objects.
void onLeftClick(SemanticGraphEdgeItem *selected)
void onLeftClick(SemanticGraphGlobalItem *)
void onLeftClick(SemanticGraphVertexItem *)
QPointer< QDialog > getConfigDialog(QWidget *parent) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
A config-dialog containing one (or multiple) proxy finders.
static QPainterPath simplePath(QPointList simpleControlPoints)
simplePath Constructs a cubic Beziercurve with an arbitrary number of control points.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
semrel::AttributedGraph fromIce(const semantic::data::Graph &graph)
This file offers overloads of toIce() and fromIce() functions for STL container types.
void setProxy(ManagedIceObject *object, T &proxy, std::string const &name)
std::map< std::pair< int, int >, GraphvizLayoutedEdge > edges
std::map< int, GraphvizLayoutedNode > nodes