diff --git a/rviz_common/CMakeLists.txt b/rviz_common/CMakeLists.txt index 770befd0c..4ab82e206 100644 --- a/rviz_common/CMakeLists.txt +++ b/rviz_common/CMakeLists.txt @@ -44,6 +44,7 @@ find_package(geometry_msgs REQUIRED) find_package(pluginlib REQUIRED) find_package(rclcpp REQUIRED) find_package(resource_retriever REQUIRED) +find_package(rviz_resource_interfaces REQUIRED) find_package(rviz_rendering REQUIRED) find_package(sensor_msgs REQUIRED) find_package(std_msgs REQUIRED) @@ -260,6 +261,7 @@ target_link_libraries(rviz_common PUBLIC tf2::tf2 tf2_ros::tf2_ros yaml-cpp::yaml-cpp + ${rviz_resource_interfaces_TARGETS} ) target_link_libraries(rviz_common PRIVATE @@ -286,6 +288,7 @@ ament_export_dependencies( tf2_ros yaml_cpp_vendor yaml-cpp + rviz_resource_interfaces ) # Export old-style CMake variables diff --git a/rviz_common/include/rviz_common/visualization_frame.hpp b/rviz_common/include/rviz_common/visualization_frame.hpp index 151bf346b..1f821c316 100644 --- a/rviz_common/include/rviz_common/visualization_frame.hpp +++ b/rviz_common/include/rviz_common/visualization_frame.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include // NOLINT: cpplint is unable to handle the include order here @@ -43,10 +44,12 @@ #include // NOLINT: cpplint is unable to handle the include order here #include // NOLINT: cpplint is unable to handle the include order here +#include #include "rviz_common/config.hpp" -#include "rviz_rendering/render_window.hpp" #include "rviz_common/window_manager_interface.hpp" #include "rviz_common/ros_integration/ros_node_abstraction_iface.hpp" +#include "rviz_rendering/render_window.hpp" +#include class QAction; class QActionGroup; @@ -83,6 +86,7 @@ class WidgetGeometryChangeDetector; */ class RVIZ_COMMON_PUBLIC VisualizationFrame : public QMainWindow, public WindowManagerInterface { + using LoadConfig = rviz_resource_interfaces::srv::LoadConfig; Q_OBJECT public: @@ -172,12 +176,19 @@ class RVIZ_COMMON_PUBLIC VisualizationFrame : public QMainWindow, public WindowM void savePersistentSettings(); - /// Load display settings from the given file. + rclcpp::Service::SharedPtr load_config_service_; + + void + loadDisplayConfigService( + const std::shared_ptr request, + std::shared_ptr response); + + /// Load display settings from the configuration string. /** - * \param path The full path of the config file to load from. + * \param config_string Can be path to the config file to load from or a yaml configuration string. */ void - loadDisplayConfig(const QString & path); + loadDisplayConfig(const QString & config_string); // TODO(wjwwood): consider changing this function to raise an exception // when there is a failure, rather than the getErrorMessage() diff --git a/rviz_common/package.xml b/rviz_common/package.xml index 8e590bf19..31f50876e 100644 --- a/rviz_common/package.xml +++ b/rviz_common/package.xml @@ -41,6 +41,7 @@ pluginlib rclcpp resource_retriever + rviz_resource_interfaces rviz_ogre_vendor rviz_rendering sensor_msgs diff --git a/rviz_common/src/rviz_common/visualization_frame.cpp b/rviz_common/src/rviz_common/visualization_frame.cpp index 937d19b5b..c68a8c15c 100644 --- a/rviz_common/src/rviz_common/visualization_frame.cpp +++ b/rviz_common/src/rviz_common/visualization_frame.cpp @@ -63,6 +63,7 @@ #include // NOLINT cpplint cannot handle include order here #include "rclcpp/clock.hpp" +#include #include "tf2_ros/buffer.hpp" #include "tf2_ros/transform_listener.hpp" @@ -75,6 +76,7 @@ #include "rviz_common/yaml_config_reader.hpp" #include "rviz_common/yaml_config_writer.hpp" #include "rviz_rendering/render_window.hpp" +#include #include "./env_config.hpp" #include "./failed_panel.hpp" @@ -351,6 +353,14 @@ void VisualizationFrame::initialize( loadDisplayConfig(QString::fromStdString(default_display_config_file_)); } + auto node_abstraction = rviz_ros_node.lock(); + auto node = node_abstraction->get_raw_node(); + load_config_service_ = node->create_service( + node_abstraction->get_node_name() + "/load_config", + std::bind( + &VisualizationFrame::loadDisplayConfigService, this, + std::placeholders::_1, std::placeholders::_2)); + // Periodically process events for the splash screen. QCoreApplication::processEvents(); @@ -670,14 +680,28 @@ void VisualizationFrame::markRecentConfig(const std::string & path) updateRecentConfigMenu(); } -void VisualizationFrame::loadDisplayConfig(const QString & qpath) +void VisualizationFrame::loadDisplayConfigService( + const std::shared_ptr request, + std::shared_ptr response) { - std::string path = qpath.toStdString(); - QFileInfo path_info(qpath); + RVIZ_COMMON_LOG_INFO_STREAM( + "Requested loadConfig" << request->config_string.c_str() + ); + loadDisplayConfig(QString::fromStdString(request->config_string)); + response->success = true; + response->message = "Loaded display config."; +} + +void VisualizationFrame::loadDisplayConfig(const QString & config_string) +{ + std::string path = config_string.toStdString(); + QFileInfo path_info(config_string); std::string actual_load_path = path; - if (!path_info.exists() || path_info.isDir()) { + bool is_loadable_path = (path_info.exists() && !path_info.isDir()); + if (!is_loadable_path) { actual_load_path = package_path_ + "/default.rviz"; - if (!QFile(QString::fromStdString(actual_load_path)).exists()) { + is_loadable_path = QFile(QString::fromStdString(actual_load_path)).exists(); + if (!is_loadable_path) { RVIZ_COMMON_LOG_ERROR_STREAM( "Default display config '" << actual_load_path.c_str() << "' not found. RViz will be very empty at first."); @@ -705,7 +729,12 @@ void VisualizationFrame::loadDisplayConfig(const QString & qpath) YamlConfigReader reader; Config config; - reader.readFile(config, QString::fromStdString(actual_load_path)); + if (is_loadable_path) { + reader.readFile(config, QString::fromStdString(actual_load_path)); + } else { // Treat as yaml string content + reader.readString(config, config_string); + } + if (!reader.error()) { try { load(config); @@ -714,11 +743,11 @@ void VisualizationFrame::loadDisplayConfig(const QString & qpath) } } - markRecentConfig(path); - - setDisplayConfigFile(path); - - last_config_dir_ = path_info.absolutePath().toStdString(); + if (is_loadable_path) { + markRecentConfig(path); + setDisplayConfigFile(path); + last_config_dir_ = path_info.absolutePath().toStdString(); + } post_load_timer_->start(1000); } diff --git a/rviz_resource_interfaces/CMakeLists.txt b/rviz_resource_interfaces/CMakeLists.txt index fca2dc586..5a0534152 100644 --- a/rviz_resource_interfaces/CMakeLists.txt +++ b/rviz_resource_interfaces/CMakeLists.txt @@ -11,6 +11,7 @@ find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "srv/GetResource.srv" + "srv/LoadConfig.srv" ) ament_export_dependencies(rosidl_default_runtime) diff --git a/rviz_resource_interfaces/srv/LoadConfig.srv b/rviz_resource_interfaces/srv/LoadConfig.srv new file mode 100644 index 000000000..db9ac475f --- /dev/null +++ b/rviz_resource_interfaces/srv/LoadConfig.srv @@ -0,0 +1,4 @@ +string config_string +--- +bool success +string message