diff --git a/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.cpp b/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.cpp index a6b0afcdd5b61d39917be0b440c440472f31af79..a3850e5d2844c8bf4c84ff113b183db628ed1197 100644 --- a/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.cpp +++ b/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.cpp @@ -453,8 +453,6 @@ namespace armarx objpose::DetachObjectFromRobotNodeOutput ObjectPoseObserver::detachObjectFromRobotNode( const objpose::DetachObjectFromRobotNodeInput& input, const Ice::Current&) { - objpose::DetachObjectFromRobotNodeOutput output; - ObjectID objectID = armarx::fromIce(input.objectID); std::string providerName = input.providerName; @@ -499,11 +497,12 @@ namespace armarx } } + objpose::DetachObjectFromRobotNodeOutput output; + output.wasAttached = bool(attachment); if (attachment) { ARMARX_INFO << "Detached object " << objectID << " by provider '" << providerName << "' from robot node '" << attachment->frameName << "' of agent '" << attachment->agentName << "'."; - output.wasAttached = true; } else { @@ -542,6 +541,22 @@ namespace armarx } + objpose::AgentFramesSeq ObjectPoseObserver::getAttachableFrames(const Ice::Current&) + { + std::scoped_lock lock(dataMutex); + + objpose::AgentFramesSeq output; + std::vector<VirtualRobot::RobotPtr> agents = { robot }; + for (VirtualRobot::RobotPtr agent : agents) + { + objpose::AgentFrames& frames = output.emplace_back(); + frames.agent = agent->getName(); + frames.frames = agent->getRobotNodeNames(); + } + return output; + } + + void ObjectPoseObserver::handleProviderUpdate(const std::string& providerName) { diff --git a/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h b/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h index ad7d2e3de5016685a58e2a65f1921b59cf16d2c7..a9db3aeeb213a101179accccd0fa4ffaad4da10e 100644 --- a/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h +++ b/source/RobotAPI/components/ObjectPoseObserver/ObjectPoseObserver.h @@ -88,28 +88,30 @@ namespace armarx // ObjectPoseObserverInterface interface public: - // READING + // OBJECT POSES objpose::data::ObjectPoseSeq getObjectPoses(ICE_CURRENT_ARG) override; objpose::data::ObjectPoseSeq getObjectPosesByProvider(const std::string& providerName, ICE_CURRENT_ARG) override; - Ice::StringSeq getAvailableProviderNames(ICE_CURRENT_ARG) override; + // PROVIDER INFORMATION + + bool hasProvider(const std::string& providerName, ICE_CURRENT_ARG) override; objpose::ProviderInfo getProviderInfo(const std::string& providerName, ICE_CURRENT_ARG) override; + Ice::StringSeq getAvailableProviderNames(ICE_CURRENT_ARG) override; objpose::ProviderInfoMap getAvailableProvidersInfo(ICE_CURRENT_ARG) override; - bool hasProvider(const std::string& providerName, ICE_CURRENT_ARG) override; - // MODIFICATION + // REQUESTING objpose::observer::RequestObjectsOutput requestObjects(const objpose::observer::RequestObjectsInput& input, ICE_CURRENT_ARG) override; + // ATTACHING + objpose::AttachObjectToRobotNodeOutput attachObjectToRobotNode(const objpose::AttachObjectToRobotNodeInput& input, ICE_CURRENT_ARG) override; objpose::DetachObjectFromRobotNodeOutput detachObjectFromRobotNode(const objpose::DetachObjectFromRobotNodeInput& input, ICE_CURRENT_ARG) override; objpose::DetachAllObjectsFromRobotNodesOutput detachAllObjectsFromRobotNodes(ICE_CURRENT_ARG) override; - - //Ice::Int getUpdateCounterByProvider(const std::string& providerName, ICE_CURRENT_ARG) override; - //StringIntDictionary getAllUpdateCounters(ICE_CURRENT_ARG) override; + objpose::AgentFramesSeq getAttachableFrames(ICE_CURRENT_ARG) override; // Remote GUI diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui index b6e436bb8e4805a0b0c4ac6d40615b404447333c..e201caac70e9cce63d98578373084921c80f14e0 100644 --- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui +++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui @@ -54,7 +54,7 @@ <attribute name="title"> <string>Objects</string> </attribute> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <widget class="QTreeWidget" name="objectsTree"> <property name="alternatingRowColors"> @@ -131,6 +131,18 @@ </item> </widget> </item> + <item> + <widget class="QLabel" name="label"> + <property name="font"> + <font> + <pointsize>9</pointsize> + </font> + </property> + <property name="text"> + <string>Right click an object entry for more options.</string> + </property> + </widget> + </item> </layout> </widget> <widget class="QWidget" name="tabRequest"> diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp index 4fc35cf54815b585866146d2ce0f8f30ef784285..04fc0dfd0b5fd607406a55567d836659b7141ec1 100644 --- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp @@ -25,6 +25,7 @@ #include <string> #include <QTimer> +#include <QMenu> #include <ArmarXCore/core/time/TimeUtil.h> @@ -37,12 +38,15 @@ namespace armarx { + static const int OBJECTS_COLUMN_ATTACHMENT = 6; + ObjectPoseGuiWidgetController::ObjectPoseGuiWidgetController() { widget.setupUi(getWidget()); widget.objectsTree->clear(); widget.objectsTree->sortItems(0, Qt::SortOrder::AscendingOrder); + widget.objectsTree->setContextMenuPolicy(Qt::CustomContextMenu); widget.requestTree->clear(); widget.requestTree->sortItems(0, Qt::SortOrder::AscendingOrder); @@ -53,6 +57,9 @@ namespace armarx connect(widget.updateButton, &QPushButton::pressed, this, &This::updateTab); connect(widget.tabWidget, &QTabWidget::currentChanged, this, &This::updateTab); + connect(widget.objectsTree, &QTreeWidget::customContextMenuRequested, + this, &This::prepareObjectContextMenu); + connect(widget.requestButton, &QPushButton::pressed, this, &This::requestSelectedObjects); QTimer* timer = new QTimer(this); @@ -126,6 +133,16 @@ namespace armarx { getProxy(objectPoseObserver, objectPoseObserverName); } + + this->attachableFrames = objectPoseObserver->getAttachableFrames(); + std::sort(attachableFrames.begin(), attachableFrames.end(), [](const auto & lhs, const auto & rhs) + { + return lhs.agent < rhs.agent; + }); + for (objpose::AgentFrames& frames : attachableFrames) + { + std::sort(frames.frames.begin(), frames.frames.end()); + } } void ObjectPoseGuiWidgetController::onDisconnectComponent() @@ -220,7 +237,7 @@ namespace armarx std::stringstream ss; if (pose.attachment) { - ss << pose.attachment->agentName << "/" << pose.attachment->frameName; + ss << pose.attachment->frameName << " (" << pose.attachment->agentName << ")"; } item->setText(col++, QString::fromStdString(ss.str())); } @@ -314,6 +331,82 @@ namespace armarx ARMARX_VERBOSE << "Gui update took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms."; } + void ObjectPoseGuiWidgetController::prepareObjectContextMenu(const QPoint& pos) + { + QTreeWidget* tree = widget.objectsTree; + QTreeWidgetItem* item = tree->itemAt(pos); + + if (item == nullptr || item->parent() == nullptr) + { + // No item or top level item => no context menu. + return; + } + + QString providerName = item->parent()->text(0); + QString objectID = item->text(0); + + QMenu* attachMenu = new QMenu("Attach to robot node", tree); + for (const objpose::AgentFrames& agentFrames : attachableFrames) + { + QMenu* agentMenu = new QMenu(QString::fromStdString(agentFrames.agent), tree); + + for (const std::string& frame : agentFrames.frames) + { + QAction* attachAgentAction = new QAction(QString::fromStdString(frame), tree); + // attachAgentAction->setStatusTip(tr("Attach object rigidly to a robot node")); + connect(attachAgentAction, &QAction::triggered, + [ = ]() + { + this->attachObjectToRobotNode(providerName, objectID, agentFrames.agent, frame); + }); + agentMenu->addAction(attachAgentAction); + } + attachMenu->addMenu(agentMenu); + } + + QAction* detachAction = new QAction(tr("Detach from to robot node"), tree); + detachAction->setEnabled(!item->text(OBJECTS_COLUMN_ATTACHMENT).isEmpty()); + connect(detachAction, &QAction::triggered, [ = ]() + { + this->detachObjectFromRobotNode(providerName, objectID); + }); + + QMenu menu(tree); + menu.addMenu(attachMenu); + menu.addAction(detachAction); + + menu.exec(tree->mapToGlobal(pos)); + } + + void ObjectPoseGuiWidgetController::attachObjectToRobotNode( + QString providerName, QString objectID, + const std::string& agentName, const std::string& frameName) + { + ARMARX_VERBOSE << "Attaching " << objectID << " by '" << providerName << "' to robot node '" + << frameName << "' of agent '" << agentName << "'."; + + objpose::AttachObjectToRobotNodeInput input; + input.providerName = providerName.toStdString(); + input.objectID = armarx::toIce(armarx::ObjectID(objectID.toStdString())); + input.agentName = agentName; + input.frameName = frameName; + + objpose::AttachObjectToRobotNodeOutput output = objectPoseObserver->attachObjectToRobotNode(input); + ARMARX_VERBOSE << "Success of attaching: " << output.success; + } + + void ObjectPoseGuiWidgetController::detachObjectFromRobotNode(QString providerName, QString objectID) + { + ARMARX_VERBOSE << "Detaching " << objectID << " by '" << providerName << "' from robot node."; + + objpose::DetachObjectFromRobotNodeInput input; + input.providerName = providerName.toStdString(); + input.objectID = armarx::toIce(armarx::ObjectID(objectID.toStdString())); + + objpose::DetachObjectFromRobotNodeOutput output = objectPoseObserver->detachObjectFromRobotNode(input); + ARMARX_VERBOSE << "Was attached: " << output.wasAttached; + } + void ObjectPoseGuiWidgetController::requestSelectedObjects() { std::map<std::string, objpose::observer::RequestObjectsInput> requestsPerProvider; diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.h b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.h index 26767131953cdd7f756951fdf011597d7716426b..84a0e6e964ffdd2cead4f144627f478f41c94d10 100644 --- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.h +++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.h @@ -97,6 +97,11 @@ namespace armarx void updateObjectsTab(); void updateRequestTab(); + void prepareObjectContextMenu(const QPoint& pos); + void attachObjectToRobotNode(QString providerName, QString objectID, + const std::string& agentName, const std::string& frameName); + void detachObjectFromRobotNode(QString providerName, QString objectID); + void requestSelectedObjects(); @@ -113,6 +118,7 @@ namespace armarx std::string objectPoseObserverName; armarx::objpose::ObjectPoseObserverInterfacePrx objectPoseObserver; + objpose::AgentFramesSeq attachableFrames; }; } diff --git a/source/RobotAPI/interface/objectpose/ObjectPoseObserver.ice b/source/RobotAPI/interface/objectpose/ObjectPoseObserver.ice index 9004c2afcc900c2f0a35d1134f1d100d551ff0ad..602043ab3b17e3afbdfbbd87b835dda80f264f29 100644 --- a/source/RobotAPI/interface/objectpose/ObjectPoseObserver.ice +++ b/source/RobotAPI/interface/objectpose/ObjectPoseObserver.ice @@ -97,21 +97,33 @@ module armarx int numDetached; }; + struct AgentFrames + { + string agent; + Ice::StringSeq frames; + }; + sequence<AgentFrames> AgentFramesSeq; + interface ObjectPoseObserverInterface extends ObserverInterface, ObjectPoseTopic { - // Reading + // Object poses + data::ObjectPoseSeq getObjectPoses(); data::ObjectPoseSeq getObjectPosesByProvider(string providerName); + // Provider information + bool hasProvider(string providerName); Ice::StringSeq getAvailableProviderNames(); ProviderInfoMap getAvailableProvidersInfo(); ProviderInfo getProviderInfo(string providerName); + // Requesting - // Modifying observer::RequestObjectsOutput requestObjects(observer::RequestObjectsInput input); + // Attaching + /// Attach an object to a robot node. AttachObjectToRobotNodeOutput attachObjectToRobotNode(AttachObjectToRobotNodeInput input); /// Detach an attached object from a robot node. @@ -119,6 +131,8 @@ module armarx /// Detach all objects from robot nodes. DetachAllObjectsFromRobotNodesOutput detachAllObjectsFromRobotNodes(); + AgentFramesSeq getAttachableFrames(); + }; };