From 7e05706ccbf779f6bd6e77a69c19478bfaab84e3 Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Wed, 18 Jun 2025 09:45:14 +0000 Subject: [PATCH 1/4] Adds a "Attach Plugin" context menu Adds a ui to attach a plugin from the Qt GUI itself to an object. Signed-off-by: Arjo Chakravarty --- .../EntityContextMenuPlugin.cc | 12 +- .../EntityContextMenuPlugin.hh | 8 +- .../EntityContextMenuPlugin.qml | 4 +- src/gui/plugins/modules/EntityContextMenu.cc | 59 +++++++++- src/gui/plugins/modules/EntityContextMenu.hh | 7 ++ src/gui/plugins/modules/EntityContextMenu.qml | 103 +++++++++++++++++- 6 files changed, 181 insertions(+), 12 deletions(-) diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc index f6d0796b44..65f18edd68 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc @@ -159,9 +159,9 @@ void EntityContextMenuItem::SetEntityContextMenuHandler( } /////////////////////////////////////////////////// -void EntityContextMenuItem::OnContextMenuRequested(QString _entity) +void EntityContextMenuItem::OnContextMenuRequested(QString _entity, uint64_t _eid) { - emit openContextMenu(std::move(_entity)); + emit openContextMenu(std::move(_entity), _eid); } ///////////////////////////////////////////////// @@ -197,7 +197,13 @@ void EntityContextMenuHandler::HandleMouseContextMenu( visual = std::dynamic_pointer_cast(visual->Parent()); } - emit ContextMenuRequested(visual->Name().c_str()); + uint64_t entityId = kNullEntity; + if (std::holds_alternative(visual->UserData("gazebo-entity"))) + { + entityId = std::get(visual->UserData("gazebo-entity")); + } + + emit ContextMenuRequested(visual->Name().c_str(), entityId); } } diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh index eb849d41dd..22d49cea55 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh @@ -26,6 +26,8 @@ #include +#include + namespace gz { namespace sim @@ -70,7 +72,7 @@ namespace sim /// \brief Signal fired when context menu event is triggered /// \param[in] _entity Scoped name of entity. - signals: void ContextMenuRequested(QString _entity); + signals: void ContextMenuRequested(QString _entity, uint64_t _entityId); }; /// \brief A QQUickItem that manages the render window @@ -91,11 +93,11 @@ namespace sim /// Note that the function name needs to start with lowercase in order for /// the connection to work on the QML side /// \param[in] _entity Scoped name of entity. - signals: void openContextMenu(QString _entity); // NOLINT + signals: void openContextMenu(QString _entity, uint64_t _entiyI); // NOLINT /// \brief Qt callback when context menu request is received /// \param[in] _entity Scoped name of entity. - public slots: void OnContextMenuRequested(QString _entity); + public slots: void OnContextMenuRequested(QString _entity, uint64_t _entityId); }; } } diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml index 5f14a00a7d..53017f9b0f 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml @@ -55,9 +55,9 @@ ColumnLayout { Connections { target: renderWindowOverlay - function onOpenContextMenu(_entity) + function onOpenContextMenu(_entity, _eid) { - entityContextMenu.open(_entity, "model"); + entityContextMenu.open(_entity, "model", _eid); } } } diff --git a/src/gui/plugins/modules/EntityContextMenu.cc b/src/gui/plugins/modules/EntityContextMenu.cc index 6af4435972..4b70228040 100644 --- a/src/gui/plugins/modules/EntityContextMenu.cc +++ b/src/gui/plugins/modules/EntityContextMenu.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -89,6 +90,9 @@ namespace gz::sim /// \brief Storing last follow target for look at. public: std::string followTargetLookAt; + /// \brief Add plugin service + public: std::string addPluginService; + /// \brief Flag used to disable look at when not following target. public: bool followingTarget{false}; @@ -217,6 +221,7 @@ void EntityContextMenu::OnRemove( this->dataPtr->removeService = "/world/" + this->dataPtr->worldName + "/remove"; + } std::function cb = @@ -325,7 +330,7 @@ void EntityContextMenu::OnRequest(const QString &_request, const QString &_data) } else if (request == "view_frames") { - gz::msgs::StringMsg req; + msgs::StringMsg req; req.set_data(_data.toStdString()); this->dataPtr->node.Request(this->dataPtr->viewFramesService, req, cb); } @@ -342,6 +347,58 @@ void EntityContextMenu::OnRequest(const QString &_request, const QString &_data) } else { + gzwarn << "Unknown request [" << request << "]" << std::endl; } } + +///////////////////////////////////////////////// +void EntityContextMenu::OnAddPlugin(const QString &_name, + const QString &_filename, const QString &_innerXml, + const uint64_t _entity) { + + if (this->dataPtr->worldName.empty()) + { + auto runners = gui::App()->findChildren(); + if (runners.empty() || runners[0] == nullptr) + { + gzerr << "Internal error: no GuiRunner found." << std::endl; + return; + } + + this->dataPtr->worldName = "default"; + auto worldNameVariant = runners[0]->property("worldName"); + if (!worldNameVariant.isValid()) + { + gzwarn << "GuiRunner's worldName not set, using[" + << this->dataPtr->worldName << "]" << std::endl; + } + else + { + this->dataPtr->worldName = worldNameVariant.toString().toStdString(); + } + + this->dataPtr->removeService = + "/world/" + this->dataPtr->worldName + "/remove"; + + } + this->dataPtr->addPluginService = + "/world/" + this->dataPtr->worldName + "/entity/system/add"; + std::function cb = + [](const msgs::Boolean &/*_rep*/, const bool _result) + { + if (!_result) + gzerr << "Error creating new plugin" << std::endl; + }; + + msgs::EntityPlugin_V entity_plugin_message; + msgs::Entity* entity = entity_plugin_message.mutable_entity(); + entity->set_id(_entity); + msgs::Plugin* new_plugin = entity_plugin_message.add_plugins(); + new_plugin->set_name(_name.toStdString()); + new_plugin->set_filename(_filename.toStdString()); + new_plugin->set_innerxml(_innerXml.toStdString()); + + this->dataPtr->node.Request( + this->dataPtr->addPluginService, entity_plugin_message, cb); +} diff --git a/src/gui/plugins/modules/EntityContextMenu.hh b/src/gui/plugins/modules/EntityContextMenu.hh index 8fa07cc9d4..78485823d1 100644 --- a/src/gui/plugins/modules/EntityContextMenu.hh +++ b/src/gui/plugins/modules/EntityContextMenu.hh @@ -88,6 +88,13 @@ namespace sim public: Q_INVOKABLE void OnRequest(const QString &_request, const QString &_data); + /// \brief Callback when a new plugin window finishes configuring + /// \param[in] _request Request type + /// \param[in] _data Request data + public: Q_INVOKABLE void OnAddPlugin(const QString &_name, + const QString &_filename, const QString &_innerXml, + const uint64_t _entity); + /// \internal /// \brief Pointer to private data. private: std::unique_ptr dataPtr; diff --git a/src/gui/plugins/modules/EntityContextMenu.qml b/src/gui/plugins/modules/EntityContextMenu.qml index 9085f7df9f..36698dfb90 100644 --- a/src/gui/plugins/modules/EntityContextMenu.qml +++ b/src/gui/plugins/modules/EntityContextMenu.qml @@ -14,8 +14,12 @@ * limitations under the License. * */ -import QtQuick 2.0 -import QtQuick.Controls 2.0 +import QtCore +import QtQuick 2.9 +import QtQuick.Controls +import QtQuick.Controls.Material 2.1 +import QtQuick.Dialogs +import QtQuick.Layouts 1.3 import GzSim 1.0 as GzSim Item { @@ -80,6 +84,14 @@ Item { onEntered: thirdMenu.open() } } + MenuItem { + id: attachPlugin + text: "Attach Plugin" + onTriggered: { + menu.close() + pluginDialog.open() + } + } } Menu { id: secondMenu @@ -109,6 +121,7 @@ Item { context.OnRequest("look_at", context.entity) } } + } Menu { id: thirdMenu @@ -172,9 +185,10 @@ Item { } } - function open(_entity, _type) { + function open(_entity, _type, _eid) { context.entity = _entity context.type = _type + context.eid = _eid moveToMenu.enabled = false followMenu.enabled = false followFreeLookMenu.enabled = false @@ -233,9 +247,92 @@ Item { menu.popup() } + Dialog { + id: pluginDialog + title: "Add Plugin Dialog" + modal: true + standardButtons: Dialog.Ok | Dialog.Cancel // Adds standard buttons + parent: Overlay.overlay + //parent: customPlugin.Window.window ? customPlugin.Window.window.contentItem : customPlugin + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: parent.width * 0.3 + height: parent.height * 0.6 + + contentItem: Item { + // Your dialog content goes here + width: parent.width + height: parent.height + + Column { + anchors.centerIn: parent + spacing: 10 + width: parent.width * 0.8 // Make inputs a bit narrower than parent + height: parent.height + + Text { + id: pluginNameLbl + text: "Plugin Name:" + } + + TextInput { + id: pluginNames + width: parent.width + height: 20 + + } + + Text { + id: pluginFilesLbl + text: "Plugin Filename:" + } + + TextInput { + id: pluginFilename + width: parent.width + height: 20 + } + + Text { + id: pluginSdfLbl + text: "Enter the inner-xml of your plugin" + } + + TextArea { + id: pluginParams + width: parent.width + height: parent.y + parent.height - (pluginSdfLbl.y + pluginFilesLbl.height) // Set a reasonable height + + font.family: "Monospace" + background: Rectangle { + color: "transparent" // No fill + border.width: 2 // No border + border.color: "lightblue" + } + leftPadding: 5 + topPadding: 5 + rightPadding: 5 + bottomPadding: 5 + } + } + } + + onAccepted: { + console.log("Dialog Accepted!"); + context.OnAddPlugin(pluginNames.text, pluginFilename.text, pluginParams.text, context.eid) + // Perform actions when OK is clicked + } + + onRejected: { + console.log("Dialog Rejected!"); + // Perform actions when Cancel is clicked or dialog is closed + } + } + GzSim.EntityContextMenuItem { id: context property string entity property string type + property int eid } } From 1d991a021a6984e1af7c95bc184b6df7044f83e0 Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Thu, 19 Jun 2025 02:47:13 +0000 Subject: [PATCH 2/4] Style Signed-off-by: Arjo Chakravarty --- .../plugins/entity_context_menu/EntityContextMenuPlugin.cc | 3 ++- .../plugins/entity_context_menu/EntityContextMenuPlugin.hh | 3 ++- src/gui/plugins/modules/EntityContextMenu.cc | 6 +----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc index 65f18edd68..e58696607e 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc @@ -159,7 +159,8 @@ void EntityContextMenuItem::SetEntityContextMenuHandler( } /////////////////////////////////////////////////// -void EntityContextMenuItem::OnContextMenuRequested(QString _entity, uint64_t _eid) +void EntityContextMenuItem::OnContextMenuRequested( + QString _entity, uint64_t _eid) { emit openContextMenu(std::move(_entity), _eid); } diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh index 22d49cea55..a279bf798f 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.hh @@ -97,7 +97,8 @@ namespace sim /// \brief Qt callback when context menu request is received /// \param[in] _entity Scoped name of entity. - public slots: void OnContextMenuRequested(QString _entity, uint64_t _entityId); + public slots: void OnContextMenuRequested(QString _entity, + uint64_t _entityId); }; } } diff --git a/src/gui/plugins/modules/EntityContextMenu.cc b/src/gui/plugins/modules/EntityContextMenu.cc index 4b70228040..cf5575f477 100644 --- a/src/gui/plugins/modules/EntityContextMenu.cc +++ b/src/gui/plugins/modules/EntityContextMenu.cc @@ -221,7 +221,6 @@ void EntityContextMenu::OnRemove( this->dataPtr->removeService = "/world/" + this->dataPtr->worldName + "/remove"; - } std::function cb = @@ -347,7 +346,6 @@ void EntityContextMenu::OnRequest(const QString &_request, const QString &_data) } else { - gzwarn << "Unknown request [" << request << "]" << std::endl; } } @@ -377,10 +375,8 @@ void EntityContextMenu::OnAddPlugin(const QString &_name, { this->dataPtr->worldName = worldNameVariant.toString().toStdString(); } - this->dataPtr->removeService = "/world/" + this->dataPtr->worldName + "/remove"; - } this->dataPtr->addPluginService = "/world/" + this->dataPtr->worldName + "/entity/system/add"; @@ -390,7 +386,7 @@ void EntityContextMenu::OnAddPlugin(const QString &_name, if (!_result) gzerr << "Error creating new plugin" << std::endl; }; - + msgs::EntityPlugin_V entity_plugin_message; msgs::Entity* entity = entity_plugin_message.mutable_entity(); entity->set_id(_entity); From 813fbb8a02d417cae88ed1cb262f0ce40a8ade7e Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Thu, 19 Jun 2025 03:00:15 +0000 Subject: [PATCH 3/4] Includes and other style related stuff Signed-off-by: Arjo Chakravarty --- .../plugins/entity_context_menu/EntityContextMenuPlugin.cc | 2 ++ src/gui/plugins/modules/EntityContextMenu.cc | 1 + src/gui/plugins/modules/EntityContextMenu.hh | 6 ++++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc index e58696607e..764df0c430 100644 --- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc +++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc @@ -17,8 +17,10 @@ #include "EntityContextMenuPlugin.hh" +#include #include #include +#include #include diff --git a/src/gui/plugins/modules/EntityContextMenu.cc b/src/gui/plugins/modules/EntityContextMenu.cc index cf5575f477..0d5b722113 100644 --- a/src/gui/plugins/modules/EntityContextMenu.cc +++ b/src/gui/plugins/modules/EntityContextMenu.cc @@ -24,6 +24,7 @@ #include #include +#include #include #include #include diff --git a/src/gui/plugins/modules/EntityContextMenu.hh b/src/gui/plugins/modules/EntityContextMenu.hh index 78485823d1..d3870e26c7 100644 --- a/src/gui/plugins/modules/EntityContextMenu.hh +++ b/src/gui/plugins/modules/EntityContextMenu.hh @@ -89,8 +89,10 @@ namespace sim const QString &_data); /// \brief Callback when a new plugin window finishes configuring - /// \param[in] _request Request type - /// \param[in] _data Request data + /// \param[in] _name Plugin name + /// \param[in] _filename Plugin filename + /// \param[in] _innerXml Inner XML of the plugin + /// \param[in] _entity Entity to attach plugin to public: Q_INVOKABLE void OnAddPlugin(const QString &_name, const QString &_filename, const QString &_innerXml, const uint64_t _entity); From 19c45b86b9a668a83af104c8cd5a6916e6cc23d8 Mon Sep 17 00:00:00 2001 From: Arjo Chakravarty Date: Thu, 19 Jun 2025 03:03:06 +0000 Subject: [PATCH 4/4] QML Style Signed-off-by: Arjo Chakravarty --- src/gui/plugins/modules/EntityContextMenu.qml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/gui/plugins/modules/EntityContextMenu.qml b/src/gui/plugins/modules/EntityContextMenu.qml index 36698dfb90..6695bda335 100644 --- a/src/gui/plugins/modules/EntityContextMenu.qml +++ b/src/gui/plugins/modules/EntityContextMenu.qml @@ -121,7 +121,6 @@ Item { context.OnRequest("look_at", context.entity) } } - } Menu { id: thirdMenu @@ -260,11 +259,10 @@ Item { height: parent.height * 0.6 contentItem: Item { - // Your dialog content goes here - width: parent.width - height: parent.height - - Column { + // Your dialog content goes here + width: parent.width + height: parent.height + Column { anchors.centerIn: parent spacing: 10 width: parent.width * 0.8 // Make inputs a bit narrower than parent @@ -314,7 +312,7 @@ Item { rightPadding: 5 bottomPadding: 5 } - } + } } onAccepted: {