diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidget.ui b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidget.ui index a25894c76c31254fb42c7dc9611d2fa5d404d4f3..63dbb94234cc38e42417b1892bd1b76ef87ef616 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidget.ui +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidget.ui @@ -58,31 +58,6 @@ <property name="orientation"> <enum>Qt::Vertical</enum> </property> - <widget class="QFrame" name="graphFrame"> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <layout class="QVBoxLayout" name="graphSceneLayout"/> - </item> - </layout> - </widget> <widget class="QWidget" name="tables" native="true"> <layout class="QVBoxLayout" name="verticalLayout"> <property name="leftMargin"> @@ -186,6 +161,32 @@ </item> </layout> </widget> + + <widget class="QFrame" name="graphFrame"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QVBoxLayout" name="graphSceneLayout"/> + </item> + </layout> + </widget> </widget> </item> <item> diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp index 59de014fbcfe3a4dafe8069aff80ae67732d9735..649b663181e84c073934943cf75f38b0d58098cb 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp @@ -147,20 +147,8 @@ namespace armarx::nav::locgrapheditor connect(view.edgeTable, &EdgeTableWidget::itemSelectionChanged, this, &This::updateEdgeHighlighting); - -#if 0 - // Tables - connect(view.vertexTable, &QTableWidget::cellDoubleClicked, this, &This::vertexTableDoubleClicked); - connect(view.vertexTable, &QTableWidget::customContextMenuRequested, this, &This::tableWidgetVerticesCustomContextMenu); - connect(view.edgeTable, &QTableWidget::cellDoubleClicked, this, &This::edgeTableDoubleClicked); - connect(view.edgeTable, &QTableWidget::customContextMenuRequested, this, &This::tableWidgetEdgesCustomContextMenu); - - connect(widget.btnAdd, &QPushButton::pressed, this, &This::addNewGraphVertex); - connect(widget.btnAddEdge, &QPushButton::pressed, this, &This::addNewEdgeBoth); - connect(widget.btnAddEdgeStartEnd, &QPushButton::pressed, this, &This::addNewEdgeStartEnd); - connect(widget.btnAddEdgeEndStart, &QPushButton::pressed, this, &This::addNewEdgeEndStart); - connect(widget.btnEdit, &QPushButton::pressed, this, &This::editGraphVertex); -#endif + connect(view.vertexTable, &VertexTableWidget::newEdgesRequested, + this, &This::addEdges); } @@ -478,6 +466,19 @@ namespace armarx::nav::locgrapheditor } + void LocationGraphEditorWidgetController::addEdges( + QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> vertexItems) + { + std::stringstream ss; + ss << "Adding edges ..."; + for (const auto& [source, target] : vertexItems) + { + ss << "\n- " << source->text() << " to " << target->text(); + } + ARMARX_IMPORTANT << ss.str(); + } + + void LocationGraphEditorWidgetController::resetHighlighting() { for (auto vertex : model.graph.vertices()) @@ -548,213 +549,4 @@ namespace armarx::nav::locgrapheditor view.vertexData->setVertex(vertex); } - -#if 0 - bool LocationGraphEditorWidgetController::addNewEdge(const std::string& fromId, const std::string& toId) - { - std::string errorMsg; - - if (!graphSeg->hasEntityById(fromId)) - { - errorMsg = "start vertex with Id '" + fromId + "' not found in segment"; - } - else if (!graphSeg->hasEntityById(toId)) - { - errorMsg = "end vertex with Id '" + toId + "' not found in segment"; - - } - else if (fromId == toId) - { - errorMsg = "starting and ending vertex are the same"; - } - else - { - auto fromVertex = graphSeg->getVertexById(fromId); - for (const auto& adjacent : fromVertex->getAdjacentVertices()) - { - if (toId == adjacent->getId()) - { - errorMsg = "edge '" + fromVertex->getName() + "' -> '" + adjacent->getName() + "' already exists"; - break; - } - } - } - - if (errorMsg.empty()) - { - widget.labelAddEdgeStatus->setText(QString::fromStdString("Ok")); - graphSeg->addEdge(fromId, toId); - graphVertexPoseResolver->forceRefetch(fromId); - graphVertexPoseResolver->forceRefetch(toId); - addEdge(fromId, toId); - updateVertex(fromId); - updateVertex(toId); - widget.labelAddEdgeStatus->setStyleSheet("QLabel { background-color : lime; }"); - } - else - { - ARMARX_WARNING << errorMsg; - widget.labelAddEdgeStatus->setText(QString::fromStdString(errorMsg)); - widget.labelAddEdgeStatus->setStyleSheet("QLabel { background-color : orange; }"); - } - - return errorMsg.empty(); - } - - - void LocationGraphEditorWidgetController::addNewEdgeBoth() - { - std::string startId = widget.editStartVertexId->text().toStdString(); - std::string endId = widget.editEndVertexId->text().toStdString(); - addNewEdge(startId, endId); - addNewEdge(endId, startId); - } - - - void LocationGraphEditorWidgetController::addNewEdgeStartEnd() - { - std::string startId = widget.editStartVertexId->text().toStdString(); - std::string endId = widget.editEndVertexId->text().toStdString(); - addNewEdge(startId, endId); - } - - - void LocationGraphEditorWidgetController::addNewEdgeEndStart() - { - std::string startId = widget.editEndVertexId->text().toStdString(); - std::string endId = widget.editStartVertexId->text().toStdString(); - addNewEdge(startId, endId); - } - - - - void LocationGraphEditorWidgetController::addKitchenGraph() - { - std::string scene {"GraphKitchen"}; - graphSeg->clearScene(scene); - - // if you insist on hardcoding scenes, use these convenience functions for improved readability: - auto addVertex = [&](const std::string & name, float x, float y, float angle) - { - graphSeg->addVertex(new ::memoryx::GraphVertex {x, y, angle, name, scene}); - ARMARX_INFO_S << "added vertex '" << name << "' at (" << x << ", " << y << ", " << angle << "rad)"; - }; - - auto addEdges = [&](const std::string & vertexFromName, const std::string & vertexToName, bool bidirectional) - { - auto vertexFrom = graphSeg->getVertexFromSceneByName(scene, vertexFromName); - auto vertexTo = graphSeg->getVertexFromSceneByName(scene, vertexToName); - ARMARX_CHECK(vertexFrom); - ARMARX_CHECK(vertexTo); - ARMARX_INFO_S << "'" << vertexFrom->getName() << "' -> '" << vertexTo->getName() << "', status: " << graphSeg->addEdge(vertexFrom->getId(), vertexTo->getId()); - if (bidirectional) - { - ARMARX_INFO_S << "'" << vertexTo->getName() << "' -> '" << vertexFrom->getName() << "', status: " << graphSeg->addEdge(vertexTo->getId(), vertexFrom->getId()); - } - }; - - // ex - addVertex("initialvertex", 2900, 7000, 0); - addVertex("sideboard", 3400, 7000, 0); - addEdges("initialvertex", "sideboard", true); - } - - - void LocationGraphEditorWidgetController::addNewGraphVertex() - { - Eigen::Matrix4f mat; - Eigen::Vector3f rpy; - Eigen::Vector3f pos; - rpy << VirtualRobot::MathTools::deg2rad(widget.spinBoxRoll->value()), - VirtualRobot::MathTools::deg2rad(widget.spinBoxPitch->value()), - VirtualRobot::MathTools::deg2rad(widget.spinBoxYaw->value()); - pos << widget.spinBoxX->value(), widget.spinBoxY->value(), widget.spinBoxZ->value(); - VirtualRobot::MathTools::posrpy2eigen4f(pos, rpy, mat); - armarx::FramedPosePtr pose = new armarx::FramedPose(mat, - widget.editFrameName->text().toStdString(), - widget.editAgentName->text().toStdString()); - memoryx::GraphVertexPtr vertex = new memoryx::GraphVertex(pose, - widget.editVertexName->text().toStdString(), - widget.editSceneName->text().toStdString()); - auto entityId = graphSeg->addVertex(vertex); - graphVertexPoseResolver->forceRefetch(entityId); - vertex->setId(entityId); - if (widget.graphsComboBox->currentText().toStdString() == widget.editSceneName->text().toStdString()) - { - widget.editVertexId->setText(QString::fromStdString(entityId)); - addVertex(vertex); - } - } - - - void LocationGraphEditorWidgetController::editGraphVertex() - { - Eigen::Matrix4f mat; - Eigen::Vector3f rpy; - Eigen::Vector3f pos; - rpy << VirtualRobot::MathTools::deg2rad(widget.spinBoxRoll->value()), - VirtualRobot::MathTools::deg2rad(widget.spinBoxPitch->value()), - VirtualRobot::MathTools::deg2rad(widget.spinBoxYaw->value()); - pos << widget.spinBoxX->value(), - widget.spinBoxY->value(), - widget.spinBoxZ->value(); - VirtualRobot::MathTools::posrpy2eigen4f(pos, rpy, mat); - armarx::FramedPosePtr pose = new armarx::FramedPose(mat, - widget.editFrameName->text().toStdString(), - widget.editAgentName->text().toStdString()); - memoryx::GraphVertexPtr vertex = new memoryx::GraphVertex(pose, - widget.editVertexName->text().toStdString(), - widget.editSceneName->text().toStdString()); - auto id = widget.editVertexId->text().toStdString(); - - vertex->setId(id); - graphSeg->updateEntity(id, vertex); - graphVertexPoseResolver->forceRefetch(id); - if (widget.graphsComboBox->currentText().toStdString() == widget.editSceneName->text().toStdString()) - { - addVertex(vertex); - } - } - - - void LocationGraphEditorWidgetController::tableWidgetVerticesCustomContextMenu(QPoint pos) - { - int row = view.vertexTable->rowAt(pos.y()); - auto vertexIt = std::find_if(vertices.begin(), vertices.end(), [&](const std::pair<std::string, VertexData>& d) - { - return d.attrib().tableWidgetVerticesIndex == row; - }); - QMenu menu; - QAction* deleteAction = menu.addAction("Delete Vertex"); - - if (menu.exec(QCursor::pos()) == deleteAction) - { - ARMARX_CHECK(vertexIt != vertices.end()); - graphSeg->removeVertex(vertexIt->second.vertex->getId()); - loadGraph(); - } - } - - - void LocationGraphEditorWidgetController::tableWidgetEdgesCustomContextMenu(QPoint pos) - { - int row = view.edgeTable->rowAt(pos.y()); - auto edgeIt = std::find_if(edges.begin(), edges.end(), [&](const std::pair<EdgeId, EdgeData>& d) - { - return d.attrib().tableWidgetEdgesIndex == row; - }); - QMenu menu; - QAction* deleteAction = menu.addAction("Delete Edge"); - - if (menu.exec(QCursor::pos()) == deleteAction) - { - ARMARX_CHECK(edgeIt != edges.end()); - graphSeg->removeEdge(edgeIt->first.first, edgeIt->first.attrib()); - loadGraph(); - } - } - - -#endif - } diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h index fe1610651b5e2a24a7da2b70e69364dcf8d700b4..0fb66749e874fdb3880882d9a049e95834c8bee5 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h @@ -133,7 +133,8 @@ namespace armarx::nav::locgrapheditor private slots: - // Data + // Model + void queryGraphs(); void updateGraphList(); void loadGraph(); @@ -145,6 +146,8 @@ namespace armarx::nav::locgrapheditor void clearEdges(); void clearGraph(); + void addEdges(QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> vertexItems); + // View & Tables diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.cpp index d978ffb66a7618c6f13af5f49b195fb72c0756b2..dfe8587af6df7c5906b5e4091ef64c6e7365be9d 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.cpp +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.cpp @@ -86,6 +86,7 @@ namespace armarx::nav::locgrapheditor if (edge.highlighted) { arrow.color(colorHighlighted); + arrow.width(width * 1.5f); } return arrow; } diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp index 6c60fac3a54c34e66e07081d6ce6299a3052fc43..45d852df365d887d90c8d659883ab2760480794f 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp @@ -20,11 +20,8 @@ */ #include "EdgeTableWidget.h" - #include "utils.h" -#include <ArmarXCore/core/logging/Logging.h> - #include <QHeaderView> @@ -43,10 +40,11 @@ namespace armarx::nav::locgrapheditor setEditTriggers(QAbstractItemView::NoEditTriggers); setSortingEnabled(true); + setAlternatingRowColors(true); + QString styleSheet = this->styleSheet(); - ARMARX_IMPORTANT << VAROUT(styleSheet.toStdString()); - styleSheet = styleSheet + "\n" + "selection-background-color: orange;"; - this->setStyleSheet(styleSheet); + styleSheet = styleSheet + "\n" + "selection-background-color: #FF8000;"; + setStyleSheet(styleSheet); setContextMenuPolicy(Qt::CustomContextMenu); } diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp index c06f36de491895a6a86694b9ad07f5806dc1a376..692e18b8c1761537cba273d2a770e6f66ffdb54e 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp @@ -22,7 +22,9 @@ #include "VertexTableWidget.h" #include "utils.h" +#include <QAction> #include <QHeaderView> +#include <QMenu> namespace armarx::nav::locgrapheditor @@ -39,9 +41,15 @@ namespace armarx::nav::locgrapheditor setEditTriggers(QAbstractItemView::NoEditTriggers); setSortingEnabled(true); - setContextMenuPolicy(Qt::CustomContextMenu); - setAlternatingRowColors(true); + + QString styleSheet = this->styleSheet(); + styleSheet = styleSheet + "\n" + "selection-background-color: #FF8000;"; + setStyleSheet(styleSheet); + + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, &This::customContextMenuRequested, + this, &This::makeContextMenu); } @@ -101,4 +109,125 @@ namespace armarx::nav::locgrapheditor return utils::getSelectedItemsOfColumn(this, 0); } + + void VertexTableWidget::makeContextMenu(QPoint pos) + { + QList<QTableWidgetItem*> items = selectedVertexItems(); + + QMenu menu; + if (items.size() == 0) + { + QAction* action = menu.addAction("No locations selected"); + action->setEnabled(false); + } + + // <- https://unicode-table.com/de/2190/ + const QString arrowLeft = QStringLiteral("â†"); + // -> https://unicode-table.com/de/2192/ + const QString arrowRight = QStringLiteral("\u2192"); + // <-> https://unicode-table.com/de/21C4/ + const QString arrowBoth = QStringLiteral("⇄"); + + // Partners selected + if (items.size() == 2) + { + menu.addSection("Selected pair of locations"); + + // Generate actions for connecting these two nodes. + std::sort(items.begin(), items.end(), [](QTableWidgetItem* first, QTableWidgetItem* second) + { + return first->text() < second->text(); + }); + QTableWidgetItem* first = items[0]; + QTableWidgetItem* second = items[1]; + + connect(menu.addAction("Add edge '" + first->text() + "' " + arrowRight + " '" + second->text() + "'"), + &QAction::triggered, [this, first, second]() + { + emit newEdgesRequested({{first, second}}); + }); + connect(menu.addAction("Add edge '" + first->text() + "' " + arrowLeft + " '" + second->text() + "'"), + &QAction::triggered, [this, first, second]() + { + emit newEdgesRequested({{second, first}}); + }); + connect(menu.addAction("Add edges '" + first->text() + "' " + arrowBoth + " '" + second->text() + "'"), + &QAction::triggered, [this, first, second]() + { + emit newEdgesRequested({{first, second}, {second, first}}); + }); + } + + // Partners via context menu + if (items.size() > 0) + { + QString edges = items.size() == 1 + ? "edge" + : "edges"; + QString desc = items.size() == 1 + ? "'" + items[0]->text() + "'" + : QString::number(items.size()) + " locations"; + + if (items.size() == 1) + { + // QAction* deleteAction = menu.addAction("Delete location '" + ); + menu.addSection("Selected single location " + desc); + } + else + { + menu.addSection("Selected bulk of " + desc); + } + + using Item = QTableWidgetItem; + using ListOfEdges = QList<QPair<Item*, Item*>>; + using AppendFunc = std::function<void(ListOfEdges& edges, Item* selected, Item* action)>; + + auto addBulkActions = [this, &items](QMenu* submenu, AppendFunc appendFunc) + { + if (items.size() == rowCount()) + { + QAction* a = submenu->addAction("No other locations"); + a->setDisabled(true); + } + for (int row = 0; row < rowCount(); ++row) + { + QTableWidgetItem* action = this->item(row, 0); + if (items.count(action) == 0) // Do no generate self-edges + { + QAction* a = submenu->addAction(action->text()); + connect(a, &QAction::triggered, + [this, &items, action, appendFunc]() + { + QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> edges; + for (auto* selected : items) + { + appendFunc(edges, selected, action); + } + emit newEdgesRequested(edges); + }); + } + } + }; + + addBulkActions(menu.addMenu("Add " + edges + " " + desc + " " + arrowRight + " ..."), + [](ListOfEdges& edges, Item* selected, Item* action) + { + edges.append({selected, action}); + }); + addBulkActions(menu.addMenu("Add " + edges + " " + desc + " " + arrowLeft + " ..."), + [](ListOfEdges& edges, Item* selected, Item* action) + { + edges.append({action, selected}); + }); + addBulkActions(menu.addMenu("Add " + edges + " " + desc + " " + arrowBoth + " ..."), + [](ListOfEdges& edges, Item* selected, Item* action) + { + edges.append({selected, action}); + edges.append({action, selected}); + }); + } + + menu.exec(mapToGlobal(pos)); + } + } diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h index 7fea8530d486ba05e3d9bf328653bc5462728da9..6d9a227f364bf5c26e90b6e2b08b65ba7ba9d641 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h @@ -50,8 +50,15 @@ namespace armarx::nav::locgrapheditor QList<QTableWidgetItem*> selectedVertexItems(); + signals: + + void newEdgesRequested(QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> edges); + + public slots: + void makeContextMenu(QPoint pos); + private slots: