diff --git a/rqt_image_overlay/CMakeLists.txt b/rqt_image_overlay/CMakeLists.txt index 9639bc8..9ca7c70 100644 --- a/rqt_image_overlay/CMakeLists.txt +++ b/rqt_image_overlay/CMakeLists.txt @@ -36,7 +36,7 @@ qt5_wrap_cpp(SOURCES # Must do this for qt's Meta-Object Compiler. src/image_manager.hpp src/color_dialog_delegate.hpp src/overlay_manager_view.hpp) -qt5_wrap_ui(UIS resource/image_overlay.ui) +qt5_wrap_ui(UIS resource/image_overlay.ui resource/configuration_dialog.ui) add_library(rqt_image_overlay SHARED ${SOURCES} ${UIS}) diff --git a/rqt_image_overlay/resource/configuration_dialog.ui b/rqt_image_overlay/resource/configuration_dialog.ui new file mode 100644 index 0000000..68f1a91 --- /dev/null +++ b/rqt_image_overlay/resource/configuration_dialog.ui @@ -0,0 +1,66 @@ + + + ConfigurationDialog + + + + 0 + 0 + + + + Image Overlay configuration + + + + + + + + Waiting &window (sec) + + + Time to wait before composing an image. If overlay messages arrive much later than the image, increase this value. + + + window + + + + + + + 3 + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + dialog_button_box + accepted() + configuration_dialog + accept() + + + dialog_button_box + rejected() + configuration_dialog + reject() + + + diff --git a/rqt_image_overlay/src/compositor.cpp b/rqt_image_overlay/src/compositor.cpp index 5a578c1..2aad132 100644 --- a/rqt_image_overlay/src/compositor.cpp +++ b/rqt_image_overlay/src/compositor.cpp @@ -22,13 +22,24 @@ namespace rqt_image_overlay { Compositor::Compositor( - const ImageManager & imageManager, const OverlayManager & overlayManager, - float frequency, rclcpp::Duration window) -: imageManager(imageManager), overlayManager(overlayManager), window(window) + const ImageManager & imageManager, const OverlayManager & overlayManager, float frequency) +: imageManager(imageManager), overlayManager(overlayManager), + window_(std::make_shared(0, 300000000)) { startTimer(1000.0 / frequency); } +void Compositor::setWindow(const rclcpp::Duration & window) +{ + std::atomic_store(&window_, std::make_shared(window)); +} + +rclcpp::Duration Compositor::getWindow() const +{ + auto window = std::atomic_load(&window_); + return *window; +} + void Compositor::setCallableSetImage(std::function)> setImage) { this->setImage = setImage; @@ -40,6 +51,9 @@ std::shared_ptr Compositor::compose() return nullptr; } + // Get window_ value through the getWindow() method so that it is thread-safe + auto window = getWindow(); + rclcpp::Time targetTime = systemClock.now() - window; auto [composition, imageHeaderTime] = imageManager.getClosestImageAndHeaderTime(targetTime); OverlayTimeInfo overlayTimeInfo{targetTime, imageHeaderTime}; diff --git a/rqt_image_overlay/src/compositor.hpp b/rqt_image_overlay/src/compositor.hpp index efc4c05..637ec67 100644 --- a/rqt_image_overlay/src/compositor.hpp +++ b/rqt_image_overlay/src/compositor.hpp @@ -32,8 +32,11 @@ class Compositor : public QObject public: Compositor( - const ImageManager & imageManager, const OverlayManager & overlayManager, - float frequency, rclcpp::Duration window = rclcpp::Duration{0, 300000000}); + const ImageManager & imageManager, const OverlayManager & overlayManager, float frequency); + + // Thread safe setter/getter for window + void setWindow(const rclcpp::Duration & window); + rclcpp::Duration getWindow() const; void setCallableSetImage(std::function)> setImage); @@ -46,7 +49,11 @@ class Compositor : public QObject std::function)> setImage; - const rclcpp::Duration window; // Wait window for collecting messages before composing image + // Wait window for collecting messages before composing image. + // To access this value, use the getWindow() method to ensure thread-safety, even from within + // this class. + std::shared_ptr window_; + rclcpp::Clock systemClock{RCL_SYSTEM_TIME}; }; diff --git a/rqt_image_overlay/src/image_overlay.cpp b/rqt_image_overlay/src/image_overlay.cpp index 00f0c08..6ec3d17 100644 --- a/rqt_image_overlay/src/image_overlay.cpp +++ b/rqt_image_overlay/src/image_overlay.cpp @@ -16,6 +16,7 @@ #include #include #include "./ui_image_overlay.h" +#include "./ui_configuration_dialog.h" #include "image_overlay.hpp" #include "compositor.hpp" #include "overlay_manager.hpp" @@ -97,7 +98,7 @@ void ImageOverlay::saveSettings( instance_settings.setValue("image_topic", QString::fromStdString(imageTopic.topic)); instance_settings.setValue("image_transport", QString::fromStdString(imageTopic.transport)); } - + instance_settings.setValue("compositor_window", compositor->getWindow().seconds()); overlayManager->saveSettings(instance_settings); } @@ -112,6 +113,11 @@ void ImageOverlay::restoreSettings( ui->image_topics_combo_box->setCurrentIndex(1); } + if (instance_settings.contains("compositor_window")) { + auto window_double = instance_settings.value("compositor_window").toDouble(); + auto duration = rclcpp::Duration::from_seconds(window_double); + compositor->setWindow(duration); + } overlayManager->restoreSettings(instance_settings); } @@ -132,6 +138,24 @@ void ImageOverlay::fillOverlayMenu() ui->add_overlay_button->setMenu(menu.get()); } +bool ImageOverlay::hasConfiguration() const +{ + return true; +} + +void ImageOverlay::triggerConfiguration() +{ + auto configuration_dialog = std::make_unique(); + auto ui_configuration_dialog = std::make_unique(); + ui_configuration_dialog->setupUi(configuration_dialog.get()); + ui_configuration_dialog->window->setValue(compositor->getWindow().seconds()); + + if (configuration_dialog->exec() == QDialog::Accepted) { + auto window_seconds = ui_configuration_dialog->window->value(); + compositor->setWindow(rclcpp::Duration::from_seconds(window_seconds)); + } +} + } // namespace rqt_image_overlay diff --git a/rqt_image_overlay/src/image_overlay.hpp b/rqt_image_overlay/src/image_overlay.hpp index 77377b4..5772bbd 100644 --- a/rqt_image_overlay/src/image_overlay.hpp +++ b/rqt_image_overlay/src/image_overlay.hpp @@ -42,6 +42,8 @@ class ImageOverlay : public rqt_gui_cpp::Plugin void restoreSettings( const qt_gui_cpp::Settings &, const qt_gui_cpp::Settings & instanceSettings) override; + bool hasConfiguration() const override; + void triggerConfiguration() override; public slots: void removeOverlay();