GraphVisualizerGuiPlugin.h
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 *
19 * @package ArmarX::RobotAPI
20 * @author Raphael Grimm <raphael dot grimm at kit dot edu>
21 * @date 2014
22 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
23 * GNU General Public License
24 */
25
26#pragma once
27
28// ArmarX
30
33
34#include <RobotAPI/interface/visualization/DebugDrawerInterface.h>
36
38#include <MemoryX/interface/gui/GraphVisualizerInterface.h>
39
40//qt
41#include <QDialog>
42#include <QGraphicsEllipseItem>
43#include <QGraphicsLineItem>
44#include <QGraphicsScene>
45#include <QMainWindow>
46#include <QMouseEvent>
47
48#include <MemoryX/interface/components/GraphNodePoseResolverInterface.h>
49//std
50#include <map>
51#include <string>
52#include <tuple>
53#include <vector>
54
55//boost
56
57
59#include <MemoryX/gui-plugins/GraphVisualizerPlugin/ui_GraphVisualizerGuiPlugin.h>
60
61namespace memoryx
62{
65
68
70
71 /**
72 * @class GraphVisualizerGuiPlugin
73 * @brief This plugin provides a widget used to visualize an undirected graph and draw it to a debug layer.
74 * @see GraphVisualizerWidget
75 */
77 {
78 Q_OBJECT
79 Q_INTERFACES(ArmarXGuiInterface)
80 Q_PLUGIN_METADATA(IID "ArmarXGuiInterface/1.00")
81 public:
83
84 QString
85 getPluginName() override
86 {
87 return "GraphVisualizerGuiPlugin";
88 }
89 };
90
91 /**
92 \page MemoryX-GuiPlugins-GraphVisualizer GraphVisualizer
93 \brief A widget used to visualize an undirected graph and draw it to a debug layer.
94
95 This widget implements the ice interface GraphVisualizerInterface and therefore
96 can be provided with an undirected graph.
97
98 The graph is drawn to a debug layer and a scene located in the widget.
99 The widget has tables containing information about the nodes and edges.
100 Nodes on the debug layer are visualized as coordinate systems.
101 Nodes on the scene are visualized as circles.
102 Edges on the debug layer and scene are visualized with lines.
103
104 Nodes can be added with
105 @code{.cpp}
106 addNode(const ::armarx::FramedPoseBasePtr& p)
107 @endcode
108 and are identified with the string stored in p.
109
110 Edges can be added with
111 @code{.cpp}
112 addEdge(const ::std::string& fst, const ::std::string& snd)
113 @endcode
114 and are identified with {fst,snd}.
115 fst and snd have to be the names of already existing nodes.
116 The order of fst and snd does not matter.
117
118 The existence can be checked with the following methods:
119 @code{.cpp}
120 hasNode(const ::std::string& name)
121 hasEdge(const ::std::string& fst, const ::std::string& snd)
122 @endcode
123
124 All edges (the whole graph) can be deleted with:
125 @code{.cpp}
126 clearEdges()
127 clearGraph()
128 @endcode
129
130 If the graph has to be redrawn to the debug layer, use
131 @code{.cpp}
132 redraw()
133 @endcode
134
135 Edges and nodes have four states: {selected, not selected}X{highlighted, not highlighted}
136
137 Selection affects the width of lines and the size of nodes. Selected lines are thicker and nodes have an increased size.
138 Selection can be toggled by double clicking the element in the table or the scene.
139
140 Highlighting affects the color. If not highlighted, elements are blue. If highlighted, elements are green.
141 (nodes drawn on a debug layer can't change color)
142 The highlight can be set and cleared with the functions:
143 @code{.cpp}
144 highlightNode(const ::std::string& name, bool highlighted)
145 highlightEdge(const ::std::string& fst, const ::std::string& snd, bool highlighted)
146 resetHilight()
147 @endcode
148
149
150 The graph used in the following examples can be created with the following code:
151 @code{.cpp}
152 static ::armarx::FramedPosePtr table {new ::armarx::FramedPose{Eigen::Vector3f{3400.f,7300.f,1000.f}, "table" }};
153 static ::armarx::FramedPosePtr fridge {new ::armarx::FramedPose{Eigen::Vector3f{2150.f,7750.f,1000.f}, "fridge" }};
154 static ::armarx::FramedPosePtr sink {new ::armarx::FramedPose{Eigen::Vector3f{2500.f,9700.f,1000.f}, "sink" }};
155 static ::armarx::FramedPosePtr hub2 {new ::armarx::FramedPose{Eigen::Vector3f{3750.f,5150.f,5000.f}, "hub2" }};
156 static ::armarx::FramedPosePtr hub1 {new ::armarx::FramedPose{Eigen::Vector3f{2900.f,8000.f,1000.f}, "hub1" }};
157 static ::armarx::FramedPosePtr hub3 {new ::armarx::FramedPose{Eigen::Vector3f{3400.f,2200.f,1000.f}, "hub3" }};
158 static ::armarx::FramedPosePtr hub4 {new ::armarx::FramedPose{Eigen::Vector3f{1900.f,3000.f,1000.f}, "hub4" }};
159 static ::armarx::FramedPosePtr counter{new ::armarx::FramedPose{Eigen::Vector3f{1890.f,4050.f,1000.f}, "counter"}};
160
161 //prx is a proxy passing the commands to the plugin
162 //add nodes
163 prx->addNode(table);
164 prx->addNode(fridge);
165 prx->addNode(sink);
166 prx->addNode(hub2);
167 prx->addNode(hub1);
168 prx->addNode(hub3);
169 prx->addNode(hub4);
170 prx->addNode(counter);
171
172 //add edges
173 prx->addEdge("hub1","hub2");
174 prx->addEdge("hub1","table");
175 prx->addEdge("hub1","sink");
176 prx->addEdge("hub1","fridge");
177 prx->addEdge("hub2","hub3");
178 prx->addEdge("hub3","hub4");
179 prx->addEdge("hub4","counter");
180
181 //highlight a node and an edge
182 prx->highlightEdge("hub2","hub3");
183 prx->highlightNode("table");
184 @endcode
185
186 @image html GraphVisualizerGuiPlugin_ConfigDialog.png "The config dialog for the plugin." width=300px
187 You can set the topic of the used debug drawer and the used debug layer.
188
189 @image html GraphVisualizerGuiPlugin_Simulation_800.png "The graph drawn to the debug layer." width=300px
190 @image html GraphVisualizerGuiPlugin_Widget_800.png "The plugin's ui." width=300px
191
192 The ui has 5 sections
193 -# Display options for the graph.
194 - a. Rotate the graph clockwise
195 - b. Rotate the graph counter clockwise
196 - c. Zoom factor for the graph
197 - d. Rotate and zoom the graph to display most of it. (The rotation is a multiple of pi/4)
198 -# Is the scene containing the graph
199 -# The table of nodes.
200 -# The table of edges.
201 -# Triggers a repaint for the debug layer.
202
203 - A) Shows a highlighted and selected node
204 - B) Shows a selected edge
205 - C) Shows a highlighted edge
206 - D) Shows a node. (no highlight or selection)
207 - E) Shows the tool tip of an edge.
208
209 @see GraphVisualizerGuiPlugin
210 */
211
212 /**
213 * @brief The GraphVisualizerWidget class
214 */
216 public armarx::ArmarXComponentWidgetControllerTemplate<GraphVisualizerWidget>,
217 public GraphVisualizerInterface
218 {
219 Q_OBJECT
221
222 public:
223 /**
224 * @brief The type of node ids. (This type implies the node exists)
225 */
226 using NodeId = const std::string;
227
228 /**
229 * @brief The type of edge ids. (This type implies the edge exists)
230 */
231 using EdgeId = const std::pair<const std::string, const std::string>;
232
233
235 ~GraphVisualizerWidget() override;
236
237 // inherited from Component
238 void onInitComponent() override;
239 void onConnectComponent() override;
240 void onExitComponent() override;
241
242 // inherited of ArmarXWidget
243 static QString
245 {
246 return "MemoryX.GraphVisualizerGUI";
247 }
248
249 static QIcon
251 {
252 return QIcon{"://icons/graph_visu.svg"};
253 }
254
255 QPointer<QDialog> getConfigDialog(QWidget* parent = 0) override;
256 void loadSettings(QSettings* settings) override;
257 void saveSettings(QSettings* settings) override;
258
259 //because the above load/save functions are *obviously* for "Save/Load Gui Config." :/
260 virtual void loadAutomaticSettings();
261 virtual void saveAutomaticSettings();
262
263 void configured() override;
264
265 // slice interface implementation
266 bool
267 hasEdge(const std::string& node1,
268 const std::string& node2,
269 const Ice::Current& = Ice::emptyCurrent) override
270 {
271 return (edges.find(toEdge(node1, node2)) != edges.end());
272 }
273
274 bool
275 hasNode(const std::string& id, const Ice::Current& = Ice::emptyCurrent) override
276 {
277 return (nodes.find(id) != nodes.end());
278 }
279
280 public slots:
281 // slice interface implementation
282 void addEdge(const std::string& node1Id,
283 const std::string& node2Id,
284 const Ice::Current& = Ice::emptyCurrent) override;
285 void addNode(const GraphNodeBasePtr& node,
286 const Ice::Current& = Ice::emptyCurrent) override;
287
288 void highlightEdge(const std::string& node1Id,
289 const std::string& node2Id,
290 bool highlighted = true,
291 const Ice::Current& = Ice::emptyCurrent) override;
292 void highlightNode(const std::string& nodeId,
293 bool highlighted = true,
294 const Ice::Current& = Ice::emptyCurrent) override;
295
296 void clearEdges(const Ice::Current& = Ice::emptyCurrent) override;
297 void clearGraph(const Ice::Current& = Ice::emptyCurrent) override;
298
299 void resetHighlight(const Ice::Current& = Ice::emptyCurrent) override;
300
301 void redraw(const Ice::Current& = Ice::emptyCurrent) override;
302 void refreshGraph();
303
304 void tableWidgetNodesCustomContextMenu(QPoint pos);
305 void tableWidgetEdgesCustomContextMenu(QPoint pos);
306
307 protected:
308 /**
309 * @brief Contains the ui.
310 */
311 Ui::GraphVisualizerGuiPlugin ui;
312
313 private slots:
314 /**
315 * @brief add kitchen graph (H2T Armar3a robot kitchen)
316 */
317 void addKitchenGraph();
318
319 void selectedSceneChanged(int i);
320 void updateSceneList();
321
322 void drawScene();
323
324 /**
325 * @brief Toggles the double clicked node's selection state.
326 * @param row Identifies the node.
327 */
328 void nodeTableDoubleClicked(int row, int);
329 /**
330 * @brief Toggles the double clicked edge's selection state.
331 * @param row Identifies the edge.
332 */
333 void edgeTableDoubleClicked(int row, int);
334
335 /**
336 * @brief Toggles the double clicked node's selection state.
337 * @param id Identifies the node.
338 */
339 void nodeDoubleClicked(NodeId id);
340
341 /**
342 * @brief Toggles the double clicked edge's selection state.
343 * @param id Identifies the edge.
344 */
345 void edgeDoubleClicked(EdgeId id);
346
347 /**
348 * @brief Rotates the view clockwise.
349 *
350 * Stepsize set by VIEW_ROTATE_STEP_SIZE_CC in GraphVisualizerGuiPlugin.cpp
351 */
352 void viewRotatedClock();
353
354 /**
355 * @brief Rotates the view counter clockwise.
356 *
357 * Stepsize set by VIEW_ROTATE_STEP_SIZE_CC in GraphVisualizerGuiPlugin.cpp
358 */
359 void viewRotatedCounterClock();
360
361 /**
362 * @brief Applies the current transforamtion to the view.
363 */
364 void transformView();
365
366 /**
367 * @brief Adjusts the view's zoom and rotation to display most of the graph.
368 */
369 void adjustView();
370
371 bool addNewEdge(const std::string& from, const std::string& to);
372 void addNewEdgeBoth();
373 void addNewEdgeStartEnd();
374 void addNewEdgeEndStart();
375
376 void addNewGraphNode();
377 void editGraphNode();
378
379 private:
380 /**
381 * @brief The NodeData struct holds data required for the node.
382 * The name is stored in the key used in the map nodes.
383 */
384 struct NodeData
385 {
386 /**
387 * @brief The Entity of the graph segment this struct represents
388 */
389 GraphNodeBasePtr node;
390
391 /**
392 * @brief The pose drawn to debugDrawer.
393 */
395
396 /**
397 * @brief The ellipse in the scene.
398 */
399 QGraphicsEllipseItem* graphicsItem;
400
401 /**
402 * @brief The row in the table tableWidgetNodes.
403 */
404 int tableWidgetNodesIndex;
405
406 /**
407 * @brief Whether the node is highlighted.
408 */
409 bool highlighted;
410 };
411
412 /**
413 * @brief The EdgeData struct holds data required for the edge.
414 * The name is stored in the key used in the map edges.
415 */
416 struct EdgeData
417 {
418 /**
419 * @brief The line in the scene.
420 */
421 QGraphicsLineItem* graphicsItem;
422 /**
423 * @brief The row in the table tableWidgetEdges.
424 */
425 int tableWidgetEdgesIndex;
426
427 /**
428 * @brief Whether the edge is highlighted.
429 */
430 bool highlighted;
431 };
432
433 /**
434 * @brief Returns the EdgeId corresponding to two nodes.
435 * @param node1 First node id.
436 * @param node2 Second node id.
437 * @return The EdgeId corresponding to two nodes.
438 */
439 static EdgeId
440 toEdge(const std::string& node1, const std::string& node2)
441 {
442 return EdgeId{node1, node2};
443 }
444
445 /**
446 * @brief Updates an edge.
447 * @param The edge to update.
448 */
449 void updateEdge(const EdgeId& id);
450
451 /**
452 * @brief Updates a node.
453 * @param The node to update.
454 */
455 void updateNode(const NodeId& id);
456
457 void setEditFields(const NodeData& node);
458
459 /**
460 * @brief The topic name used by debugDrawer.
461 */
462 std::string debugDrawerTopicName;
463
464 /**
465 * @brief Used to draw onto debug layers.
466 */
468
469 /**
470 * @brief The config dialog.
471 */
472 QPointer<GraphVisualizerConfigDialog> dialog;
473
474 float getYawAngle(const armarx::PoseBasePtr& pose) const;
475
476 /**
477 * @brief The scene displayed in the widget.
478 *
479 * For y coordinates -pos->y is used to mirror the scene on the y axis.
480 * If pos->y would be used the graph displayed in the scene would not
481 * match the graph drawn to the debug layer.
482 */
483 QPointer<QGraphicsScene> scene;
484
485 /**
486 * @brief The nodes.
487 */
488 std::map<std::string, NodeData> nodes;
489
490 /**
491 * @brief The edges.
492 */
493 std::map<EdgeId, EdgeData> edges;
494
495 /**
496 * @brief The view's rotation angle.
497 */
498 qreal viewAngle;
499
500 /**
501 * @brief The layer to draw on.
502 */
503 std::string debugDrawerLayerName;
504
505 bool editStartNodeNext;
506
507 std::string priorKnowledgeProxyName;
508 memoryx::PriorKnowledgeInterfacePrx priorKnowledgePrx;
509 memoryx::GraphNodePoseResolverInterfacePrx gnpr;
510 memoryx::GraphMemorySegmentBasePrx graphSeg;
511 QSettings settings;
512 QString lastSelectedSceneName;
513
514 /**
515 * selectScene(): private function called when button load is pushed, and calls the function loadScene()
516 */
517 void selectScene();
518
519 /**
520 * @brief loadScene Private function that parses XML file to load a scene
521 * @param xmlFile
522 */
523 void loadScene(const std::string& xmlFile);
524
525
528 };
529
530 /**
531 * @brief Boost shared pointer to a GraphVisualizerWidget.
532 */
533 using GraphVisualizerGuiPluginPtr = std::shared_ptr<GraphVisualizerWidget>;
534
535 /**
536 * @brief Required to override the double click event. This is required to toggle the select state.
537 */
538 class GraphVisualizerGraphicsEllipseItem : public QGraphicsEllipseItem
539 {
540 public:
542
544 NodeId name,
545 qreal x,
546 qreal y,
547 qreal width,
548 qreal height,
549 QGraphicsItem* parent = nullptr) :
550 QGraphicsEllipseItem{x, y, width, height, parent},
551 id{name},
552 parentVisuWidget(visuWidget)
553 {
554 }
555
559
560 protected:
561 void
562 mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override
563 {
564 parentVisuWidget.nodeDoubleClicked(id);
565 }
566
567 private:
568 /**
569 * @brief Required to identify the element.
570 */
571 const NodeId id;
572
573 /**
574 * @brief Required to call nodeDoubleClicked on it. (This class is no QObject so it does not support signals)
575 */
576 GraphVisualizerWidget& parentVisuWidget;
577 };
578
579 /**
580 * @brief Required to override the double click event. This is required to toggle the select state.
581 */
582 class GraphVisualizerGraphicsLineItem : public QGraphicsLineItem
583 {
584 public:
586
588 EdgeId name,
589 qreal x1,
590 qreal y1,
591 qreal x2,
592 qreal y2,
593 QGraphicsItem* parent = 0) :
594 QGraphicsLineItem{x1, y1, x2, y2, parent}, id{name}, parentVisuWidget(visuWidget)
595 {
596 }
597
599 {
600 }
601
602 signals:
603 protected:
604 void
605 mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) override
606 {
607 parentVisuWidget.edgeDoubleClicked(id);
608 }
609
610 private:
611 /**
612 * @brief Required to identify the element.
613 */
614 const EdgeId id;
615
616 /**
617 * @brief Required to call edgeDoubleClicked on it. (This class is no QObject so it does not support signals)
618 */
619 GraphVisualizerWidget& parentVisuWidget;
620 };
621
622 class MouseEventProcessor : public QObject
623 {
624 Q_OBJECT
625 public:
629
630 protected:
632 bool eventFilter(QObject* obj, QEvent* event) override;
633 };
634} // namespace memoryx
The main gui interface.
Required to override the double click event.
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override
GraphVisualizerGraphicsEllipseItem(GraphVisualizerWidget &visuWidget, NodeId name, qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent=nullptr)
Required to override the double click event.
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override
GraphVisualizerGraphicsLineItem(GraphVisualizerWidget &visuWidget, EdgeId name, qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent=0)
The GraphVisualizerWidget class.
void onInitComponent() override
Pure virtual hook for the subclass.
void clearGraph(const Ice::Current &=Ice::emptyCurrent) override
void redraw(const Ice::Current &=Ice::emptyCurrent) override
Ui::GraphVisualizerGuiPlugin ui
Contains the ui.
void resetHighlight(const Ice::Current &=Ice::emptyCurrent) override
bool hasNode(const std::string &id, const Ice::Current &=Ice::emptyCurrent) override
const std::pair< const std::string, const std::string > EdgeId
The type of edge ids.
void loadSettings(QSettings *settings) override
Implement to load the settings that are part of the GUI configuration.
void highlightEdge(const std::string &node1Id, const std::string &node2Id, bool highlighted=true, const Ice::Current &=Ice::emptyCurrent) override
void saveSettings(QSettings *settings) override
Implement to save the settings as part of the GUI configuration.
void clearEdges(const Ice::Current &=Ice::emptyCurrent) override
QPointer< QDialog > getConfigDialog(QWidget *parent=0) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
void onConnectComponent() override
Pure virtual hook for the subclass.
void configured() override
This function must be implemented by the user, if he supplies a config dialog.
void addEdge(const std::string &node1Id, const std::string &node2Id, const Ice::Current &=Ice::emptyCurrent) override
bool hasEdge(const std::string &node1, const std::string &node2, const Ice::Current &=Ice::emptyCurrent) override
void onExitComponent() override
Hook for subclass.
const std::string NodeId
The type of node ids.
void addNode(const GraphNodeBasePtr &node, const Ice::Current &=Ice::emptyCurrent) override
void highlightNode(const std::string &nodeId, bool highlighted=true, const Ice::Current &=Ice::emptyCurrent) override
MouseEventProcessor(GraphVisualizerWidget *gvw)
bool eventFilter(QObject *obj, QEvent *event) override
This file offers overloads of toIce() and fromIce() functions for STL container types.
::IceInternal::ProxyHandle<::IceProxy::armarx::DebugDrawerInterface > DebugDrawerInterfacePrx
IceInternal::Handle< FramedPose > FramedPosePtr
Definition FramedPose.h:272
VirtualRobot headers.
std::shared_ptr< GraphVisualizerWidget > GraphVisualizerGuiPluginPtr
Boost shared pointer to a GraphVisualizerWidget.