diff --git a/include/ignition/gui/Helpers.hh b/include/ignition/gui/Helpers.hh index 1d8219022..2a22abf0d 100644 --- a/include/ignition/gui/Helpers.hh +++ b/include/ignition/gui/Helpers.hh @@ -70,6 +70,15 @@ namespace ignition std::string uniqueFilePath(const std::string &_pathAndName, const std::string &_extension); + /// \brief The main window's "worldNames" property may be filled with a list + /// of the names of all worlds currently loaded. This information can be + /// used by plugins to choose which world to work with. + /// This helper function provides a handy access to the world names list. + /// \return List of world names, as stored in the `MainWindow`'s + /// "worldNames" property. + IGNITION_GUI_VISIBLE + QStringList worldNames(); + /// \brief Returns the first element on a QList which matches the given /// property. /// \param[in] _list The list to search through. diff --git a/include/ignition/gui/qml/IgnCard.qml b/include/ignition/gui/qml/IgnCard.qml index 9c8d98ec9..f86cb347d 100644 --- a/include/ignition/gui/qml/IgnCard.qml +++ b/include/ignition/gui/qml/IgnCard.qml @@ -115,6 +115,7 @@ Pane { * Tool bar background color */ property string pluginToolBarColor: + typeof MainWindow === "undefined" || MainWindow.pluginToolBarColorLight === "" || MainWindow.pluginToolBarColorDark === "" ? Material.accent : @@ -125,6 +126,7 @@ Pane { * Tool bar text color */ property string pluginToolBarTextColor: + typeof MainWindow === "undefined" || MainWindow.pluginToolBarTextColorLight === "" || MainWindow.pluginToolBarTextColorDark === "" ? Material.background : diff --git a/src/Helpers.cc b/src/Helpers.cc index 1a5e7f1e0..4c6a51329 100644 --- a/src/Helpers.cc +++ b/src/Helpers.cc @@ -20,8 +20,10 @@ #include #include +#include "ignition/gui/Application.hh" #include "ignition/gui/Enums.hh" #include "ignition/gui/Helpers.hh" +#include "ignition/gui/MainWindow.hh" ///////////////////////////////////////////////// std::string ignition::gui::humanReadable(const std::string &_key) @@ -164,3 +166,17 @@ std::string ignition::gui::uniqueFilePath(const std::string &_pathAndName, return result; } + +///////////////////////////////////////////////// +QStringList ignition::gui::worldNames() +{ + auto win = App()->findChild(); + if (nullptr == win) + return {}; + + auto worldNamesVariant = win->property("worldNames"); + if (!worldNamesVariant.isValid()) + return {}; + + return worldNamesVariant.toStringList(); +} diff --git a/src/Helpers_TEST.cc b/src/Helpers_TEST.cc index 0e5a36413..e0fc28ab4 100644 --- a/src/Helpers_TEST.cc +++ b/src/Helpers_TEST.cc @@ -23,6 +23,7 @@ #include "test_config.h" // NOLINT(build/include) #include "ignition/gui/Application.hh" +#include "ignition/gui/MainWindow.hh" #include "ignition/gui/Helpers.hh" int gg_argc = 1; @@ -143,3 +144,32 @@ TEST(HelpersTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(findFirstByProperty)) EXPECT_EQ(findFirstByProperty(list, "banana", 3.0), nullptr); EXPECT_EQ(findFirstByProperty(list, "acerola", 1.0), nullptr); } + +///////////////////////////////////////////////// +// See https://github.com/ignitionrobotics/ign-gui/issues/75 +TEST(HelpersTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(worldNames)) +{ + // No app, no window, no names + EXPECT_TRUE(worldNames().empty()); + + Application app(gg_argc, gg_argv); + auto mainWindow = app.findChild(); + ASSERT_NE(nullptr, mainWindow); + + // No names by default + EXPECT_TRUE(worldNames().empty()); + + QStringList names{"banana", "grape"}; + mainWindow->setProperty("worldNames", names); + + // Has names + EXPECT_FALSE(worldNames().empty()); + ASSERT_EQ(2, worldNames().size()); + EXPECT_EQ("banana", worldNames()[0]); + EXPECT_EQ("grape", worldNames()[1]); + + mainWindow->setProperty("worldNames", QStringList()); + + // No more names + EXPECT_TRUE(worldNames().empty()); +} diff --git a/src/plugins/topic_echo/TopicEcho.qml b/src/plugins/topic_echo/TopicEcho.qml index 5bb0f11c8..e1b91543c 100644 --- a/src/plugins/topic_echo/TopicEcho.qml +++ b/src/plugins/topic_echo/TopicEcho.qml @@ -84,8 +84,8 @@ Rectangle { } Rectangle { - width: topicEcho.parent.width - 20 - height: topicEcho.parent.height - 200 + width: topicEcho.parent !== null ? topicEcho.parent.width - 20 : 50 + height: topicEcho.parent !== null ? topicEcho.parent.height - 200 : 50 color: "transparent" ListView { diff --git a/src/plugins/world_control/WorldControl.cc b/src/plugins/world_control/WorldControl.cc index 5b66bfbbd..6a7640c1c 100644 --- a/src/plugins/world_control/WorldControl.cc +++ b/src/plugins/world_control/WorldControl.cc @@ -16,8 +16,10 @@ */ #include -#include #include +#include + +#include "ignition/gui/Helpers.hh" #include "WorldControl.hh" @@ -80,6 +82,12 @@ void WorldControl::LoadConfig(const tinyxml2::XMLElement *_pluginElem) return; } + // World name from window, to construct default topics and services + std::string worldName; + auto worldNames = gui::worldNames(); + if (!worldNames.empty()) + worldName = worldNames[0].toStdString(); + // For world control requests auto serviceElem = _pluginElem->FirstChildElement("service"); if (nullptr != serviceElem && nullptr != serviceElem->GetText()) @@ -87,11 +95,19 @@ void WorldControl::LoadConfig(const tinyxml2::XMLElement *_pluginElem) if (this->dataPtr->controlService.empty()) { - ignerr << "Must specify a service for world control requests." - << std::endl; - return; + if (worldName.empty()) + { + ignerr << "Must specify a for world control requests, or set " + << "the MainWindow's [worldNames] property." << std::endl; + return; + } + + this->dataPtr->controlService = "/world/" + worldName + "/control"; } + ignmsg << "Using world control service [" << this->dataPtr->controlService + << "]" << std::endl; + // Play / pause buttons if (auto playElem = _pluginElem->FirstChildElement("play_pause")) { @@ -128,6 +144,11 @@ void WorldControl::LoadConfig(const tinyxml2::XMLElement *_pluginElem) if (nullptr != statsTopicElem && nullptr != statsTopicElem->GetText()) statsTopic = statsTopicElem->GetText(); + if (statsTopic.empty() && !worldName.empty()) + { + statsTopic = "/world/" + worldName + "/stats"; + } + if (!statsTopic.empty()) { // Subscribe to world_stats @@ -136,6 +157,10 @@ void WorldControl::LoadConfig(const tinyxml2::XMLElement *_pluginElem) { ignerr << "Failed to subscribe to [" << statsTopic << "]" << std::endl; } + else + { + ignmsg << "Listening to stats on [" << statsTopic << "]" << std::endl; + } } } diff --git a/src/plugins/world_stats/WorldStats.cc b/src/plugins/world_stats/WorldStats.cc index 97752632d..13c11c71e 100644 --- a/src/plugins/world_stats/WorldStats.cc +++ b/src/plugins/world_stats/WorldStats.cc @@ -16,8 +16,10 @@ */ #include -#include #include +#include + +#include "ignition/gui/Helpers.hh" #include "WorldStats.hh" @@ -83,6 +85,12 @@ void WorldStats::LoadConfig(const tinyxml2::XMLElement *_pluginElem) return; } + // World name from window, to construct default topics and services + std::string worldName; + auto worldNames = gui::worldNames(); + if (!worldNames.empty()) + worldName = worldNames[0].toStdString(); + // Subscribe std::string topic; auto topicElem = _pluginElem->FirstChildElement("topic"); @@ -91,9 +99,14 @@ void WorldStats::LoadConfig(const tinyxml2::XMLElement *_pluginElem) if (topic.empty()) { - ignerr << "Must specify a topic to subscribe to world statistics." - << std::endl; - return; + if (worldName.empty()) + { + ignerr << "Must specify a to subscribe to world statistics, or " + << "set the MainWindow's [worldNames] property." << std::endl; + return; + } + + topic = "/world/" + worldName + "/stats"; } if (!this->dataPtr->node.Subscribe(topic, &WorldStats::OnWorldStatsMsg, @@ -103,6 +116,8 @@ void WorldStats::LoadConfig(const tinyxml2::XMLElement *_pluginElem) return; } + ignmsg << "Listening to stats on [" << topic << "]" << std::endl; + // Sim time if (auto simTimeElem = _pluginElem->FirstChildElement("sim_time")) {