├── src
├── ros
│ ├── CMakeLists.txt
│ └── thread
│ │ ├── BasicThread.cpp
│ │ ├── RecordBagThread.hpp
│ │ ├── PCDsToBagThread.hpp
│ │ ├── EditBagThread.hpp
│ │ ├── DummyBagThread.hpp
│ │ ├── BagToPCDsThread.hpp
│ │ ├── BagToVideoThread.hpp
│ │ ├── VideoToBagThread.hpp
│ │ ├── BagToImagesThread.hpp
│ │ ├── MergeBagsThread.hpp
│ │ ├── SendTF2Thread.hpp
│ │ ├── PublishImagesThread.hpp
│ │ ├── BasicThread.hpp
│ │ ├── ChangeCompressionBagThread.hpp
│ │ ├── PublishVideoThread.hpp
│ │ ├── MergeBagsThread.cpp
│ │ ├── SendTF2Thread.cpp
│ │ ├── ChangeCompressionBagThread.cpp
│ │ ├── RecordBagThread.cpp
│ │ ├── PCDsToBagThread.cpp
│ │ ├── VideoToBagThread.cpp
│ │ ├── PublishVideoThread.cpp
│ │ ├── TF2ToFileThread.hpp
│ │ ├── PublishImagesThread.cpp
│ │ └── TF2ToFileThread.cpp
├── CMakeLists.txt
├── ui
│ ├── settings
│ │ ├── input
│ │ │ ├── RGBSettings.hpp
│ │ │ ├── VideoSettings.hpp
│ │ │ ├── PublishSettings.hpp
│ │ │ ├── AdvancedSettings.hpp
│ │ │ ├── DummyBagSettings.hpp
│ │ │ ├── EditBagSettings.hpp
│ │ │ ├── RecordBagSettings.hpp
│ │ │ ├── TF2ToFileSettings.hpp
│ │ │ ├── PCDsToBagSettings.hpp
│ │ │ ├── VideoToBagSettings.hpp
│ │ │ ├── MergeBagsSettings.hpp
│ │ │ ├── BagToVideoSettings.hpp
│ │ │ ├── BasicSettings.hpp
│ │ │ ├── CompressBagSettings.hpp
│ │ │ ├── BagToImagesSettings.hpp
│ │ │ ├── SendTF2Settings.hpp
│ │ │ ├── DeleteSourceSettings.hpp
│ │ │ ├── VideoSettings.cpp
│ │ │ ├── RGBSettings.cpp
│ │ │ ├── PCDsToBagSettings.cpp
│ │ │ ├── VideoToBagSettings.cpp
│ │ │ ├── BasicSettings.cpp
│ │ │ ├── DeleteSourceSettings.cpp
│ │ │ ├── CompressBagSettings.cpp
│ │ │ ├── TF2ToFileSettings.cpp
│ │ │ ├── BagToVideoSettings.cpp
│ │ │ ├── AdvancedSettings.cpp
│ │ │ ├── PublishSettings.cpp
│ │ │ ├── BagToImagesSettings.cpp
│ │ │ ├── MergeBagsSettings.cpp
│ │ │ ├── DummyBagSettings.cpp
│ │ │ ├── RecordBagSettings.cpp
│ │ │ ├── CMakeLists.txt
│ │ │ ├── SendTF2Settings.cpp
│ │ │ └── EditBagSettings.cpp
│ │ ├── CMakeLists.txt
│ │ ├── DialogSettings.cpp
│ │ └── DialogSettings.hpp
│ ├── SettingsDialog.hpp
│ ├── InputWidgets
│ │ ├── BagToPCDsWidget.hpp
│ │ ├── TopicsServicesInfoWidget.hpp
│ │ ├── BagInfoWidget.hpp
│ │ ├── BagToImagesWidget.hpp
│ │ ├── BagToVideoWidget.hpp
│ │ ├── ChangeCompressionWidget.hpp
│ │ ├── PCDsToBagWidget.hpp
│ │ ├── TF2ToFileWidget.hpp
│ │ ├── RecordBagWidget.hpp
│ │ ├── MergeBagsWidget.hpp
│ │ ├── VideoToBagWidget.hpp
│ │ ├── BagToPCDsWidget.cpp
│ │ ├── PublishWidget.hpp
│ │ ├── SendTF2Widget.hpp
│ │ ├── EditBagWidget.hpp
│ │ ├── DummyBagWidget.hpp
│ │ ├── CMakeLists.txt
│ │ ├── AdvancedInputWidget.hpp
│ │ └── BasicInputWidget.hpp
│ ├── CMakeLists.txt
│ ├── HelperWidgets
│ │ ├── LowDiskSpaceWidget.hpp
│ │ ├── CMakeLists.txt
│ │ ├── MessageCountWidget.hpp
│ │ ├── TopicComboBoxWidget.hpp
│ │ ├── TopicWidget.hpp
│ │ ├── MessageCountWidget.cpp
│ │ ├── BasicBagWidget.hpp
│ │ ├── TopicListingInputWidget.hpp
│ │ ├── LowDiskSpaceWidget.cpp
│ │ ├── TopicComboBoxWidget.cpp
│ │ └── TopicWidget.cpp
│ ├── ProgressWidget.hpp
│ ├── MainWindow.hpp
│ └── StartWidget.hpp
├── utils
│ ├── NodeWrapper.hpp
│ ├── UtilsGeneral.hpp
│ ├── UtilsGeneral.cpp
│ ├── VideoEncoder.cpp
│ ├── VideoEncoder.hpp
│ ├── CMakeLists.txt
│ ├── UtilsUI.hpp
│ └── UtilsROS.hpp
├── main.cpp
└── cli
│ └── CMakeLists.txt
├── resources
├── gifs
│ ├── merging_black.gif
│ ├── merging_white.gif
│ ├── recording_black.gif
│ ├── recording_white.gif
│ ├── compressing_black.gif
│ ├── compressing_white.gif
│ ├── publishing_black.gif
│ ├── publishing_white.gif
│ ├── sending_tf2_black.gif
│ ├── sending_tf2_white.gif
│ ├── decompressing_black.gif
│ └── decompressing_white.gif
├── icons
│ ├── minus_black.svg
│ ├── minus_white.svg
│ ├── plus_black.svg
│ ├── plus_white.svg
│ ├── warning.svg
│ ├── info_tools_black.svg
│ ├── info_tools_white.svg
│ ├── bag_tools_black.svg
│ ├── bag_tools_white.svg
│ ├── publishing_tools_black.svg
│ ├── publishing_tools_white.svg
│ ├── bag_info_black.svg
│ ├── bag_info_white.svg
│ ├── send_tf2_black.svg
│ ├── send_tf2_white.svg
│ ├── topics_services_info_black.svg
│ ├── topics_services_info_white.svg
│ ├── edit_bag_black.svg
│ ├── edit_bag_white.svg
│ ├── dummy_bag_black.svg
│ ├── dummy_bag_white.svg
│ ├── bag_to_video_black.svg
│ ├── bag_to_video_white.svg
│ ├── video_to_bag_black.svg
│ ├── video_to_bag_white.svg
│ ├── compress_bag_black.svg
│ ├── compress_bag_white.svg
│ ├── decompress_bag_black.svg
│ ├── decompress_bag_white.svg
│ ├── publish_video_black.svg
│ ├── publish_video_white.svg
│ ├── gear_white.svg
│ ├── record_bag_black.svg
│ ├── record_bag_white.svg
│ ├── gear_black.svg
│ ├── main.svg
│ ├── merge_bags_black.svg
│ ├── merge_bags_white.svg
│ ├── tf2_to_file_black.svg
│ ├── tf2_to_file_white.svg
│ ├── publish_images_black.svg
│ └── publish_images_white.svg
└── resources.qrc
├── test
├── main.cpp
├── CMakeLists.txt
└── utils
│ └── UtilsUITest.cpp
├── package.xml
└── .github
└── workflows
├── jazzy.yml
├── humble.yml
├── kilted.yml
└── rolling.yml
/src/ros/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(thread)
2 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(cli)
2 | add_subdirectory(ros)
3 | add_subdirectory(ui)
4 | add_subdirectory(utils)
5 |
--------------------------------------------------------------------------------
/resources/gifs/merging_black.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/merging_black.gif
--------------------------------------------------------------------------------
/resources/gifs/merging_white.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/merging_white.gif
--------------------------------------------------------------------------------
/resources/gifs/recording_black.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/recording_black.gif
--------------------------------------------------------------------------------
/resources/gifs/recording_white.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/recording_white.gif
--------------------------------------------------------------------------------
/resources/gifs/compressing_black.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/compressing_black.gif
--------------------------------------------------------------------------------
/resources/gifs/compressing_white.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/compressing_white.gif
--------------------------------------------------------------------------------
/resources/gifs/publishing_black.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/publishing_black.gif
--------------------------------------------------------------------------------
/resources/gifs/publishing_white.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/publishing_white.gif
--------------------------------------------------------------------------------
/resources/gifs/sending_tf2_black.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/sending_tf2_black.gif
--------------------------------------------------------------------------------
/resources/gifs/sending_tf2_white.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/sending_tf2_white.gif
--------------------------------------------------------------------------------
/resources/gifs/decompressing_black.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/decompressing_black.gif
--------------------------------------------------------------------------------
/resources/gifs/decompressing_white.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaxFleur/ros2_utils_tool/HEAD/resources/gifs/decompressing_white.gif
--------------------------------------------------------------------------------
/src/ros/thread/BasicThread.cpp:
--------------------------------------------------------------------------------
1 | #include "BasicThread.hpp"
2 |
3 | BasicThread::BasicThread(const QString& sourceDirectory,
4 | const QString& topicName,
5 | QObject* parent) :
6 | QThread(parent), m_sourceDirectory(sourceDirectory.toStdString()), m_topicName(topicName.toStdString())
7 | {
8 | }
9 |
--------------------------------------------------------------------------------
/resources/icons/minus_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/resources/icons/minus_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/test/main.cpp:
--------------------------------------------------------------------------------
1 | #define CATCH_CONFIG_RUNNER
2 | #include "catch_ros2/catch_ros2.hpp"
3 |
4 | #include
5 |
6 | #include "rclcpp/rclcpp.hpp"
7 |
8 | int
9 | main(int argc, char **argv)
10 | {
11 | setenv("QT_QPA_PLATFORM", "offscreen", 0);
12 |
13 | QApplication app(argc, argv);
14 | rclcpp::init(argc, argv);
15 |
16 | return Catch::Session().run(argc, argv);
17 | }
18 |
--------------------------------------------------------------------------------
/resources/icons/plus_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/resources/icons/plus_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/src/ui/settings/input/RGBSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedSettings.hpp"
4 |
5 | // Store rgb settings
6 | class RGBSettings : public AdvancedSettings {
7 | public:
8 | RGBSettings(Parameters::RGBParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | protected:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::RGBParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/VideoSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "RGBSettings.hpp"
4 |
5 | // Store video settings
6 | class VideoSettings : public RGBSettings {
7 | public:
8 | VideoSettings(Parameters::VideoParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | protected:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::VideoParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(input)
2 |
3 | add_library (rt_settings INTERFACE)
4 |
5 | target_include_directories (rt_settings
6 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
7 | )
8 |
9 | target_sources(rt_settings INTERFACE
10 | ${CMAKE_CURRENT_LIST_DIR}/GeneralSettings.hpp
11 | ${CMAKE_CURRENT_LIST_DIR}/DialogSettings.cpp
12 | ${CMAKE_CURRENT_LIST_DIR}/DialogSettings.hpp
13 | )
14 |
15 | target_link_libraries(rt_settings
16 | INTERFACE Qt::Widgets
17 | )
18 |
--------------------------------------------------------------------------------
/src/ui/settings/input/PublishSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "VideoSettings.hpp"
4 |
5 | // Store publishing parameters
6 | class PublishSettings : public VideoSettings {
7 | public:
8 | PublishSettings(Parameters::PublishParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::PublishParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/AdvancedSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicSettings.hpp"
4 |
5 | // Store advanced settings
6 | class AdvancedSettings : public BasicSettings {
7 | public:
8 | AdvancedSettings(Parameters::AdvancedParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | protected:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::AdvancedParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/utils/NodeWrapper.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "rclcpp/rclcpp.hpp"
4 |
5 | // Wrapper for a ROS node to keep ROS includes out of the UI code
6 | class NodeWrapper {
7 | public:
8 | explicit
9 | NodeWrapper(const std::string& nodeName)
10 | {
11 | m_node = std::make_shared(nodeName);
12 | }
13 |
14 | std::shared_ptr
15 | getNode()
16 | {
17 | return m_node;
18 | }
19 |
20 | private:
21 | std::shared_ptr m_node;
22 | };
23 |
--------------------------------------------------------------------------------
/src/ui/settings/input/DummyBagSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicSettings.hpp"
4 |
5 | // Store dummy bag creation parameters
6 | class DummyBagSettings : public BasicSettings {
7 | public:
8 | DummyBagSettings(Parameters::DummyBagParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::DummyBagParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/EditBagSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "DeleteSourceSettings.hpp"
4 |
5 | // Store bag editing parameters
6 | class EditBagSettings : public DeleteSourceSettings {
7 | public:
8 | EditBagSettings(Parameters::EditBagParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::EditBagParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/utils/UtilsGeneral.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | // Util functions for all sorts of things
6 | namespace Utils::General
7 | {
8 | // Returns available drive space in gigabytes
9 | [[nodiscard]] float
10 | getAvailableDriveSpace(const QString& path);
11 |
12 | // Get a file extension
13 | [[nodiscard]] const QString
14 | getFileExtension(const QString& path);
15 |
16 | static constexpr float MINIMUM_RECOMMENDED_DRIVE_SPACE = 10.737f;
17 | static constexpr long GIGABYTE_IN_BYTES = 1073741824;
18 | }
19 |
--------------------------------------------------------------------------------
/src/ui/settings/input/RecordBagSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicSettings.hpp"
4 |
5 | // Store bag recording parameters
6 | class RecordBagSettings : public BasicSettings {
7 | public:
8 | RecordBagSettings(Parameters::RecordBagParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::RecordBagParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/TF2ToFileSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedSettings.hpp"
4 |
5 | // Store tf2 to json parameters
6 | class TF2ToFileSettings : public AdvancedSettings {
7 | public:
8 | TF2ToFileSettings(Parameters::TF2ToFileParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::TF2ToFileParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/PCDsToBagSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedSettings.hpp"
4 |
5 | // Store bag creation parameters
6 | class PCDsToBagSettings : public AdvancedSettings {
7 | public:
8 | PCDsToBagSettings(Parameters::PCDsToBagParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::PCDsToBagParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/VideoToBagSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "VideoSettings.hpp"
4 |
5 | // Store bag creation parameters
6 | class VideoToBagSettings : public VideoSettings {
7 | public:
8 | VideoToBagSettings(Parameters::VideoToBagParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::VideoToBagParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ros/thread/RecordBagThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread used to record a bag file
7 | class RecordBagThread : public BasicThread {
8 | Q_OBJECT
9 | public:
10 | explicit
11 | RecordBagThread(const Parameters::RecordBagParameters& parameters,
12 | QObject* parent = nullptr);
13 |
14 | void
15 | run() override;
16 |
17 | private:
18 | const Parameters::RecordBagParameters& m_parameters;
19 | };
20 |
--------------------------------------------------------------------------------
/src/ui/settings/input/MergeBagsSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "DeleteSourceSettings.hpp"
4 |
5 | // Store bag editing parameters
6 | class MergeBagsSettings : public DeleteSourceSettings {
7 | public:
8 | MergeBagsSettings(Parameters::MergeBagsParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::MergeBagsParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/BagToVideoSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "VideoSettings.hpp"
4 |
5 | // Store video out of ROS bag creation parameters
6 | class BagToVideoSettings : public VideoSettings {
7 | public:
8 | BagToVideoSettings(Parameters::BagToVideoParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::BagToVideoParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/BasicSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "GeneralSettings.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Store parameters used by all input widgets
7 | class BasicSettings : public GeneralSettings {
8 | public:
9 | BasicSettings(Parameters::BasicParameters& parameters,
10 | const QString& groupName);
11 |
12 | bool
13 | write() override;
14 |
15 | protected:
16 | bool
17 | read() override;
18 |
19 | private:
20 | Parameters::BasicParameters& m_parameters;
21 | };
22 |
--------------------------------------------------------------------------------
/src/ui/settings/input/CompressBagSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "DeleteSourceSettings.hpp"
4 |
5 | // Store bag editing parameters
6 | class CompressBagSettings : public DeleteSourceSettings {
7 | public:
8 | CompressBagSettings(Parameters::CompressBagParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::CompressBagParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/settings/input/BagToImagesSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "RGBSettings.hpp"
4 |
5 | // Store parameters for image sequence out of ROS bag creation
6 | class BagToImagesSettings : public RGBSettings {
7 | public:
8 | BagToImagesSettings(Parameters::BagToImagesParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | private:
15 | bool
16 | read() override;
17 |
18 | private:
19 | Parameters::BagToImagesParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ros/thread/PCDsToBagThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread used to write a set of pcd files to a bag file
7 | class PCDsToBagThread : public BasicThread {
8 | Q_OBJECT
9 |
10 | public:
11 | explicit
12 | PCDsToBagThread(const Parameters::PCDsToBagParameters& parameters,
13 | QObject* parent = nullptr);
14 |
15 | void
16 | run() override;
17 |
18 | private:
19 | const Parameters::PCDsToBagParameters& m_parameters;
20 | };
21 |
--------------------------------------------------------------------------------
/src/ui/SettingsDialog.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "DialogSettings.hpp"
4 | #include "Parameters.hpp"
5 |
6 | #include
7 |
8 | // Dialog used to modify settings
9 | class SettingsDialog : public QDialog {
10 | Q_OBJECT
11 |
12 | public:
13 | SettingsDialog(Parameters::DialogParameters& parameters,
14 | QWidget* parent = 0);
15 |
16 | private:
17 | void
18 | storeParametersCheckStateChanged();
19 |
20 | private:
21 | Parameters::DialogParameters& m_parameters;
22 |
23 | DialogSettings m_settings;
24 | };
25 |
--------------------------------------------------------------------------------
/resources/icons/warning.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "ui/MainWindow.hpp"
2 |
3 | #include "rclcpp/rclcpp.hpp"
4 |
5 | #include
6 |
7 | int
8 | main(int argc, char* argv[])
9 | {
10 | // Initialize ROS and Qt
11 | rclcpp::init(argc, argv);
12 | QApplication app(argc, argv);
13 | app.setWindowIcon(QIcon(":/icons/main.svg"));
14 |
15 | MainWindow mainWindow;
16 | mainWindow.show();
17 |
18 | while (rclcpp::ok()) {
19 | app.processEvents();
20 | std::this_thread::sleep_for(std::chrono::milliseconds(20));
21 | }
22 |
23 | rclcpp::shutdown();
24 | return EXIT_SUCCESS;
25 | }
26 |
--------------------------------------------------------------------------------
/src/utils/UtilsGeneral.cpp:
--------------------------------------------------------------------------------
1 | #include "UtilsGeneral.hpp"
2 |
3 | #include
4 |
5 | #include
6 |
7 | namespace Utils::General
8 | {
9 | [[nodiscard]] float
10 | getAvailableDriveSpace(const QString& path)
11 | {
12 | std::error_code errorCode;
13 | const auto spaceInfo = std::filesystem::space(path.toStdString(), errorCode);
14 |
15 | return (float) spaceInfo.available / (float) Utils::General::GIGABYTE_IN_BYTES;
16 | }
17 |
18 |
19 | const QString
20 | getFileExtension(const QString& path)
21 | {
22 | QFileInfo fileInfo(path);
23 | return fileInfo.suffix();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/BagToPCDsWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedSettings.hpp"
4 | #include "Parameters.hpp"
5 | #include "TopicComboBoxWidget.hpp"
6 |
7 | class QComboBox;
8 | class QLineEdit;
9 |
10 | // The widget used to manage writing pcd files out of a ROS bag
11 | class BagToPCDsWidget : public TopicComboBoxWidget
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | BagToPCDsWidget(Parameters::AdvancedParameters& parameters,
17 | QWidget* parent = 0);
18 |
19 | private:
20 | Parameters::AdvancedParameters& m_parameters;
21 |
22 | AdvancedSettings m_settings;
23 | };
24 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/TopicsServicesInfoWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicInputWidget.hpp"
4 |
5 | #include
6 | #include
7 |
8 | class QTreeWidget;
9 |
10 | // The widget showing topics and services info data
11 | class TopicsServicesInfoWidget : public BasicInputWidget
12 | {
13 | Q_OBJECT
14 | public:
15 | explicit
16 | TopicsServicesInfoWidget(QWidget* parent = 0);
17 |
18 | private:
19 | void
20 | fillTree() const;
21 |
22 | private:
23 | QPointer m_treeWidget;
24 |
25 | static constexpr int COL_NAME = 0;
26 | static constexpr int COL_TYPE = 1;
27 | };
28 |
--------------------------------------------------------------------------------
/src/ui/settings/input/SendTF2Settings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicSettings.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Store sending tf2 parameters
7 | class SendTF2Settings : public BasicSettings {
8 | public:
9 | SendTF2Settings(Parameters::SendTF2Parameters& parameters,
10 | const QString& groupName);
11 |
12 | bool
13 | write() override;
14 |
15 | private:
16 | bool
17 | read() override;
18 |
19 | private:
20 | Parameters::SendTF2Parameters& m_parameters;
21 |
22 | static constexpr int TRANSLATION_SIZE = 3;
23 | static constexpr int ROTATION_SIZE = 4;
24 | };
25 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/BagInfoWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicInputWidget.hpp"
4 |
5 | #include
6 | #include
7 |
8 | class QTreeWidget;
9 |
10 | // The widget showing bag info data
11 | class BagInfoWidget : public BasicInputWidget
12 | {
13 | Q_OBJECT
14 | public:
15 | explicit
16 | BagInfoWidget(QWidget* parent = 0);
17 |
18 | private slots:
19 | void
20 | displayBagInfo();
21 |
22 | private:
23 | // Main tree used to display bag info
24 | QPointer m_infoTreeWidget;
25 |
26 | static constexpr int COL_DESCRIPTION = 0;
27 | static constexpr int COL_INFORMATION = 1;
28 | };
29 |
--------------------------------------------------------------------------------
/src/utils/VideoEncoder.cpp:
--------------------------------------------------------------------------------
1 | #include "VideoEncoder.hpp"
2 |
3 | VideoEncoder::VideoEncoder(int fourcc) :
4 | m_fourcc(fourcc)
5 | {
6 | }
7 |
8 |
9 | bool
10 | VideoEncoder::setVideoWriter(const std::string& directory, int fps, int width, int height, bool useHardwareAcceleration, bool useBWImages)
11 | {
12 | m_videoWriter = cv::VideoWriter(directory, m_fourcc, fps, cv::Size(width, height), {
13 | cv::VIDEOWRITER_PROP_HW_ACCELERATION, useHardwareAcceleration ? cv::VIDEO_ACCELERATION_ANY : cv::VIDEO_ACCELERATION_NONE,
14 | cv::VIDEOWRITER_PROP_IS_COLOR, !useBWImages
15 | });
16 |
17 | return m_videoWriter.isOpened();
18 | }
19 |
--------------------------------------------------------------------------------
/src/ros/thread/EditBagThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread used to write an edited ROS bag file
7 | class EditBagThread : public BasicThread {
8 | Q_OBJECT
9 | public:
10 | explicit
11 | EditBagThread(const Parameters::EditBagParameters& parameters,
12 | unsigned int numberOfThreads,
13 | QObject* parent = nullptr);
14 |
15 | void
16 | run() override;
17 |
18 | private:
19 | const Parameters::EditBagParameters& m_parameters;
20 |
21 | const unsigned int m_numberOfThreads;
22 | };
23 |
--------------------------------------------------------------------------------
/resources/icons/info_tools_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/resources/icons/info_tools_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/src/ros/thread/DummyBagThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Dummy bag thread, used to create a bag file with dummy data
7 | class DummyBagThread : public BasicThread {
8 | Q_OBJECT
9 | public:
10 | explicit
11 | DummyBagThread(const Parameters::DummyBagParameters& parameters,
12 | unsigned int numberOfThreads,
13 | QObject* parent = nullptr);
14 |
15 | void
16 | run() override;
17 |
18 | private:
19 | const Parameters::DummyBagParameters& m_parameters;
20 |
21 | const unsigned int m_numberOfThreads;
22 | };
23 |
--------------------------------------------------------------------------------
/src/ros/thread/BagToPCDsThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread used to write a bag images topic to a set of pcd files
7 | class BagToPCDsThread : public BasicThread {
8 | Q_OBJECT
9 | public:
10 | explicit
11 | BagToPCDsThread(const Parameters::AdvancedParameters& parameters,
12 | unsigned int numberOfThreads,
13 | QObject* parent = nullptr);
14 |
15 | void
16 | run() override;
17 |
18 | private:
19 | const Parameters::AdvancedParameters& m_parameters;
20 |
21 | const unsigned int m_numberOfThreads;
22 | };
23 |
--------------------------------------------------------------------------------
/src/ros/thread/BagToVideoThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread writing a bag images topic to a video file
7 | class BagToVideoThread : public BasicThread {
8 | Q_OBJECT
9 | public:
10 | explicit
11 | BagToVideoThread(const Parameters::BagToVideoParameters& parameters,
12 | bool useHardwareAcceleration,
13 | QObject* parent = nullptr);
14 |
15 | void
16 | run() override;
17 |
18 | private:
19 | const Parameters::BagToVideoParameters& m_parameters;
20 |
21 | const bool m_useHardwareAcceleration;
22 | };
23 |
--------------------------------------------------------------------------------
/src/ros/thread/VideoToBagThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread used to write a video to a bag file
7 | class VideoToBagThread : public BasicThread {
8 | Q_OBJECT
9 |
10 | public:
11 | explicit
12 | VideoToBagThread(const Parameters::VideoToBagParameters& parameters,
13 | bool useHardwareAcceleration,
14 | QObject* parent = nullptr);
15 |
16 | void
17 | run() override;
18 |
19 | private:
20 | const Parameters::VideoToBagParameters& m_parameters;
21 |
22 | const bool m_useHardwareAcceleration;
23 | };
24 |
--------------------------------------------------------------------------------
/src/ros/thread/BagToImagesThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread used to write a bag images topic to a set of images
7 | class BagToImagesThread : public BasicThread {
8 | Q_OBJECT
9 | public:
10 | explicit
11 | BagToImagesThread(const Parameters::BagToImagesParameters& parameters,
12 | unsigned int numberOfThreads,
13 | QObject* parent = nullptr);
14 |
15 | void
16 | run() override;
17 |
18 | private:
19 | const Parameters::BagToImagesParameters& m_parameters;
20 |
21 | const unsigned int m_numberOfThreads;
22 | };
23 |
--------------------------------------------------------------------------------
/src/ui/settings/input/DeleteSourceSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedSettings.hpp"
4 |
5 | // Store bag editing parameters
6 | class DeleteSourceSettings : public AdvancedSettings {
7 | public:
8 | DeleteSourceSettings(Parameters::DeleteSourceParameters& parameters,
9 | const QString& groupName);
10 |
11 | bool
12 | write() override;
13 |
14 | protected:
15 | bool
16 | read() override;
17 |
18 | void
19 | setDefaultValueToTrue()
20 | {
21 | m_isDefaultValueTrue = true;
22 | }
23 |
24 | private:
25 | Parameters::DeleteSourceParameters& m_parameters;
26 |
27 | bool m_isDefaultValueTrue = false;
28 | };
29 |
--------------------------------------------------------------------------------
/src/ui/settings/input/VideoSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "VideoSettings.hpp"
2 |
3 | VideoSettings::VideoSettings(Parameters::VideoParameters& parameters, const QString& groupName) :
4 | RGBSettings(parameters, groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | VideoSettings::write()
12 | {
13 | if (!RGBSettings::write()) {
14 | return false;
15 | }
16 |
17 | writeParameter(m_groupName, "fps", m_parameters.fps);
18 |
19 | return true;
20 | }
21 |
22 |
23 | bool
24 | VideoSettings::read()
25 | {
26 | if (!RGBSettings::read()) {
27 | return false;
28 | }
29 |
30 | m_parameters.fps = readParameter(m_groupName, "fps", 30);
31 |
32 | return true;
33 | }
34 |
--------------------------------------------------------------------------------
/src/ros/thread/MergeBagsThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread used to merge a bag file using two input bag files
7 | // Makes use of the rosbag2_transport API for simplicity and performance
8 | class MergeBagsThread : public BasicThread {
9 | Q_OBJECT
10 | public:
11 | explicit
12 | MergeBagsThread(const Parameters::MergeBagsParameters& parameters,
13 | unsigned int numberOfThreads,
14 | QObject* parent = nullptr);
15 |
16 | void
17 | run() override;
18 |
19 | private:
20 | const Parameters::MergeBagsParameters& m_parameters;
21 |
22 | const unsigned int m_numberOfThreads;
23 | };
24 |
--------------------------------------------------------------------------------
/resources/icons/bag_tools_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/resources/icons/bag_tools_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/src/utils/VideoEncoder.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | // OpenCV video encoder used to write videos
6 | class VideoEncoder {
7 | public:
8 | explicit
9 | VideoEncoder(int fourcc);
10 |
11 | bool
12 | setVideoWriter(const std::string& directory,
13 | int fps,
14 | int width,
15 | int height,
16 | bool useHardwareAcceleration,
17 | bool useBWImages);
18 |
19 | inline void
20 | writeImageToVideo(const cv::Mat& mat)
21 | {
22 | m_videoWriter.write(mat);
23 | }
24 |
25 | private:
26 | int m_fourcc;
27 | cv::VideoWriter m_videoWriter;
28 | };
29 |
--------------------------------------------------------------------------------
/src/ui/settings/input/RGBSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "RGBSettings.hpp"
2 |
3 | RGBSettings::RGBSettings(Parameters::RGBParameters& parameters, const QString& groupName) :
4 | AdvancedSettings(parameters, groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | RGBSettings::write()
12 | {
13 | if (!AdvancedSettings::write()) {
14 | return false;
15 | }
16 |
17 | writeParameter(m_groupName, "switch_red_blue", m_parameters.exchangeRedBlueValues);
18 |
19 | return true;
20 | }
21 |
22 |
23 | bool
24 | RGBSettings::read()
25 | {
26 | if (!AdvancedSettings::read()) {
27 | return false;
28 | }
29 |
30 | m_parameters.exchangeRedBlueValues = readParameter(m_groupName, "switch_red_blue", false);
31 |
32 | return true;
33 | }
34 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | ros2_utils_tool
4 | 0.14.0
5 | The ros2_utils_tool package
6 |
7 | maxime
8 | EUPLv1.2
9 |
10 | cv_bridge
11 | geometry_msgs
12 | pcl_conversions
13 | rclcpp
14 | rosbag2_cpp
15 | rosbag2_transport
16 | sensor_msgs
17 | tf2_msgs
18 | tf2_ros
19 |
20 | catch_ros2
21 |
22 | ament_cmake
23 |
24 |
25 | ament_cmake
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/ros/thread/SendTF2Thread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | #include "rclcpp/rclcpp.hpp"
7 | #include "tf2_ros/transform_broadcaster.h"
8 |
9 | // Thread used to publish transformations. Runs using a ROS node to enable publishing
10 | class SendTF2Thread : public BasicThread {
11 | Q_OBJECT
12 |
13 | public:
14 | explicit
15 | SendTF2Thread(const Parameters::SendTF2Parameters& parameters,
16 | QObject* parent = nullptr);
17 |
18 | void
19 | run() override;
20 |
21 | private:
22 | std::shared_ptr m_node;
23 | std::shared_ptr m_broadcaster;
24 |
25 | const Parameters::SendTF2Parameters& m_parameters;
26 |
27 | static constexpr int PROGRESS = 0;
28 | };
29 |
--------------------------------------------------------------------------------
/src/ui/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(InputWidgets)
2 | add_subdirectory(HelperWidgets)
3 | add_subdirectory(settings)
4 |
5 | add_library (rt_ui INTERFACE)
6 |
7 | target_include_directories (rt_ui
8 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
9 | )
10 |
11 | target_sources(rt_ui INTERFACE
12 | ${CMAKE_CURRENT_LIST_DIR}/MainWindow.cpp
13 | ${CMAKE_CURRENT_LIST_DIR}/MainWindow.hpp
14 | ${CMAKE_CURRENT_LIST_DIR}/ProgressWidget.cpp
15 | ${CMAKE_CURRENT_LIST_DIR}/ProgressWidget.hpp
16 | ${CMAKE_CURRENT_LIST_DIR}/SettingsDialog.cpp
17 | ${CMAKE_CURRENT_LIST_DIR}/SettingsDialog.hpp
18 | ${CMAKE_CURRENT_LIST_DIR}/StartWidget.cpp
19 | ${CMAKE_CURRENT_LIST_DIR}/StartWidget.hpp
20 | )
21 |
22 | target_link_libraries(rt_ui
23 | INTERFACE Qt::Widgets rt_all_threads rt_input_widgets rt_settings rt_utils
24 | )
25 |
--------------------------------------------------------------------------------
/src/ui/settings/input/PCDsToBagSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "PCDsToBagSettings.hpp"
2 |
3 | PCDsToBagSettings::PCDsToBagSettings(Parameters::PCDsToBagParameters& parameters,
4 | const QString& groupName) :
5 | AdvancedSettings(parameters, groupName), m_parameters(parameters)
6 | {
7 | read();
8 | }
9 |
10 |
11 | bool
12 | PCDsToBagSettings::write()
13 | {
14 | if (!AdvancedSettings::write()) {
15 | return false;
16 | }
17 |
18 | writeParameter(m_groupName, "rate", m_parameters.rate);
19 |
20 | return true;
21 | }
22 |
23 |
24 | bool
25 | PCDsToBagSettings::read()
26 | {
27 | if (!AdvancedSettings::read()) {
28 | return false;
29 | }
30 |
31 | m_parameters.rate = readParameter(m_groupName, "rate", 5);
32 |
33 | return true;
34 | }
35 |
--------------------------------------------------------------------------------
/src/ui/settings/input/VideoToBagSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "VideoToBagSettings.hpp"
2 |
3 | VideoToBagSettings::VideoToBagSettings(Parameters::VideoToBagParameters& parameters,
4 | const QString& groupName) :
5 | VideoSettings(parameters, groupName), m_parameters(parameters)
6 | {
7 | read();
8 | }
9 |
10 |
11 | bool
12 | VideoToBagSettings::write()
13 | {
14 | if (!VideoSettings::write()) {
15 | return false;
16 | }
17 |
18 | writeParameter(m_groupName, "custom_fps", m_parameters.useCustomFPS);
19 |
20 | return true;
21 | }
22 |
23 |
24 | bool
25 | VideoToBagSettings::read()
26 | {
27 | if (!VideoSettings::read()) {
28 | return false;
29 | }
30 |
31 | m_parameters.useCustomFPS = readParameter(m_groupName, "custom_fps", false);
32 |
33 | return true;
34 | }
35 |
--------------------------------------------------------------------------------
/src/ui/settings/input/BasicSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "BasicSettings.hpp"
2 |
3 | #include "DialogSettings.hpp"
4 |
5 | BasicSettings::BasicSettings(Parameters::BasicParameters& parameters, const QString& groupName) :
6 | GeneralSettings(groupName), m_parameters(parameters)
7 | {
8 | }
9 |
10 |
11 | bool
12 | BasicSettings::write()
13 | {
14 | if (!DialogSettings::getStaticParameter("save_parameters", false)) {
15 | return false;
16 | }
17 |
18 | writeParameter(m_groupName, "source_dir", m_parameters.sourceDirectory);
19 |
20 | return true;
21 | }
22 |
23 |
24 | bool
25 | BasicSettings::read()
26 | {
27 | if (!DialogSettings::getStaticParameter("save_parameters", false)) {
28 | return false;
29 | }
30 |
31 | m_parameters.sourceDirectory = readParameter(m_groupName, "source_dir", QString(""));
32 |
33 | return true;
34 | }
35 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/LowDiskSpaceWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class QLabel;
7 |
8 | // Used to display a warning icon of low disk space along with the available disk space
9 | class LowDiskSpaceWidget : public QWidget
10 | {
11 | Q_OBJECT
12 |
13 | public:
14 | LowDiskSpaceWidget(QWidget* parent = 0);
15 |
16 | public:
17 | void
18 | setVisibility(const QString& path);
19 |
20 | [[nodiscard]] bool
21 | isDiskSpaceSpaceSufficient()
22 | {
23 | return m_isDiskSpaceSufficient;
24 | }
25 |
26 | private:
27 | void
28 | setPixmapLabelIcon() const;
29 |
30 | bool
31 | event(QEvent *event);
32 |
33 | private:
34 | QPointer m_warningIconLabel;
35 | QPointer m_diskSpaceLabel;
36 |
37 | bool m_isDiskSpaceSufficient;
38 |
39 | static constexpr int ICON_SIZE = 25;
40 | };
41 |
--------------------------------------------------------------------------------
/src/utils/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library (rt_utils INTERFACE)
2 |
3 | target_include_directories (rt_utils
4 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
5 | )
6 |
7 | target_sources(rt_utils INTERFACE
8 | ${CMAKE_CURRENT_LIST_DIR}/NodeWrapper.hpp
9 | ${CMAKE_CURRENT_LIST_DIR}/Parameters.hpp
10 | ${CMAKE_CURRENT_LIST_DIR}/UtilsCLI.cpp
11 | ${CMAKE_CURRENT_LIST_DIR}/UtilsCLI.hpp
12 | ${CMAKE_CURRENT_LIST_DIR}/UtilsGeneral.cpp
13 | ${CMAKE_CURRENT_LIST_DIR}/UtilsGeneral.hpp
14 | ${CMAKE_CURRENT_LIST_DIR}/UtilsROS.cpp
15 | ${CMAKE_CURRENT_LIST_DIR}/UtilsROS.hpp
16 | ${CMAKE_CURRENT_LIST_DIR}/UtilsUI.cpp
17 | ${CMAKE_CURRENT_LIST_DIR}/UtilsUI.hpp
18 | ${CMAKE_CURRENT_LIST_DIR}/VideoEncoder.cpp
19 | ${CMAKE_CURRENT_LIST_DIR}/VideoEncoder.hpp
20 | )
21 |
22 | target_link_libraries(rt_utils
23 | INTERFACE ${OpenCV_LIBS} Qt::Widgets rt_settings tf2_ros::tf2_ros
24 | )
25 |
--------------------------------------------------------------------------------
/src/ros/thread/PublishImagesThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | #include "rclcpp/rclcpp.hpp"
7 | #include "sensor_msgs/msg/image.hpp"
8 |
9 | // Thread used to publish image sequences as a ROS topic
10 | // This thread also runs as a separate ROS node to enable the image messages publishing
11 | class PublishImagesThread : public BasicThread {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit
16 | PublishImagesThread(const Parameters::PublishParameters& parameters,
17 | QObject* parent = nullptr);
18 |
19 | void
20 | run() override;
21 |
22 | private:
23 | std::shared_ptr m_node;
24 | rclcpp::Publisher::SharedPtr m_publisher;
25 |
26 | const Parameters::PublishParameters& m_parameters;
27 |
28 | static constexpr int PROGRESS = 0;
29 | };
30 |
--------------------------------------------------------------------------------
/src/ui/settings/input/DeleteSourceSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "DeleteSourceSettings.hpp"
2 |
3 | DeleteSourceSettings::DeleteSourceSettings(Parameters::DeleteSourceParameters& parameters,
4 | const QString& groupName) :
5 | AdvancedSettings(parameters, groupName), m_parameters(parameters)
6 | {
7 | read();
8 | }
9 |
10 |
11 | bool
12 | DeleteSourceSettings::write()
13 | {
14 | if (!AdvancedSettings::write()) {
15 | return false;
16 | }
17 |
18 | writeParameter(m_groupName, "delete_source", m_parameters.deleteSource);
19 |
20 | return true;
21 | }
22 |
23 |
24 | bool
25 | DeleteSourceSettings::read()
26 | {
27 | if (!AdvancedSettings::read()) {
28 | return false;
29 | }
30 |
31 | m_parameters.deleteSource = readParameter(m_groupName, "delete_source", m_isDefaultValueTrue);
32 |
33 | return true;
34 | }
35 |
--------------------------------------------------------------------------------
/src/cli/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library (rt_cli INTERFACE)
2 |
3 | target_include_directories (rt_cli
4 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
5 | )
6 |
7 | target_sources(rt_cli INTERFACE
8 | ${CMAKE_CURRENT_LIST_DIR}/BagToImages.cpp
9 | ${CMAKE_CURRENT_LIST_DIR}/BagToPCDs.cpp
10 | ${CMAKE_CURRENT_LIST_DIR}/BagToVideo.cpp
11 | ${CMAKE_CURRENT_LIST_DIR}/CompressBag.cpp
12 | ${CMAKE_CURRENT_LIST_DIR}/DecompressBag.cpp
13 | ${CMAKE_CURRENT_LIST_DIR}/DummyBag.cpp
14 | ${CMAKE_CURRENT_LIST_DIR}/MergeBags.cpp
15 | ${CMAKE_CURRENT_LIST_DIR}/PCDsToBag.cpp
16 | ${CMAKE_CURRENT_LIST_DIR}/PublishImages.cpp
17 | ${CMAKE_CURRENT_LIST_DIR}/PublishVideo.cpp
18 | ${CMAKE_CURRENT_LIST_DIR}/SendTF2.cpp
19 | ${CMAKE_CURRENT_LIST_DIR}/TF2ToFile.cpp
20 | ${CMAKE_CURRENT_LIST_DIR}/VideoToBag.cpp
21 | )
22 |
23 | target_link_libraries(rt_cli
24 | INTERFACE Qt::Widgets rt_thread rt_utils
25 | )
26 |
--------------------------------------------------------------------------------
/src/ros/thread/BasicThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | // Basic thread class, overridden by custom classes
6 | class BasicThread : public QThread {
7 | Q_OBJECT
8 | public:
9 | explicit
10 | BasicThread(const QString& sourceDirectory,
11 | const QString& topicName,
12 | QObject* parent = nullptr);
13 |
14 | signals:
15 | void
16 | informOfGatheringData();
17 |
18 | // Update progress displayal in widget
19 | void
20 | progressChanged(const QString& progressString,
21 | int progress);
22 |
23 | void
24 | finished();
25 |
26 | void
27 | processing();
28 |
29 | // Might fail in some cases (CV instance opening failed, invalid input params...)
30 | void
31 | failed();
32 |
33 | protected:
34 | const std::string m_sourceDirectory;
35 | const std::string m_topicName;
36 | };
37 |
--------------------------------------------------------------------------------
/src/ros/thread/ChangeCompressionBagThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | // Thread used to write a compressed to an uncompressed file (or vice versa)
7 | // Makes use of the rosbag2_transport API for simplicity and performance
8 | class ChangeCompressionBagThread : public BasicThread {
9 | Q_OBJECT
10 | public:
11 | explicit
12 | ChangeCompressionBagThread(const Parameters::CompressBagParameters& parameters,
13 | int numberOfThreads,
14 | bool compress,
15 | QObject* parent = nullptr);
16 |
17 | void
18 | run() override;
19 |
20 | private:
21 | const Parameters::CompressBagParameters& m_parameters;
22 |
23 | int m_numberOfThreads;
24 | bool m_compress;
25 | };
26 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/BagToImagesWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BagToImagesSettings.hpp"
4 | #include "Parameters.hpp"
5 | #include "TopicComboBoxWidget.hpp"
6 |
7 | class QCheckBox;
8 | class QFormLayout;
9 | class QSlider;
10 |
11 | // The widget used to manage writing images out of a ROS bag
12 | class BagToImagesWidget : public TopicComboBoxWidget
13 | {
14 | Q_OBJECT
15 |
16 | public:
17 | BagToImagesWidget(Parameters::BagToImagesParameters& parameters,
18 | QWidget* parent = 0);
19 |
20 | private slots:
21 | void
22 | adjustWidgetsToChangedFormat(const QString& text);
23 |
24 | private:
25 | QPointer m_qualitySlider;
26 | QPointer m_optimizeOrBilevelCheckBox;
27 |
28 | QPointer m_advancedOptionsFormLayout;
29 |
30 | Parameters::BagToImagesParameters& m_parameters;
31 |
32 | BagToImagesSettings m_settings;
33 | };
34 |
--------------------------------------------------------------------------------
/src/ui/settings/input/CompressBagSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "CompressBagSettings.hpp"
2 |
3 | CompressBagSettings::CompressBagSettings(Parameters::CompressBagParameters& parameters,
4 | const QString& groupName) :
5 | DeleteSourceSettings(parameters, groupName), m_parameters(parameters)
6 | {
7 | setDefaultValueToTrue();
8 | read();
9 | }
10 |
11 |
12 | bool
13 | CompressBagSettings::write()
14 | {
15 | if (!DeleteSourceSettings::write()) {
16 | return false;
17 | }
18 |
19 | writeParameter(m_groupName, "compress_per_message", m_parameters.compressPerMessage);
20 |
21 | return true;
22 | }
23 |
24 |
25 | bool
26 | CompressBagSettings::read()
27 | {
28 | if (!DeleteSourceSettings::read()) {
29 | return false;
30 | }
31 |
32 | m_parameters.compressPerMessage = readParameter(m_groupName, "compress_per_message", false);
33 |
34 | return true;
35 | }
36 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/BagToVideoWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BagToVideoSettings.hpp"
4 | #include "Parameters.hpp"
5 | #include "TopicComboBoxWidget.hpp"
6 |
7 | #include
8 | #include
9 |
10 | class QCheckBox;
11 | class QComboBox;
12 | class QFormLayout;
13 |
14 | // Widget used to configure a video encoding out of a ros bag
15 | class BagToVideoWidget : public TopicComboBoxWidget
16 | {
17 | Q_OBJECT
18 |
19 | public:
20 | BagToVideoWidget(Parameters::BagToVideoParameters& parameters,
21 | QWidget* parent = 0);
22 |
23 | private slots:
24 | void
25 | formatComboBoxTextChanged(const QString& text);
26 |
27 | private:
28 | QPointer m_formatComboBox;
29 | QPointer m_advancedOptionsFormLayout;
30 | QPointer m_useLosslessCheckBox;
31 |
32 | Parameters::BagToVideoParameters& m_parameters;
33 |
34 | BagToVideoSettings m_settings;
35 | };
36 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/ChangeCompressionWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedInputWidget.hpp"
4 | #include "CompressBagSettings.hpp"
5 | #include "Parameters.hpp"
6 |
7 | // The widget used to manage compressing a bag file
8 | class ChangeCompressionWidget : public AdvancedInputWidget
9 | {
10 | Q_OBJECT
11 |
12 | public:
13 | ChangeCompressionWidget(Parameters::CompressBagParameters& parameters,
14 | bool compress,
15 | QWidget* parent = 0);
16 |
17 | private slots:
18 | void
19 | findSourceButtonPressed() override;
20 |
21 | void
22 | okButtonPressed() const override;
23 |
24 | private:
25 | [[nodiscard]] bool
26 | isBagFileValid(const QString& bagDirectory) const;
27 |
28 | private:
29 | Parameters::CompressBagParameters& m_parameters;
30 |
31 | CompressBagSettings m_settings;
32 |
33 | bool m_compress;
34 | };
35 |
--------------------------------------------------------------------------------
/.github/workflows/jazzy.yml:
--------------------------------------------------------------------------------
1 | name: jazzy
2 |
3 | on: [push]
4 |
5 | env:
6 | BUILD_TYPE: Release
7 |
8 | jobs:
9 | run_jazzy:
10 | runs-on: ubuntu-24.04
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 |
15 | - uses: ros-tooling/setup-ros@v0.7
16 | with:
17 | required-ros-distributions: jazzy
18 |
19 | - name: Install dependencies
20 | run: sudo apt update && sudo apt install -y qt6-base-dev ros-jazzy-catch-ros2
21 |
22 | - name: Create workspace and copy repo there
23 | run: mkdir -p ros2_ws/src/ros2_utils_tool && cp -r resources src test CMakeLists.txt package.xml ros2_ws/src/ros2_utils_tool
24 |
25 | - name: Source ROS and build ws
26 | run: cd ros2_ws && source /opt/ros/jazzy/setup.bash && colcon build --packages-select ros2_utils_tool
27 |
28 | - name: Source ws and run tests
29 | run: cd ros2_ws && source install/setup.bash && ros2 run ros2_utils_tool tool_tests
30 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/PCDsToBagWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "PCDsToBagSettings.hpp"
4 | #include "Parameters.hpp"
5 | #include "TopicComboBoxWidget.hpp"
6 |
7 | #include
8 | #include
9 |
10 | // Widget used to write pcd files to a ROS bag file
11 | class PCDsToBagWidget : public TopicComboBoxWidget
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | PCDsToBagWidget(Parameters::PCDsToBagParameters& parameters,
17 | bool usePredefinedTopicName,
18 | bool warnROS2NameConvention,
19 | QWidget* parent = 0);
20 |
21 | private slots:
22 | void
23 | findSourceButtonPressed() override;
24 |
25 | void
26 | okButtonPressed() const override;
27 |
28 | private:
29 | Parameters::PCDsToBagParameters& m_parameters;
30 |
31 | PCDsToBagSettings m_settings;
32 |
33 | const bool m_warnROS2NameConvention;
34 | };
35 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/TF2ToFileWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "TF2ToFileSettings.hpp"
4 | #include "TopicComboBoxWidget.hpp"
5 | #include "Parameters.hpp"
6 |
7 | #include
8 | #include
9 |
10 | class QComboBox;
11 | class QFormLayout;
12 | class QRadioButton;
13 |
14 | // Widget used to configure sending tf2 messages
15 | class TF2ToFileWidget : public TopicComboBoxWidget
16 | {
17 | Q_OBJECT
18 |
19 | public:
20 | TF2ToFileWidget(Parameters::TF2ToFileParameters& parameters,
21 | QWidget* parent = 0);
22 |
23 | void
24 | formatComboBoxTextChanged(bool switched);
25 |
26 | private:
27 | QPointer m_advancedOptionsFormLayout;
28 | QPointer m_compactRadioButton;
29 | QPointer m_indentedRadioButton;
30 | QPointer m_formatComboBox;
31 |
32 | Parameters::TF2ToFileParameters& m_parameters;
33 |
34 | TF2ToFileSettings m_settings;
35 | };
36 |
--------------------------------------------------------------------------------
/.github/workflows/humble.yml:
--------------------------------------------------------------------------------
1 | name: humble
2 |
3 | on: [push]
4 |
5 | env:
6 | BUILD_TYPE: Release
7 |
8 | jobs:
9 | run_humble:
10 | runs-on: ubuntu-22.04
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 |
15 | - uses: ros-tooling/setup-ros@v0.7
16 | with:
17 | required-ros-distributions: humble
18 |
19 | - name: Install dependencies
20 | run: sudo apt update && sudo apt install -y qt6-base-dev ros-humble-catch-ros2
21 |
22 | - name: Create workspace and copy repo there
23 | run: mkdir -p ros2_ws/src/ros2_utils_tool && cp -r resources src test CMakeLists.txt package.xml ros2_ws/src/ros2_utils_tool
24 |
25 | - name: Source ROS and build ws
26 | run: cd ros2_ws && source /opt/ros/humble/setup.bash && colcon build --packages-select ros2_utils_tool
27 |
28 | - name: Source ws and run tests
29 | run: cd ros2_ws && source install/setup.bash && ros2 run ros2_utils_tool tool_tests
30 |
--------------------------------------------------------------------------------
/.github/workflows/kilted.yml:
--------------------------------------------------------------------------------
1 | name: kilted
2 |
3 | on: [push]
4 |
5 | env:
6 | BUILD_TYPE: Release
7 |
8 | jobs:
9 | run_kilted:
10 | runs-on: ubuntu-24.04
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 |
15 | - uses: ros-tooling/setup-ros@v0.7
16 | with:
17 | required-ros-distributions: kilted
18 |
19 | - name: Install dependencies
20 | run: sudo apt update && sudo apt install -y qt6-base-dev ros-kilted-catch-ros2
21 |
22 | - name: Create workspace and copy repo there
23 | run: mkdir -p ros2_ws/src/ros2_utils_tool && cp -r resources src test CMakeLists.txt package.xml ros2_ws/src/ros2_utils_tool
24 |
25 | - name: Source ROS and build ws
26 | run: cd ros2_ws && source /opt/ros/kilted/setup.bash && colcon build --packages-select ros2_utils_tool
27 |
28 | - name: Source ws and run tests
29 | run: cd ros2_ws && source install/setup.bash && ros2 run ros2_utils_tool tool_tests
30 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library (rt_helper_widgets INTERFACE)
2 |
3 | target_include_directories (rt_helper_widgets
4 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
5 | )
6 |
7 | target_sources(rt_helper_widgets INTERFACE
8 | ${CMAKE_CURRENT_LIST_DIR}/BasicBagWidget.cpp
9 | ${CMAKE_CURRENT_LIST_DIR}/BasicBagWidget.hpp
10 | ${CMAKE_CURRENT_LIST_DIR}/LowDiskSpaceWidget.cpp
11 | ${CMAKE_CURRENT_LIST_DIR}/LowDiskSpaceWidget.hpp
12 | ${CMAKE_CURRENT_LIST_DIR}/TopicWidget.cpp
13 | ${CMAKE_CURRENT_LIST_DIR}/TopicWidget.hpp
14 | ${CMAKE_CURRENT_LIST_DIR}/TopicComboBoxWidget.cpp
15 | ${CMAKE_CURRENT_LIST_DIR}/TopicComboBoxWidget.hpp
16 | ${CMAKE_CURRENT_LIST_DIR}/MessageCountWidget.cpp
17 | ${CMAKE_CURRENT_LIST_DIR}/MessageCountWidget.hpp
18 | ${CMAKE_CURRENT_LIST_DIR}/TopicListingInputWidget.cpp
19 | ${CMAKE_CURRENT_LIST_DIR}/TopicListingInputWidget.hpp
20 | )
21 |
22 | target_link_libraries(rt_helper_widgets
23 | INTERFACE Qt::Widgets
24 | )
25 |
--------------------------------------------------------------------------------
/.github/workflows/rolling.yml:
--------------------------------------------------------------------------------
1 | name: rolling
2 |
3 | on: [push]
4 |
5 | env:
6 | BUILD_TYPE: Release
7 |
8 | jobs:
9 | run_rolling:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 |
15 | - uses: ros-tooling/setup-ros@v0.7
16 | with:
17 | required-ros-distributions: rolling
18 |
19 | - name: Install dependencies
20 | run: sudo apt update && sudo apt install -y qt6-base-dev ros-rolling-catch-ros2
21 |
22 | - name: Create workspace and copy repo there
23 | run: mkdir -p ros2_ws/src/ros2_utils_tool && cp -r resources src test CMakeLists.txt package.xml ros2_ws/src/ros2_utils_tool
24 |
25 | - name: Source ROS and build ws
26 | run: cd ros2_ws && source /opt/ros/rolling/setup.bash && colcon build --packages-select ros2_utils_tool
27 |
28 | - name: Source ws and run tests
29 | run: cd ros2_ws && source install/setup.bash && ros2 run ros2_utils_tool tool_tests
30 |
--------------------------------------------------------------------------------
/resources/icons/publishing_tools_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/resources/icons/publishing_tools_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/RecordBagWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Parameters.hpp"
4 | #include "RecordBagSettings.hpp"
5 | #include "TopicListingInputWidget.hpp"
6 |
7 | #include
8 |
9 | class QLineEdit;
10 | class QWidget;
11 |
12 | // Widget used to manage recording a bag file
13 | class RecordBagWidget : public TopicListingInputWidget
14 | {
15 | Q_OBJECT
16 |
17 | public:
18 | RecordBagWidget(Parameters::RecordBagParameters& parameters,
19 | QWidget* parent = 0);
20 |
21 | private slots:
22 | void
23 | removeLineEdit(int row);
24 |
25 | void
26 | createNewTopicLineEdit(const QString& topicName,
27 | int index);
28 |
29 | private:
30 | std::optional
31 | areTopicsValid() const override;
32 |
33 | private:
34 | QVector > m_topicLineEdits;
35 |
36 | Parameters::RecordBagParameters& m_parameters;
37 |
38 | RecordBagSettings m_settings;
39 | };
40 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/MessageCountWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | // Display the lower and upper messages which should be extracted out of an existing ROS bag
8 | // into a newly edited one
9 | class MessageCountWidget : public QWidget
10 | {
11 | Q_OBJECT
12 |
13 | public:
14 | MessageCountWidget(int minimum,
15 | int maximum,
16 | int currentMaximumValue,
17 | QWidget* parent = 0);
18 |
19 | signals:
20 | void
21 | lowerValueChanged(int value);
22 |
23 | void
24 | upperValueChanged(int value);
25 |
26 | public:
27 | int
28 | getLowerValue() const
29 | {
30 | return m_lowerBox->value();
31 | }
32 |
33 | int
34 | getHigherValue() const
35 | {
36 | return m_upperBox->value();
37 | }
38 |
39 | private:
40 | QPointer m_lowerBox;
41 | QPointer m_upperBox;
42 | };
43 |
--------------------------------------------------------------------------------
/test/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | find_package(catch_ros2 REQUIRED)
2 |
3 | add_executable(tool_tests
4 | ${CMAKE_CURRENT_LIST_DIR}/main.cpp
5 |
6 | ${CMAKE_CURRENT_LIST_DIR}/ros/threads/ThreadsTest.cpp
7 |
8 | ${CMAKE_CURRENT_LIST_DIR}/ui/settings/SettingsTest.cpp
9 |
10 | ${CMAKE_CURRENT_LIST_DIR}/utils/UtilsCLITest.cpp
11 | ${CMAKE_CURRENT_LIST_DIR}/utils/UtilsROSTest.cpp
12 | ${CMAKE_CURRENT_LIST_DIR}/utils/UtilsUITest.cpp
13 | )
14 |
15 | target_link_libraries(tool_tests
16 | rclcpp::rclcpp rosbag2_cpp::rosbag2_cpp rosbag2_transport::rosbag2_transport
17 | cv_bridge::cv_bridge ${geometry_msgs_TARGETS} ${sensor_msgs_TARGETS} ${std_msgs_TARGETS} ${tf2_msgs_TARGETS}
18 | catch_ros2::catch_ros2_with_main Qt::Widgets rt_all_threads rt_settings_input rt_utils
19 | )
20 |
21 | if($ENV{ROS_DISTRO} STREQUAL "kilted")
22 | target_link_libraries(tool_tests
23 | pcl_conversions::pcl_conversions
24 | )
25 | else()
26 | ament_target_dependencies(tool_tests pcl_conversions)
27 | endif()
28 |
29 |
--------------------------------------------------------------------------------
/src/ros/thread/PublishVideoThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | #include "rclcpp/rclcpp.hpp"
7 | #include "sensor_msgs/msg/image.hpp"
8 |
9 | // Thread used to publish a video as ROS topic
10 | // This thread also runs as a separate ROS node to enable the image messages publishing
11 | class PublishVideoThread : public BasicThread {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit
16 | PublishVideoThread(const Parameters::PublishParameters& parameters,
17 | bool useHardwareAcceleration,
18 | QObject* parent = nullptr);
19 |
20 | void
21 | run() override;
22 |
23 | private:
24 | std::shared_ptr m_node;
25 | rclcpp::Publisher::SharedPtr m_publisher;
26 |
27 | const Parameters::PublishParameters& m_parameters;
28 |
29 | const bool m_useHardwareAcceleration;
30 |
31 | static constexpr int PROGRESS = 0;
32 | };
33 |
--------------------------------------------------------------------------------
/src/ui/settings/input/TF2ToFileSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "TF2ToFileSettings.hpp"
2 |
3 | TF2ToFileSettings::TF2ToFileSettings(Parameters::TF2ToFileParameters& parameters,
4 | const QString& groupName) :
5 | AdvancedSettings(parameters, groupName), m_parameters(parameters)
6 | {
7 | read();
8 | }
9 |
10 |
11 | bool
12 | TF2ToFileSettings::write()
13 | {
14 | if (!AdvancedSettings::write()) {
15 | return false;
16 | }
17 |
18 | writeParameter(m_groupName, "keep_timestamps", m_parameters.keepTimestamps);
19 | writeParameter(m_groupName, "compact_output", m_parameters.compactOutput);
20 |
21 | return true;
22 | }
23 |
24 |
25 | bool
26 | TF2ToFileSettings::read()
27 | {
28 | if (!AdvancedSettings::read()) {
29 | return false;
30 | }
31 |
32 | m_parameters.keepTimestamps = readParameter(m_groupName, "keep_timestamps", true);
33 | m_parameters.compactOutput = readParameter(m_groupName, "compact_output", true);
34 |
35 | return true;
36 | }
37 |
--------------------------------------------------------------------------------
/resources/icons/bag_info_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/resources/icons/bag_info_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/TopicComboBoxWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedInputWidget.hpp"
4 |
5 | class QComboBox;
6 |
7 | // Augments the advanced input widget with an additional topic combo box
8 | class TopicComboBoxWidget : public AdvancedInputWidget
9 | {
10 | Q_OBJECT
11 |
12 | public:
13 | TopicComboBoxWidget(Parameters::AdvancedParameters& parameters,
14 | const QString& headerText,
15 | const QString& iconPath,
16 | const QString& sourceFormLayoutName,
17 | const QString& targetFormLayoutName,
18 | const QString& settingsIdentifier,
19 | int outputFormat,
20 | QWidget* parent = 0);
21 |
22 | protected slots:
23 | void
24 | findSourceButtonPressed() override;
25 |
26 | protected:
27 | QPointer m_topicNameComboBox;
28 | };
29 |
--------------------------------------------------------------------------------
/src/ui/settings/input/BagToVideoSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "BagToVideoSettings.hpp"
2 |
3 | BagToVideoSettings::BagToVideoSettings(Parameters::BagToVideoParameters& parameters, const QString& groupName) :
4 | VideoSettings(parameters, groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | BagToVideoSettings::write()
12 | {
13 | if (!VideoSettings::write()) {
14 | return false;
15 | }
16 |
17 | writeParameter(m_groupName, "format", m_parameters.format);
18 | writeParameter(m_groupName, "bw_images", m_parameters.useBWImages);
19 | writeParameter(m_groupName, "lossless_images", m_parameters.lossless);
20 |
21 | return true;
22 | }
23 |
24 |
25 | bool
26 | BagToVideoSettings::read()
27 | {
28 | if (!VideoSettings::read()) {
29 | return false;
30 | }
31 |
32 | m_parameters.format = readParameter(m_groupName, "format", QString("mp4"));
33 | m_parameters.useBWImages = readParameter(m_groupName, "bw_images", false);
34 | m_parameters.lossless = readParameter(m_groupName, "lossless_images", false);
35 |
36 | return true;
37 | }
38 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/TopicWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | class QToolButton;
8 |
9 | // Small helper widget used to select a topic type for the ROS bag dummy creation
10 | class TopicWidget : public QWidget
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | TopicWidget(bool isDummyWidget,
16 | bool addRemoveButton,
17 | const QString& topicTypeText = "",
18 | const QString& topicNameText = "",
19 | QWidget* parent = 0);
20 |
21 | const QString
22 | getTopicName() const
23 | {
24 | return m_topicNameLineEdit->text();
25 | }
26 |
27 | signals:
28 | void
29 | topicTypeChanged(QString type);
30 |
31 | void
32 | topicNameChanged(QString name);
33 |
34 | void
35 | topicRemoveButtonClicked();
36 |
37 | private:
38 | void
39 | setPixmapLabelIcon() const;
40 |
41 | bool
42 | event(QEvent *event);
43 |
44 | private:
45 | QPointer m_topicNameLineEdit;
46 | QPointer m_removeTopicButton;
47 | };
48 |
--------------------------------------------------------------------------------
/src/ui/settings/input/AdvancedSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "AdvancedSettings.hpp"
2 |
3 | AdvancedSettings::AdvancedSettings(Parameters::AdvancedParameters& parameters, const QString& groupName) :
4 | BasicSettings(parameters, groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | AdvancedSettings::write()
12 | {
13 | if (!BasicSettings::write()) {
14 | return false;
15 | }
16 |
17 | writeParameter(m_groupName, "target_dir", m_parameters.targetDirectory);
18 | writeParameter(m_groupName, "topic_name", m_parameters.topicName);
19 | writeParameter(m_groupName, "show_advanced", m_parameters.showAdvancedOptions);
20 |
21 | return true;
22 | }
23 |
24 |
25 | bool
26 | AdvancedSettings::read()
27 | {
28 | if (!BasicSettings::read()) {
29 | return false;
30 | }
31 |
32 | m_parameters.targetDirectory = readParameter(m_groupName, "target_dir", QString(""));
33 | m_parameters.topicName = readParameter(m_groupName, "topic_name", QString(""));
34 | m_parameters.showAdvancedOptions = readParameter(m_groupName, "show_advanced", false);
35 |
36 | return true;
37 | }
38 |
--------------------------------------------------------------------------------
/src/ui/ProgressWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Parameters.hpp"
4 | #include "UtilsUI.hpp"
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | class BasicThread;
11 |
12 | // Base widget showing overall progress
13 | // The progress widget will access the main thread used to perform the corresponding operation.
14 | // If the user presses the Cancel button, the thread will be cancelled and we'll return to the main window.
15 | class ProgressWidget : public QWidget
16 | {
17 | Q_OBJECT
18 |
19 | public:
20 | ProgressWidget(const QString& headerLabelText,
21 | Parameters::BasicParameters& parameters,
22 | const Utils::UI::TOOL_ID threadTypeId,
23 | QWidget* parent = 0);
24 |
25 | ~ProgressWidget();
26 |
27 | void
28 | startThread();
29 |
30 | signals:
31 | void
32 | progressStopped();
33 |
34 | void
35 | finished();
36 |
37 | private:
38 | bool
39 | event(QEvent *event) override;
40 |
41 | private:
42 | QPointer m_thread;
43 |
44 | QPointer m_movie;
45 | };
46 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/MergeBagsWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicBagWidget.hpp"
4 | #include "MergeBagsSettings.hpp"
5 | #include "Parameters.hpp"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | // Widget for editing a bag file
12 | class MergeBagsWidget : public BasicBagWidget
13 | {
14 | Q_OBJECT
15 | public:
16 | explicit
17 | MergeBagsWidget(Parameters::MergeBagsParameters& mergeBagParameters,
18 | QWidget* parent = 0);
19 |
20 | private slots:
21 | void
22 | findSourceButtonPressed() override;
23 |
24 | void
25 | createTopicTree(bool resetTopicsParameter);
26 |
27 | void
28 | itemCheckStateChanged(QTreeWidgetItem* item,
29 | int column) override;
30 |
31 | void
32 | okButtonPressed() const override;
33 |
34 | private:
35 | QPointer m_secondSourceLineEdit;
36 |
37 | QPointer m_sufficientSpaceLabel;
38 |
39 | Parameters::MergeBagsParameters& m_parameters;
40 |
41 | MergeBagsSettings m_settings;
42 |
43 | bool m_secondSourceButtonClicked { false };
44 | };
45 |
--------------------------------------------------------------------------------
/src/ui/settings/input/PublishSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "PublishSettings.hpp"
2 |
3 | PublishSettings::PublishSettings(Parameters::PublishParameters& parameters, const QString& groupName) :
4 | VideoSettings(parameters, groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | PublishSettings::write()
12 | {
13 | if (!VideoSettings::write()) {
14 | return false;
15 | }
16 |
17 | writeParameter(m_groupName, "loop", m_parameters.loop);
18 | writeParameter(m_groupName, "scale", m_parameters.scale);
19 | writeParameter(m_groupName, "width", m_parameters.width);
20 | writeParameter(m_groupName, "height", m_parameters.height);
21 |
22 | return true;
23 | }
24 |
25 |
26 | bool
27 | PublishSettings::read()
28 | {
29 | if (!VideoSettings::read()) {
30 | return false;
31 | }
32 |
33 | m_parameters.loop = readParameter(m_groupName, "loop", false);
34 | m_parameters.scale = readParameter(m_groupName, "scale", false);
35 | m_parameters.width = readParameter(m_groupName, "width", 1280);
36 | m_parameters.height = readParameter(m_groupName, "height", 720);
37 |
38 | return true;
39 | }
40 |
--------------------------------------------------------------------------------
/resources/icons/send_tf2_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
--------------------------------------------------------------------------------
/resources/icons/send_tf2_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/VideoToBagWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Parameters.hpp"
4 | #include "TopicComboBoxWidget.hpp"
5 | #include "VideoToBagSettings.hpp"
6 |
7 | #include
8 | #include
9 |
10 | class QFormLayout;
11 | class QSpinBox;
12 |
13 | // Widget used to write a video file to a ROS bag file
14 | class VideoToBagWidget : public TopicComboBoxWidget
15 | {
16 | Q_OBJECT
17 |
18 | public:
19 | VideoToBagWidget(Parameters::VideoToBagParameters& parameters,
20 | bool usePredefinedTopicName,
21 | bool warnROS2NameConvention,
22 | QWidget* parent = 0);
23 |
24 | private slots:
25 | void
26 | findSourceButtonPressed() override;
27 |
28 | void
29 | useCustomFPSCheckBoxPressed(int state);
30 |
31 | void
32 | okButtonPressed() const override;
33 |
34 | private:
35 | QPointer m_advancedOptionsFormLayout;
36 | QPointer m_fpsSpinBox;
37 |
38 | Parameters::VideoToBagParameters& m_parameters;
39 |
40 | VideoToBagSettings m_settings;
41 |
42 | const bool m_warnROS2NameConvention;
43 | };
44 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/BagToPCDsWidget.cpp:
--------------------------------------------------------------------------------
1 | #include "BagToPCDsWidget.hpp"
2 |
3 | #include "UtilsUI.hpp"
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | BagToPCDsWidget::BagToPCDsWidget(Parameters::AdvancedParameters& parameters, QWidget *parent) :
10 | TopicComboBoxWidget(parameters, "Bag to PCD Files", ":/icons/bag_to_pcd", "Bag File:", "PCDs Location:", "bag_to_pcds", OUTPUT_PCDS, parent),
11 | m_parameters(parameters), m_settings(parameters, "bag_to_pcds")
12 | {
13 | m_sourceLineEdit->setToolTip("The source bag file directory.");
14 | m_topicNameComboBox->setToolTip("The point cloud bag topic.\nIf the bag contains multiple point cloud topics, you can choose one of them.");
15 | m_targetLineEdit->setToolTip("The target point cloud files directory.");
16 |
17 | m_basicOptionsFormLayout->insertRow(1, "Topic Name:", m_topicNameComboBox);
18 |
19 | m_controlsLayout->addStretch();
20 |
21 | // Generally, only enable this if the source bag, topic name and target dir line edit contain text
22 | enableOkButton(!m_parameters.sourceDirectory.isEmpty() &&
23 | !m_topicNameComboBox->currentText().isEmpty() && !m_parameters.targetDirectory.isEmpty());
24 | }
25 |
--------------------------------------------------------------------------------
/resources/icons/topics_services_info_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
--------------------------------------------------------------------------------
/resources/icons/topics_services_info_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
--------------------------------------------------------------------------------
/resources/icons/edit_bag_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
30 |
--------------------------------------------------------------------------------
/resources/icons/edit_bag_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
30 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/PublishWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicInputWidget.hpp"
4 | #include "Parameters.hpp"
5 | #include "PublishSettings.hpp"
6 |
7 | #include
8 | #include
9 |
10 | class QFormLayout;
11 | class QLineEdit;
12 | class QSpinBox;
13 |
14 | // Widget used to configure publishing a video OR image sequence as ROS messages
15 | class PublishWidget : public BasicInputWidget
16 | {
17 | Q_OBJECT
18 |
19 | public:
20 | PublishWidget(Parameters::PublishParameters& parameters,
21 | bool usePredefinedTopicName,
22 | bool warnROS2NameConvention,
23 | bool publishVideo,
24 | QWidget* parent = 0);
25 |
26 | private slots:
27 | void
28 | searchButtonPressed();
29 |
30 | void
31 | scaleCheckBoxPressed(int state);
32 |
33 | void
34 | okButtonPressed() const;
35 |
36 | private:
37 | QPointer m_topicNameLineEdit;
38 | QPointer m_advancedOptionsFormLayout;
39 | QPointer m_widthSpinBox;
40 | QPointer m_heightSpinBox;
41 |
42 | Parameters::PublishParameters& m_parameters;
43 |
44 | PublishSettings m_settings;
45 |
46 | const bool m_warnROS2NameConvention;
47 | const bool m_publishVideo;
48 | };
49 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/SendTF2Widget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicInputWidget.hpp"
4 | #include "Parameters.hpp"
5 | #include "SendTF2Settings.hpp"
6 |
7 | #include
8 | #include
9 |
10 | class NodeWrapper;
11 |
12 | class QCheckBox;
13 | class QFormLayout;
14 | class QLabel;
15 | class QSpinBox;
16 | class QTimer;
17 |
18 | // Widget used to configure sending a TF2 message
19 | class SendTF2Widget : public BasicInputWidget
20 | {
21 | Q_OBJECT
22 |
23 | public:
24 | SendTF2Widget(Parameters::SendTF2Parameters& parameters,
25 | QWidget* parent = 0);
26 |
27 | private slots:
28 | void
29 | staticCheckBoxPressed(int state);
30 |
31 | void
32 | okButtonPressed() const;
33 |
34 | private:
35 | QPointer m_isStaticCheckBox;
36 | QPointer m_rateSpinBox;
37 | QPointer m_formLayout;
38 |
39 | QPointer m_transformSentLabel;
40 | QPointer m_timer;
41 |
42 | std::shared_ptr m_nodeWrapper;
43 |
44 | Parameters::SendTF2Parameters& m_parameters;
45 |
46 | SendTF2Settings m_settings;
47 |
48 | static constexpr double SPINBOX_LOWER_RANGE = -1000.0;
49 | static constexpr double SPINBOX_UPPER_RANGE = 1000.0;
50 | static constexpr int NUMBER_OF_DECIMALS = 5;
51 | static constexpr int LABEL_SHOWN_DURATION = 1500;
52 | };
53 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/EditBagWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicBagWidget.hpp"
4 | #include "EditBagSettings.hpp"
5 | #include "Parameters.hpp"
6 |
7 | #include
8 | #include
9 |
10 | class QCheckBox;
11 | class QLabel;
12 |
13 | // Widget for editing a bag file
14 | class EditBagWidget : public BasicBagWidget
15 | {
16 | Q_OBJECT
17 | public:
18 | explicit
19 | EditBagWidget(Parameters::EditBagParameters& parameters,
20 | bool warnROS2NameConvention,
21 | QWidget* parent = 0);
22 |
23 | private slots:
24 | void
25 | findSourceButtonPressed() override;
26 |
27 | void
28 | createTopicTree();
29 |
30 | void
31 | itemCheckStateChanged(QTreeWidgetItem* item,
32 | int column) override;
33 |
34 | void
35 | okButtonPressed() const override;
36 |
37 | private:
38 | QPointer m_editLabel;
39 | QPointer m_differentDirsLabel;
40 |
41 | QPointer m_updateTimestampsCheckBox;
42 |
43 | Parameters::EditBagParameters& m_parameters;
44 |
45 | EditBagSettings m_settings;
46 |
47 | const bool m_warnROS2NameConvention;
48 |
49 | static constexpr int COL_MESSAGE_COUNT = 3;
50 | static constexpr int COL_RENAMING = 4;
51 |
52 | static constexpr int BUFFER_SPACE = 200;
53 | };
54 |
--------------------------------------------------------------------------------
/src/ui/settings/input/BagToImagesSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "BagToImagesSettings.hpp"
2 |
3 | BagToImagesSettings::BagToImagesSettings(Parameters::BagToImagesParameters& parameters, const QString& groupName) :
4 | RGBSettings(parameters, groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | BagToImagesSettings::write()
12 | {
13 | if (!RGBSettings::write()) {
14 | return false;
15 | }
16 |
17 | writeParameter(m_groupName, "format", m_parameters.format);
18 | writeParameter(m_groupName, "quality", m_parameters.quality);
19 | writeParameter(m_groupName, "bw_images", m_parameters.useBWImages);
20 | writeParameter(m_groupName, "jpg_optimize", m_parameters.jpgOptimize);
21 | writeParameter(m_groupName, "png_bilevel", m_parameters.pngBilevel);
22 |
23 | return true;
24 | }
25 |
26 |
27 | bool
28 | BagToImagesSettings::read()
29 | {
30 | if (!RGBSettings::read()) {
31 | return false;
32 | }
33 |
34 | m_parameters.format = readParameter(m_groupName, "format", QString("jpg"));
35 | m_parameters.quality = readParameter(m_groupName, "quality", 8);
36 | m_parameters.useBWImages = readParameter(m_groupName, "bw_images", false);
37 | m_parameters.jpgOptimize = readParameter(m_groupName, "jpg_optimize", false);
38 | m_parameters.pngBilevel = readParameter(m_groupName, "png_bilevel", false);
39 |
40 | return true;
41 | }
42 |
--------------------------------------------------------------------------------
/resources/icons/dummy_bag_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/resources/icons/dummy_bag_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/resources/icons/bag_to_video_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/resources/icons/bag_to_video_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/resources/icons/video_to_bag_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/resources/icons/video_to_bag_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/resources/icons/compress_bag_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/resources/icons/compress_bag_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/resources/icons/decompress_bag_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/resources/icons/decompress_bag_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/MessageCountWidget.cpp:
--------------------------------------------------------------------------------
1 | #include "MessageCountWidget.hpp"
2 |
3 | #include
4 | #include
5 |
6 | MessageCountWidget::MessageCountWidget(int minimum, int maximum, int currentMaximumValue, QWidget *parent) :
7 | QWidget(parent)
8 | {
9 | m_lowerBox = new QSpinBox;
10 | // Start with a value of 0 instead one 1, most users should be able to handle that :-P
11 | m_lowerBox->setRange(0, maximum);
12 | m_lowerBox->setValue(minimum);
13 |
14 | m_upperBox = new QSpinBox;
15 | m_upperBox->setRange(1, maximum);
16 | m_upperBox->setValue(currentMaximumValue);
17 |
18 | auto* const rangeDifferenceLabel = new QLabel("-");
19 |
20 | auto* const mainLayout = new QHBoxLayout;
21 | mainLayout->addWidget(m_lowerBox);
22 | mainLayout->addStretch();
23 | mainLayout->addWidget(rangeDifferenceLabel);
24 | mainLayout->addStretch();
25 | mainLayout->addWidget(m_upperBox);
26 | // Set these margins because we are going to integrate the widget into existing widgets
27 | // where any extra widgets would create undesired extra space
28 | mainLayout->setContentsMargins(0, 0, 0, 0);
29 |
30 | setLayout(mainLayout);
31 |
32 | connect(m_lowerBox, QOverload::of(&QSpinBox::valueChanged), this, [this] (int value) {
33 | emit lowerValueChanged(value);
34 | });
35 | connect(m_upperBox, QOverload::of(&QSpinBox::valueChanged), this, [this] (int value) {
36 | emit upperValueChanged(value);
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/resources/icons/publish_video_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
27 |
--------------------------------------------------------------------------------
/resources/icons/publish_video_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
27 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/DummyBagWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "DummyBagSettings.hpp"
4 | #include "Parameters.hpp"
5 | #include "TopicListingInputWidget.hpp"
6 |
7 | #include
8 | #include
9 |
10 | class QSpinBox;
11 |
12 | // Widget used to manage creating a ROS bag with dummy data
13 | class DummyBagWidget : public TopicListingInputWidget
14 | {
15 | Q_OBJECT
16 |
17 | public:
18 | DummyBagWidget(Parameters::DummyBagParameters& parameters,
19 | bool warnROS2NameConvention,
20 | QWidget* parent = 0);
21 |
22 | private slots:
23 | void
24 | removeDummyTopicWidget(int row);
25 |
26 | void
27 | createNewDummyTopicWidget(const Parameters::DummyBagParameters::DummyBagTopic& topics,
28 | int index,
29 | bool isCtor = true);
30 |
31 | void
32 | useCustomRateCheckBoxPressed(int state);
33 |
34 | private:
35 | std::optional
36 | areTopicsValid() const override;
37 |
38 | private:
39 | QPointer m_rateSpinBox;
40 |
41 | Parameters::DummyBagParameters& m_parameters;
42 |
43 | DummyBagSettings m_settings;
44 |
45 | const bool m_warnROS2NameConvention;
46 |
47 | static constexpr int TOPIC_WIDGET_OFFSET_CTOR = 3;
48 | static constexpr int TOPIC_WIDGET_OFFSET = 4;
49 | static constexpr int MAXIMUM_NUMBER_OF_TOPICS = 5;
50 | };
51 |
--------------------------------------------------------------------------------
/resources/icons/gear_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/resources/icons/record_bag_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
--------------------------------------------------------------------------------
/resources/icons/record_bag_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
--------------------------------------------------------------------------------
/resources/icons/gear_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/src/ui/settings/DialogSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "DialogSettings.hpp"
2 |
3 | DialogSettings::DialogSettings(Parameters::DialogParameters& parameters, const QString& groupName) :
4 | GeneralSettings(groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | DialogSettings::write()
12 | {
13 | writeParameter(m_groupName, "max_threads", m_parameters.maxNumberOfThreads);
14 | writeParameter(m_groupName, "hw_acc", m_parameters.useHardwareAcceleration);
15 | writeParameter(m_groupName, "save_parameters", m_parameters.saveParameters);
16 | writeParameter(m_groupName, "predefined_topic_names", m_parameters.usePredefinedTopicNames);
17 | writeParameter(m_groupName, "warn_ros2_name_convention", m_parameters.warnROS2NameConvention);
18 | writeParameter(m_groupName, "warn_target_overwrite", m_parameters.warnTargetOverwrite);
19 | writeParameter(m_groupName, "warn_low_disk_space", m_parameters.warnLowDiskSpace);
20 |
21 | return true;
22 | }
23 |
24 |
25 | bool
26 | DialogSettings::read()
27 | {
28 | m_parameters.maxNumberOfThreads = readParameter(m_groupName, "max_threads", std::thread::hardware_concurrency());
29 | m_parameters.useHardwareAcceleration = readParameter(m_groupName, "hw_acc", false);
30 | m_parameters.saveParameters = readParameter(m_groupName, "save_parameters", false);
31 | m_parameters.usePredefinedTopicNames = readParameter(m_groupName, "predefined_topic_names", true);
32 | m_parameters.warnROS2NameConvention = readParameter(m_groupName, "warn_ros2_name_convention", false);
33 | m_parameters.warnTargetOverwrite = readParameter(m_groupName, "warn_target_overwrite", true);
34 | m_parameters.warnLowDiskSpace = readParameter(m_groupName, "warn_low_disk_space", true);
35 |
36 | return true;
37 | }
38 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/BasicBagWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedInputWidget.hpp"
4 | #include "DeleteSourceSettings.hpp"
5 | #include "Parameters.hpp"
6 |
7 | #include
8 | #include
9 |
10 | class QCheckBox;
11 | class QHBoxLayout;
12 | class QTreeWidget;
13 | class QTreeWidgetItem;
14 |
15 | // Widget for displaying bag contents for manipulation
16 | class BasicBagWidget : public AdvancedInputWidget
17 | {
18 | Q_OBJECT
19 | public:
20 | explicit
21 | BasicBagWidget(Parameters::DeleteSourceParameters& parameters,
22 | const QString& titleText,
23 | const QString& iconText,
24 | const QString& settingsIdentifierText,
25 | const int outputFormat,
26 | QWidget* parent = 0);
27 |
28 | protected slots:
29 | virtual void
30 | itemCheckStateChanged(QTreeWidgetItem* item,
31 | int column) = 0;
32 |
33 | protected:
34 | [[nodiscard]] bool
35 | areIOParametersValid(int topicSize,
36 | int topicSizeWithoutDuplicates,
37 | const QString& secondSourceParameter = QString()) const;
38 |
39 | protected:
40 | QPointer m_treeWidget;
41 |
42 | QPointer m_deleteSourceCheckBox;
43 |
44 | QPointer m_diskSpaceLayout;
45 |
46 | QPointer m_findTargetWidget;
47 |
48 | static constexpr int COL_CHECKBOXES = 0;
49 | static constexpr int COL_TOPIC_NAME = 1;
50 | static constexpr int COL_TOPIC_TYPE = 2;
51 |
52 | private:
53 | Parameters::DeleteSourceParameters& m_parameters;
54 |
55 | DeleteSourceSettings m_settings;
56 |
57 | bool m_isDiskSpaceSufficient{ true };
58 | };
59 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/TopicListingInputWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicInputWidget.hpp"
4 | #include "BasicSettings.hpp"
5 | #include "Parameters.hpp"
6 |
7 | #include
8 | #include
9 |
10 | #include
11 |
12 | class TopicWidget;
13 |
14 | class QFormLayout;
15 | class QHBoxLayout;
16 | class QLabel;
17 | class QToolButton;
18 | class QVBoxLayout;
19 |
20 | // A widget showing widgets to add and remove topics
21 | class TopicListingInputWidget : public BasicInputWidget
22 | {
23 | Q_OBJECT
24 | public:
25 | explicit
26 | TopicListingInputWidget(Parameters::BasicParameters& parameters,
27 | const QString& titleText,
28 | const QString& iconText,
29 | const QString& settingsIdentifierText,
30 | QWidget* parent = 0);
31 |
32 | protected slots:
33 | void
34 | sourceButtonPressed();
35 |
36 | void
37 | okButtonPressed() const;
38 |
39 | protected:
40 | virtual std::optional
41 | areTopicsValid() const = 0;
42 |
43 | // Have to overwrite this one because we are using more additional icons then just the top one
44 | void
45 | setPixmapLabelIcon() const;
46 |
47 | bool
48 | event(QEvent *event);
49 |
50 | protected:
51 | // A QFormLayout provides no easy access to layout labels, so we keep them
52 | // in an extra vector for renaming after a row was removed
53 | QVector > m_topicLabels;
54 | QVector > m_topicWidgets;
55 |
56 | QPointer m_addTopicButton;
57 |
58 | QPointer m_formLayout;
59 | QPointer m_topicButtonLayout;
60 | QPointer m_controlsLayout;
61 |
62 | int m_numberOfTopics = 0;
63 |
64 | private:
65 | Parameters::BasicParameters& m_parameters;
66 |
67 | BasicSettings m_settings;
68 | };
69 |
--------------------------------------------------------------------------------
/src/ui/settings/input/MergeBagsSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "MergeBagsSettings.hpp"
2 |
3 | MergeBagsSettings::MergeBagsSettings(Parameters::MergeBagsParameters& parameters,
4 | const QString& groupName) :
5 | DeleteSourceSettings(parameters, groupName), m_parameters(parameters)
6 | {
7 | read();
8 | }
9 |
10 |
11 | bool
12 | MergeBagsSettings::write()
13 | {
14 | if (!DeleteSourceSettings::write()) {
15 | return false;
16 | }
17 |
18 | QSettings settings;
19 | settings.beginGroup(m_groupName);
20 |
21 | settings.beginWriteArray("topics");
22 | for (auto i = 0; i < m_parameters.topics.size(); ++i) {
23 | settings.setArrayIndex(i);
24 | writeParameter(settings, "name", m_parameters.topics.at(i).name);
25 | writeParameter(settings, "dir", m_parameters.topics.at(i).bagDir);
26 | writeParameter(settings, "is_selected", m_parameters.topics.at(i).isSelected);
27 | }
28 | settings.endArray();
29 | settings.endGroup();
30 |
31 | writeParameter(m_groupName, "second_source", m_parameters.secondSourceDirectory);
32 |
33 | return true;
34 | }
35 |
36 |
37 | bool
38 | MergeBagsSettings::read()
39 | {
40 | if (!DeleteSourceSettings::read()) {
41 | return false;
42 | }
43 |
44 | QSettings settings;
45 | settings.beginGroup(m_groupName);
46 | m_parameters.topics.clear();
47 |
48 | const auto size = settings.beginReadArray("topics");
49 | for (auto i = 0; i < size; ++i) {
50 | settings.setArrayIndex(i);
51 | m_parameters.topics.append({ readParameter(settings, "name", QString("")), readParameter(settings, "dir", QString("")),
52 | readParameter(settings, "is_selected", false) });
53 | }
54 | settings.endArray();
55 | settings.endGroup();
56 |
57 | m_parameters.secondSourceDirectory = readParameter(m_groupName, "second_source", QString(""));
58 |
59 | return true;
60 | }
61 |
--------------------------------------------------------------------------------
/src/ui/settings/input/DummyBagSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "DummyBagSettings.hpp"
2 |
3 | DummyBagSettings::DummyBagSettings(Parameters::DummyBagParameters& parameters, const QString& groupName) :
4 | BasicSettings(parameters, groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | DummyBagSettings::write()
12 | {
13 | if (!BasicSettings::write()) {
14 | return false;
15 | }
16 |
17 | QSettings settings;
18 | settings.beginGroup(m_groupName);
19 |
20 | settings.beginWriteArray("topics");
21 | for (auto i = 0; i < m_parameters.topics.size(); ++i) {
22 | settings.setArrayIndex(i);
23 | writeParameter(settings, "type", m_parameters.topics.at(i).type);
24 | writeParameter(settings, "name", m_parameters.topics.at(i).name);
25 | }
26 | settings.endArray();
27 | settings.endGroup();
28 |
29 | writeParameter(m_groupName, "msg_count", m_parameters.messageCount);
30 | writeParameter(m_groupName, "rate", m_parameters.rate);
31 | writeParameter(m_groupName, "use_custom_rate", m_parameters.useCustomRate);
32 |
33 | return true;
34 | }
35 |
36 |
37 | bool
38 | DummyBagSettings::read()
39 | {
40 | if (!BasicSettings::read()) {
41 | return false;
42 | }
43 |
44 | QSettings settings;
45 |
46 | settings.beginGroup(m_groupName);
47 | m_parameters.topics.clear();
48 |
49 | const auto size = settings.beginReadArray("topics");
50 | for (auto i = 0; i < size; ++i) {
51 | settings.setArrayIndex(i);
52 | m_parameters.topics.append({ readParameter(settings, "type", QString("")), readParameter(settings, "name", QString("")) });
53 | }
54 | settings.endArray();
55 | settings.endGroup();
56 |
57 | m_parameters.messageCount = readParameter(m_groupName, "msg_count", 100);
58 | m_parameters.rate = readParameter(m_groupName, "rate", 10);
59 | m_parameters.useCustomRate = readParameter(m_groupName, "use_custom_rate", false);
60 |
61 | return true;
62 | }
63 |
--------------------------------------------------------------------------------
/src/ros/thread/MergeBagsThread.cpp:
--------------------------------------------------------------------------------
1 | #include "MergeBagsThread.hpp"
2 |
3 | #include "rosbag2_transport/bag_rewrite.hpp"
4 |
5 | #include
6 |
7 | MergeBagsThread::MergeBagsThread(const Parameters::MergeBagsParameters& parameters,
8 | unsigned int numberOfThreads, QObject* parent) :
9 | BasicThread(parameters.sourceDirectory, "", parent),
10 | m_parameters(parameters), m_numberOfThreads(numberOfThreads)
11 | {
12 | }
13 |
14 |
15 | void
16 | MergeBagsThread::run()
17 | {
18 | const auto targetDirectoryStd = m_parameters.targetDirectory.toStdString();
19 | if (std::filesystem::exists(targetDirectoryStd)) {
20 | std::filesystem::remove_all(targetDirectoryStd);
21 | }
22 |
23 | // Setup input parameters
24 | rosbag2_storage::StorageOptions inputStorageFirstBag;
25 | inputStorageFirstBag.uri = m_sourceDirectory;
26 | rosbag2_storage::StorageOptions inputStorageSecondBag;
27 | inputStorageSecondBag.uri = m_parameters.secondSourceDirectory.toStdString();
28 |
29 | // Output parameters
30 | rosbag2_storage::StorageOptions outputStorage;
31 | outputStorage.uri = targetDirectoryStd;
32 |
33 | rosbag2_transport::RecordOptions outputRecord;
34 | for (const auto& topic : m_parameters.topics) {
35 | if (!topic.isSelected) {
36 | continue;
37 | }
38 |
39 | outputRecord.topics.push_back(topic.name.toStdString());
40 | }
41 |
42 | std::vector > outputBags;
43 | outputBags.push_back({ outputStorage, outputRecord });
44 |
45 | emit processing();
46 | // Do merge
47 | rosbag2_transport::bag_rewrite({ inputStorageFirstBag, inputStorageSecondBag }, outputBags);
48 |
49 | if (m_parameters.deleteSource) {
50 | std::filesystem::remove_all(m_sourceDirectory);
51 | std::filesystem::remove_all(m_parameters.secondSourceDirectory.toStdString());
52 | }
53 | emit finished();
54 | }
55 |
--------------------------------------------------------------------------------
/resources/icons/main.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
--------------------------------------------------------------------------------
/src/ros/thread/SendTF2Thread.cpp:
--------------------------------------------------------------------------------
1 | #include "SendTF2Thread.hpp"
2 |
3 | SendTF2Thread::SendTF2Thread(const Parameters::SendTF2Parameters& parameters,
4 | QObject* parent) :
5 | BasicThread(parameters.sourceDirectory, "", parent),
6 | m_parameters(parameters)
7 | {
8 | m_node = std::make_shared("tf_node");
9 | m_broadcaster = std::make_shared(m_node);
10 | }
11 |
12 |
13 | void
14 | SendTF2Thread::run()
15 | {
16 | const int rate = ((1000 / (float) m_parameters.rate) * 1000);
17 | geometry_msgs::msg::TransformStamped transformStamped;
18 |
19 | // Send the transformation using the specified rate
20 | auto timer = m_node->create_wall_timer(std::chrono::microseconds(rate), [this, &transformStamped] {
21 | if (isInterruptionRequested()) {
22 | return;
23 | }
24 |
25 | transformStamped.header.stamp = m_node->get_clock()->now();
26 | transformStamped.header.frame_id = "world";
27 | transformStamped.child_frame_id = m_parameters.childFrameName.toStdString();
28 |
29 | transformStamped.transform.translation.x = m_parameters.translation[0];
30 | transformStamped.transform.translation.y = m_parameters.translation[1];
31 | transformStamped.transform.translation.z = m_parameters.translation[2];
32 |
33 | transformStamped.transform.rotation.x = m_parameters.rotation[0];
34 | transformStamped.transform.rotation.y = m_parameters.rotation[1];
35 | transformStamped.transform.rotation.z = m_parameters.rotation[2];
36 | transformStamped.transform.rotation.w = m_parameters.rotation[3];
37 |
38 | m_broadcaster->sendTransform(transformStamped);
39 | });
40 |
41 | auto executor = std::make_shared();
42 | executor->add_node(m_node);
43 |
44 | while (!isInterruptionRequested()) {
45 | executor->spin_once();
46 | }
47 |
48 | timer->cancel();
49 | emit finished();
50 | }
51 |
--------------------------------------------------------------------------------
/src/ui/settings/DialogSettings.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "GeneralSettings.hpp"
4 | #include "Parameters.hpp"
5 |
6 | template
7 | concept DialogSettingsParameter = std::same_as || std::same_as;
8 |
9 | // Settings modified from settings dialog
10 | class DialogSettings : public GeneralSettings {
11 | public:
12 | DialogSettings(Parameters::DialogParameters& parameters,
13 | const QString& groupName);
14 |
15 | // Make these static because we need to access and modify some values from many different places
16 | // without having to use the entire dialog parameter instance
17 | template
18 | requires DialogSettingsParameter
19 | static T
20 | getStaticParameter(const QString& identifier,
21 | T defaultValue)
22 | {
23 | QSettings settings;
24 | settings.beginGroup("dialog");
25 | T staticParameter;
26 |
27 | if constexpr (std::is_same_v) {
28 | staticParameter = settings.value(identifier).isValid() ? settings.value(identifier).toInt() : defaultValue;
29 | } else {
30 | staticParameter = settings.value(identifier).isValid() ? settings.value(identifier).toBool() : defaultValue;
31 | }
32 |
33 | settings.endGroup();
34 | return staticParameter;
35 | }
36 |
37 | template
38 | requires DialogSettingsParameter
39 | static void
40 | writeStaticParameter(const QString& identifier,
41 | T value)
42 | {
43 | QSettings settings;
44 | settings.beginGroup("dialog");
45 |
46 | if (settings.value(identifier).value() == value) {
47 | return;
48 | }
49 |
50 | settings.setValue(identifier, value);
51 | }
52 |
53 | bool
54 | write() override;
55 |
56 | private:
57 | bool
58 | read() override;
59 |
60 | private:
61 | Parameters::DialogParameters& m_parameters;
62 | };
63 |
--------------------------------------------------------------------------------
/resources/icons/merge_bags_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/resources/icons/merge_bags_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/src/ros/thread/ChangeCompressionBagThread.cpp:
--------------------------------------------------------------------------------
1 | #include "ChangeCompressionBagThread.hpp"
2 |
3 | #include "rosbag2_transport/bag_rewrite.hpp"
4 |
5 | #include
6 |
7 | ChangeCompressionBagThread::ChangeCompressionBagThread(const Parameters::CompressBagParameters& parameters,
8 | int numberOfThreads, bool compress, QObject* parent) :
9 | BasicThread(parameters.sourceDirectory, parameters.topicName, parent),
10 | m_parameters(parameters), m_numberOfThreads(numberOfThreads), m_compress(compress)
11 | {
12 | }
13 |
14 |
15 | void
16 | ChangeCompressionBagThread::run()
17 | {
18 | const auto targetDirectoryStd = m_parameters.targetDirectory.toStdString();
19 | if (std::filesystem::exists(targetDirectoryStd)) {
20 | std::filesystem::remove_all(targetDirectoryStd);
21 | }
22 |
23 | // Input params
24 | rosbag2_storage::StorageOptions inputStorage;
25 | inputStorage.uri = m_sourceDirectory;
26 |
27 | // Output params
28 | rosbag2_storage::StorageOptions outputStorage;
29 | outputStorage.uri = targetDirectoryStd;
30 |
31 | rosbag2_transport::RecordOptions outputRecord;
32 | #ifdef ROS_HUMBLE
33 | outputRecord.all = true;
34 | #else
35 | outputRecord.all_topics = true;
36 | #endif
37 | if (m_compress) {
38 | outputRecord.compression_format = "zstd";
39 | outputRecord.compression_mode = m_parameters.compressPerMessage ? "message" : "file";
40 | outputRecord.compression_threads = m_numberOfThreads;
41 | // Need to set this so no messages are dropped
42 | outputRecord.compression_queue_size = 0;
43 | }
44 |
45 | std::vector > outputBags;
46 | outputBags.push_back({ outputStorage, outputRecord });
47 |
48 | emit processing();
49 | // Main rewrite
50 | rosbag2_transport::bag_rewrite({ inputStorage }, outputBags);
51 |
52 | if (m_parameters.deleteSource) {
53 | std::filesystem::remove_all(m_sourceDirectory);
54 | }
55 | emit finished();
56 | }
57 |
--------------------------------------------------------------------------------
/src/ui/settings/input/RecordBagSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "RecordBagSettings.hpp"
2 |
3 | RecordBagSettings::RecordBagSettings(Parameters::RecordBagParameters& parameters, const QString& groupName) :
4 | BasicSettings(parameters, groupName), m_parameters(parameters)
5 | {
6 | read();
7 | }
8 |
9 |
10 | bool
11 | RecordBagSettings::write()
12 | {
13 | if (!BasicSettings::write()) {
14 | return false;
15 | }
16 |
17 | QSettings settings;
18 | settings.beginGroup(m_groupName);
19 |
20 | settings.beginWriteArray("topics");
21 | for (auto i = 0; i < m_parameters.topics.size(); ++i) {
22 | settings.setArrayIndex(i);
23 | writeParameter(settings, "name", m_parameters.topics.at(i));
24 | }
25 | settings.endArray();
26 | settings.endGroup();
27 |
28 | writeParameter(m_groupName, "all_topics", m_parameters.allTopics);
29 | writeParameter(m_groupName, "show_advanced", m_parameters.showAdvancedOptions);
30 | writeParameter(m_groupName, "include_hidden_topics", m_parameters.includeHiddenTopics);
31 | writeParameter(m_groupName, "include_unpublished_topics", m_parameters.includeUnpublishedTopics);
32 |
33 | return true;
34 | }
35 |
36 |
37 | bool
38 | RecordBagSettings::read()
39 | {
40 | if (!BasicSettings::read()) {
41 | return false;
42 | }
43 |
44 | QSettings settings;
45 |
46 | settings.beginGroup(m_groupName);
47 | m_parameters.topics.clear();
48 |
49 | const auto size = settings.beginReadArray("topics");
50 | for (auto i = 0; i < size; ++i) {
51 | settings.setArrayIndex(i);
52 | m_parameters.topics.append({ readParameter(settings, "name", QString("")) });
53 | }
54 | settings.endArray();
55 | settings.endGroup();
56 |
57 | m_parameters.allTopics = readParameter(m_groupName, "all_topics", true);
58 | m_parameters.showAdvancedOptions = readParameter(m_groupName, "show_advanced", false);
59 | m_parameters.includeHiddenTopics = readParameter(m_groupName, "include_hidden_topics", false);
60 | m_parameters.includeUnpublishedTopics = readParameter(m_groupName, "include_unpublished_topics", false);
61 |
62 | return true;
63 | }
64 |
--------------------------------------------------------------------------------
/src/ros/thread/RecordBagThread.cpp:
--------------------------------------------------------------------------------
1 | #include "RecordBagThread.hpp"
2 |
3 | #include "rclcpp/rclcpp.hpp"
4 | #include "rosbag2_transport/recorder.hpp"
5 |
6 | #include
7 |
8 | RecordBagThread::RecordBagThread(const Parameters::RecordBagParameters& parameters, QObject* parent) :
9 | BasicThread(parameters.sourceDirectory, "", parent), m_parameters(parameters)
10 | {
11 | }
12 |
13 |
14 | void
15 | RecordBagThread::run()
16 | {
17 | const auto targetDirectoryStd = m_parameters.sourceDirectory.toStdString();
18 | if (std::filesystem::exists(targetDirectoryStd)) {
19 | std::filesystem::remove_all(targetDirectoryStd);
20 | }
21 |
22 | rosbag2_storage::StorageOptions storageOptions;
23 | storageOptions.uri = targetDirectoryStd;
24 |
25 | rosbag2_transport::RecordOptions recordOptions;
26 | if (m_parameters.allTopics) {
27 | #ifdef ROS_HUMBLE
28 | recordOptions.all = true;
29 | #else
30 | recordOptions.all_topics = true;
31 | #endif
32 | } else {
33 | for (const auto& topic : m_parameters.topics) {
34 | recordOptions.topics.push_back(topic.toStdString());
35 | }
36 | }
37 | recordOptions.rmw_serialization_format = "cdr";
38 | recordOptions.include_hidden_topics = m_parameters.includeHiddenTopics;
39 | recordOptions.include_unpublished_topics = m_parameters.includeUnpublishedTopics;
40 |
41 | auto writer = std::make_unique();
42 | auto recorder = std::make_shared(std::move(writer), storageOptions, recordOptions);
43 |
44 | // Initialize recorder
45 | recorder->record();
46 | auto executor = std::make_shared();
47 | executor->add_node(recorder);
48 |
49 | // Need to spin it in an extra thread for writing messages (no idea why, though)
50 | auto spinThread = std::thread([executor] {
51 | executor->spin();
52 | });
53 |
54 | rclcpp::Rate rate(50);
55 | while (!isInterruptionRequested()) {
56 | rate.sleep();
57 | }
58 |
59 | recorder->stop();
60 | executor->cancel();
61 | spinThread.join();
62 |
63 | emit finished();
64 | }
65 |
--------------------------------------------------------------------------------
/src/ui/settings/input/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library (rt_settings_input INTERFACE)
2 |
3 | target_include_directories (rt_settings_input
4 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
5 | )
6 |
7 | target_sources(rt_settings_input INTERFACE
8 | ${CMAKE_CURRENT_LIST_DIR}/AdvancedSettings.cpp
9 | ${CMAKE_CURRENT_LIST_DIR}/AdvancedSettings.hpp
10 | ${CMAKE_CURRENT_LIST_DIR}/BagToImagesSettings.cpp
11 | ${CMAKE_CURRENT_LIST_DIR}/BagToImagesSettings.hpp
12 | ${CMAKE_CURRENT_LIST_DIR}/BagToVideoSettings.cpp
13 | ${CMAKE_CURRENT_LIST_DIR}/BagToVideoSettings.hpp
14 | ${CMAKE_CURRENT_LIST_DIR}/BasicSettings.cpp
15 | ${CMAKE_CURRENT_LIST_DIR}/BasicSettings.hpp
16 | ${CMAKE_CURRENT_LIST_DIR}/CompressBagSettings.cpp
17 | ${CMAKE_CURRENT_LIST_DIR}/CompressBagSettings.hpp
18 | ${CMAKE_CURRENT_LIST_DIR}/DeleteSourceSettings.cpp
19 | ${CMAKE_CURRENT_LIST_DIR}/DeleteSourceSettings.hpp
20 | ${CMAKE_CURRENT_LIST_DIR}/DummyBagSettings.cpp
21 | ${CMAKE_CURRENT_LIST_DIR}/DummyBagSettings.hpp
22 | ${CMAKE_CURRENT_LIST_DIR}/EditBagSettings.cpp
23 | ${CMAKE_CURRENT_LIST_DIR}/EditBagSettings.hpp
24 | ${CMAKE_CURRENT_LIST_DIR}/MergeBagsSettings.cpp
25 | ${CMAKE_CURRENT_LIST_DIR}/MergeBagsSettings.hpp
26 | ${CMAKE_CURRENT_LIST_DIR}/PCDsToBagSettings.cpp
27 | ${CMAKE_CURRENT_LIST_DIR}/PCDsToBagSettings.hpp
28 | ${CMAKE_CURRENT_LIST_DIR}/PublishSettings.cpp
29 | ${CMAKE_CURRENT_LIST_DIR}/PublishSettings.hpp
30 | ${CMAKE_CURRENT_LIST_DIR}/RecordBagSettings.cpp
31 | ${CMAKE_CURRENT_LIST_DIR}/RecordBagSettings.hpp
32 | ${CMAKE_CURRENT_LIST_DIR}/RGBSettings.cpp
33 | ${CMAKE_CURRENT_LIST_DIR}/RGBSettings.hpp
34 | ${CMAKE_CURRENT_LIST_DIR}/SendTF2Settings.cpp
35 | ${CMAKE_CURRENT_LIST_DIR}/SendTF2Settings.hpp
36 | ${CMAKE_CURRENT_LIST_DIR}/TF2ToFileSettings.cpp
37 | ${CMAKE_CURRENT_LIST_DIR}/TF2ToFileSettings.hpp
38 | ${CMAKE_CURRENT_LIST_DIR}/VideoSettings.cpp
39 | ${CMAKE_CURRENT_LIST_DIR}/VideoSettings.hpp
40 | ${CMAKE_CURRENT_LIST_DIR}/VideoToBagSettings.cpp
41 | ${CMAKE_CURRENT_LIST_DIR}/VideoToBagSettings.hpp
42 | )
43 |
44 | target_link_libraries(rt_settings_input
45 | INTERFACE Qt::Widgets rt_settings
46 | )
47 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library (rt_input_widgets INTERFACE)
2 |
3 | target_include_directories (rt_input_widgets
4 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
5 | )
6 |
7 | target_sources(rt_input_widgets INTERFACE
8 | ${CMAKE_CURRENT_LIST_DIR}/AdvancedInputWidget.cpp
9 | ${CMAKE_CURRENT_LIST_DIR}/AdvancedInputWidget.hpp
10 | ${CMAKE_CURRENT_LIST_DIR}/BagInfoWidget.cpp
11 | ${CMAKE_CURRENT_LIST_DIR}/BagInfoWidget.hpp
12 | ${CMAKE_CURRENT_LIST_DIR}/BagToPCDsWidget.cpp
13 | ${CMAKE_CURRENT_LIST_DIR}/BagToPCDsWidget.hpp
14 | ${CMAKE_CURRENT_LIST_DIR}/BagToImagesWidget.cpp
15 | ${CMAKE_CURRENT_LIST_DIR}/BagToImagesWidget.hpp
16 | ${CMAKE_CURRENT_LIST_DIR}/BagToVideoWidget.cpp
17 | ${CMAKE_CURRENT_LIST_DIR}/BagToVideoWidget.hpp
18 | ${CMAKE_CURRENT_LIST_DIR}/BasicInputWidget.cpp
19 | ${CMAKE_CURRENT_LIST_DIR}/BasicInputWidget.hpp
20 | ${CMAKE_CURRENT_LIST_DIR}/ChangeCompressionWidget.cpp
21 | ${CMAKE_CURRENT_LIST_DIR}/ChangeCompressionWidget.hpp
22 | ${CMAKE_CURRENT_LIST_DIR}/DummyBagWidget.cpp
23 | ${CMAKE_CURRENT_LIST_DIR}/DummyBagWidget.hpp
24 | ${CMAKE_CURRENT_LIST_DIR}/EditBagWidget.cpp
25 | ${CMAKE_CURRENT_LIST_DIR}/EditBagWidget.hpp
26 | ${CMAKE_CURRENT_LIST_DIR}/MergeBagsWidget.cpp
27 | ${CMAKE_CURRENT_LIST_DIR}/MergeBagsWidget.hpp
28 | ${CMAKE_CURRENT_LIST_DIR}/PCDsToBagWidget.cpp
29 | ${CMAKE_CURRENT_LIST_DIR}/PCDsToBagWidget.hpp
30 | ${CMAKE_CURRENT_LIST_DIR}/PublishWidget.cpp
31 | ${CMAKE_CURRENT_LIST_DIR}/PublishWidget.hpp
32 | ${CMAKE_CURRENT_LIST_DIR}/RecordBagWidget.cpp
33 | ${CMAKE_CURRENT_LIST_DIR}/RecordBagWidget.hpp
34 | ${CMAKE_CURRENT_LIST_DIR}/SendTF2Widget.cpp
35 | ${CMAKE_CURRENT_LIST_DIR}/SendTF2Widget.hpp
36 | ${CMAKE_CURRENT_LIST_DIR}/TF2ToFileWidget.cpp
37 | ${CMAKE_CURRENT_LIST_DIR}/TF2ToFileWidget.hpp
38 | ${CMAKE_CURRENT_LIST_DIR}/TopicsServicesInfoWidget.cpp
39 | ${CMAKE_CURRENT_LIST_DIR}/TopicsServicesInfoWidget.hpp
40 | ${CMAKE_CURRENT_LIST_DIR}/VideoToBagWidget.cpp
41 | ${CMAKE_CURRENT_LIST_DIR}/VideoToBagWidget.hpp
42 | )
43 |
44 | target_link_libraries(rt_input_widgets
45 | INTERFACE Qt::Widgets rt_helper_widgets rt_settings_input rt_utils
46 | )
47 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/LowDiskSpaceWidget.cpp:
--------------------------------------------------------------------------------
1 | #include "LowDiskSpaceWidget.hpp"
2 |
3 | #include "UtilsGeneral.hpp"
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | LowDiskSpaceWidget::LowDiskSpaceWidget(QWidget *parent) :
12 | QWidget(parent)
13 | {
14 | m_warningIconLabel = new QLabel;
15 | m_diskSpaceLabel = new QLabel;
16 |
17 | auto labelFont = m_diskSpaceLabel->font();
18 | labelFont.setBold(true);
19 | m_diskSpaceLabel->setFont(labelFont);
20 |
21 | auto palette = m_diskSpaceLabel->palette();
22 | palette.setColor(QPalette::WindowText, Qt::red);
23 | m_diskSpaceLabel->setPalette(palette);
24 |
25 | auto* const mainLayout = new QHBoxLayout;
26 | mainLayout->addWidget(m_warningIconLabel);
27 | mainLayout->addWidget(m_diskSpaceLabel);
28 | mainLayout->addStretch();
29 | // Will be integrated into other widgets, so remove the extra space
30 | mainLayout->setContentsMargins(0, 0, 0, 0);
31 | setLayout(mainLayout);
32 |
33 | setPixmapLabelIcon();
34 | }
35 |
36 |
37 | void
38 | LowDiskSpaceWidget::setVisibility(const QString& path)
39 | {
40 | const auto diskSpace = Utils::General::getAvailableDriveSpace(path);
41 | m_isDiskSpaceSufficient = diskSpace > Utils::General::MINIMUM_RECOMMENDED_DRIVE_SPACE;
42 |
43 | if (!m_isDiskSpaceSufficient) {
44 | m_diskSpaceLabel->setText("Free available space is only " + QString::number(diskSpace) + " GiB!");
45 | }
46 | m_warningIconLabel->setVisible(!m_isDiskSpaceSufficient);
47 | m_diskSpaceLabel->setVisible(!m_isDiskSpaceSufficient);
48 | }
49 |
50 |
51 | void
52 | LowDiskSpaceWidget::setPixmapLabelIcon() const
53 | {
54 | const auto visibility = m_warningIconLabel->isVisible();
55 |
56 | m_warningIconLabel->setPixmap(QIcon(":/icons/warning.svg").pixmap(QSize(ICON_SIZE, ICON_SIZE)));
57 | m_warningIconLabel->setVisible(visibility);
58 | }
59 |
60 |
61 | bool
62 | LowDiskSpaceWidget::event(QEvent *event)
63 | {
64 | [[unlikely]] if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::PaletteChange) {
65 | setPixmapLabelIcon();
66 | }
67 | return QWidget::event(event);
68 | }
69 |
--------------------------------------------------------------------------------
/resources/icons/tf2_to_file_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
43 |
--------------------------------------------------------------------------------
/resources/icons/tf2_to_file_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
43 |
--------------------------------------------------------------------------------
/src/ui/settings/input/SendTF2Settings.cpp:
--------------------------------------------------------------------------------
1 | #include "SendTF2Settings.hpp"
2 |
3 | SendTF2Settings::SendTF2Settings(Parameters::SendTF2Parameters& parameters,
4 | const QString& groupName) :
5 | BasicSettings(parameters, groupName), m_parameters(parameters)
6 | {
7 | read();
8 | }
9 |
10 |
11 | bool
12 | SendTF2Settings::write()
13 | {
14 | if (!BasicSettings::write()) {
15 | return false;
16 | }
17 |
18 | writeParameter(m_groupName, "translation_x", m_parameters.translation[0]);
19 | writeParameter(m_groupName, "translation_y", m_parameters.translation[1]);
20 | writeParameter(m_groupName, "translation_z", m_parameters.translation[2]);
21 | writeParameter(m_groupName, "rotation_x", m_parameters.rotation[0]);
22 | writeParameter(m_groupName, "rotation_y", m_parameters.rotation[1]);
23 | writeParameter(m_groupName, "rotation_z", m_parameters.rotation[2]);
24 | writeParameter(m_groupName, "rotation_w", m_parameters.rotation[3]);
25 | writeParameter(m_groupName, "name", m_parameters.childFrameName);
26 | writeParameter(m_groupName, "rate", m_parameters.rate);
27 | writeParameter(m_groupName, "is_static", m_parameters.isStatic);
28 |
29 | return true;
30 | }
31 |
32 |
33 | bool
34 | SendTF2Settings::read()
35 | {
36 | if (!BasicSettings::read()) {
37 | return false;
38 | }
39 |
40 | m_parameters.translation[0] = readParameter(m_groupName, "translation_x", 0.0);
41 | m_parameters.translation[1] = readParameter(m_groupName, "translation_y", 0.0);
42 | m_parameters.translation[2] = readParameter(m_groupName, "translation_z", 0.0);
43 | m_parameters.rotation[0] = readParameter(m_groupName, "rotation_x", 0.0);
44 | m_parameters.rotation[1] = readParameter(m_groupName, "rotation_y", 0.0);
45 | m_parameters.rotation[2] = readParameter(m_groupName, "rotation_z", 0.0);
46 | m_parameters.rotation[3] = readParameter(m_groupName, "rotation_w", 0.0);
47 | m_parameters.childFrameName = readParameter(m_groupName, "name", QString("tf_test"));
48 | m_parameters.rate = readParameter(m_groupName, "rate", 1);
49 | m_parameters.isStatic = readParameter(m_groupName, "is_static", true);
50 |
51 | return true;
52 | }
53 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/AdvancedInputWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AdvancedSettings.hpp"
4 | #include "BasicInputWidget.hpp"
5 | #include "Parameters.hpp"
6 |
7 | class QFormLayout;
8 | class QLineEdit;
9 | class QVBoxLayout;
10 |
11 | // Derived from basic input, provides functions to search for an input bag or a target directory
12 | class AdvancedInputWidget : public BasicInputWidget
13 | {
14 | Q_OBJECT
15 |
16 | public:
17 | AdvancedInputWidget(Parameters::AdvancedParameters& parameters,
18 | const QString& headerText,
19 | const QString& iconPath,
20 | const QString& sourceFormLayoutName,
21 | const QString& targetFormLayoutName,
22 | const QString& settingsIdentifier,
23 | int outputFormat,
24 | QWidget* parent = 0);
25 |
26 | protected slots:
27 | virtual void
28 | findSourceButtonPressed();
29 |
30 | void
31 | findTargetButtonPressed();
32 |
33 | virtual void
34 | okButtonPressed() const;
35 |
36 | void
37 | setFileFormat(const QString& fileFormat)
38 | {
39 | m_fileFormat = fileFormat;
40 | }
41 |
42 | protected:
43 | void
44 | fillTargetLineEdit();
45 |
46 | protected:
47 | QPointer m_targetLineEdit;
48 |
49 | QPointer m_basicOptionsFormLayout;
50 | QPointer m_controlsLayout;
51 |
52 | Parameters::AdvancedParameters& m_parameters;
53 |
54 | AdvancedSettings m_settings;
55 |
56 | const int m_outputFormat;
57 |
58 | static constexpr int OUTPUT_VIDEO = 0;
59 | static constexpr int OUTPUT_IMAGES = 1;
60 | static constexpr int OUTPUT_PCDS = 2;
61 | static constexpr int OUTPUT_TF_TO_FILE = 3;
62 | static constexpr int OUTPUT_BAG = 4;
63 | static constexpr int OUTPUT_BAG_EDITED = 5;
64 | static constexpr int OUTPUT_BAG_MERGED = 6;
65 | static constexpr int OUTPUT_BAG_COMPRESSED = 7;
66 | static constexpr int OUTPUT_BAG_DECOMPRESSED = 8;
67 |
68 | private:
69 | QString m_fileFormat;
70 | };
71 |
--------------------------------------------------------------------------------
/src/ros/thread/PCDsToBagThread.cpp:
--------------------------------------------------------------------------------
1 | #include "PCDsToBagThread.hpp"
2 |
3 | #include
4 |
5 | #include "rosbag2_cpp/writer.hpp"
6 |
7 | #include "sensor_msgs/msg/point_cloud.hpp"
8 |
9 | #include
10 |
11 | PCDsToBagThread::PCDsToBagThread(const Parameters::PCDsToBagParameters& parameters, QObject* parent) :
12 | BasicThread(parameters.sourceDirectory, parameters.topicName, parent),
13 | m_parameters(parameters)
14 | {
15 | }
16 |
17 |
18 | void
19 | PCDsToBagThread::run()
20 | {
21 | const auto targetDirectoryStd = m_parameters.targetDirectory.toStdString();
22 | if (std::filesystem::exists(targetDirectoryStd)) {
23 | std::filesystem::remove_all(targetDirectoryStd);
24 | }
25 |
26 | emit informOfGatheringData();
27 |
28 | auto frameCount = 0;
29 | // It is faster to first store all valid pcd file paths and then iterate over those
30 | std::set sortedPCDsSet;
31 | for (auto const& entry : std::filesystem::directory_iterator(m_sourceDirectory)) {
32 | if (entry.path().extension() != ".pcd") {
33 | continue;
34 | }
35 |
36 | sortedPCDsSet.insert(entry.path());
37 | frameCount++;
38 | }
39 |
40 | pcl::PCLPointCloud2 cloud;
41 | sensor_msgs::msg::PointCloud2 message;
42 | pcl::PCDReader reader;
43 |
44 | auto writer = std::make_unique();
45 | writer->open(targetDirectoryStd);
46 |
47 | auto iterationCount = 1;
48 | auto timeStamp = rclcpp::Clock(RCL_ROS_TIME).now();
49 | const auto duration = rclcpp::Duration::from_seconds(1.0f / (float) m_parameters.rate);
50 |
51 | while (true) {
52 | if (isInterruptionRequested() || sortedPCDsSet.empty()) {
53 | break;
54 | }
55 |
56 | // Read pcd from file
57 | reader.read(*sortedPCDsSet.begin(), cloud);
58 | timeStamp += duration;
59 | // Write
60 | pcl_conversions::fromPCL(cloud, message);
61 | writer->write(message, m_topicName, timeStamp);
62 |
63 | sortedPCDsSet.erase(sortedPCDsSet.begin());
64 | emit progressChanged("Writing pcd file " + QString::number(iterationCount) + " of " + QString::number(frameCount) + "...",
65 | ((float) iterationCount / (float) frameCount) * 100);
66 | iterationCount++;
67 | }
68 |
69 | emit finished();
70 | }
71 |
--------------------------------------------------------------------------------
/src/ui/settings/input/EditBagSettings.cpp:
--------------------------------------------------------------------------------
1 | #include "EditBagSettings.hpp"
2 |
3 | EditBagSettings::EditBagSettings(Parameters::EditBagParameters& parameters,
4 | const QString& groupName) :
5 | DeleteSourceSettings(parameters, groupName), m_parameters(parameters)
6 | {
7 | read();
8 | }
9 |
10 |
11 | bool
12 | EditBagSettings::write()
13 | {
14 | if (!DeleteSourceSettings::write()) {
15 | return false;
16 | }
17 |
18 | QSettings settings;
19 | settings.beginGroup(m_groupName);
20 |
21 | settings.beginWriteArray("topics");
22 | for (auto i = 0; i < m_parameters.topics.size(); ++i) {
23 | settings.setArrayIndex(i);
24 | writeParameter(settings, "renamed_name", m_parameters.topics.at(i).renamedTopicName);
25 | writeParameter(settings, "original_name", m_parameters.topics.at(i).originalTopicName);
26 | writeParameter(settings, "is_selected", m_parameters.topics.at(i).isSelected);
27 | writeParameter(settings, "lower_boundary", m_parameters.topics.at(i).lowerBoundary);
28 | writeParameter(settings, "upper_boundary", m_parameters.topics.at(i).upperBoundary);
29 | }
30 | settings.endArray();
31 | settings.endGroup();
32 |
33 | writeParameter(m_groupName, "update_timestamps", m_parameters.updateTimestamps);
34 |
35 | return true;
36 | }
37 |
38 |
39 | bool
40 | EditBagSettings::read()
41 | {
42 | if (!DeleteSourceSettings::read()) {
43 | return false;
44 | }
45 |
46 | QSettings settings;
47 | settings.beginGroup(m_groupName);
48 | m_parameters.topics.clear();
49 |
50 | const auto size = settings.beginReadArray("topics");
51 | for (auto i = 0; i < size; ++i) {
52 | settings.setArrayIndex(i);
53 | m_parameters.topics.append({ readParameter(settings, "renamed_name", QString("")), readParameter(settings, "original_name", QString("")),
54 | static_cast(readParameter(settings, "lower_boundary", 0)),
55 | static_cast(readParameter(settings, "upper_boundary", 0)),
56 | readParameter(settings, "is_selected", false) });
57 | }
58 | settings.endArray();
59 | settings.endGroup();
60 |
61 | m_parameters.updateTimestamps = readParameter(m_groupName, "update_timestamps", false);
62 |
63 | return true;
64 | }
65 |
--------------------------------------------------------------------------------
/src/ui/MainWindow.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Parameters.hpp"
4 | #include "UtilsUI.hpp"
5 |
6 | #include
7 |
8 | // Main window displaying the main user interface. The basic workflow is as follows:
9 | // The main window uses the start widget to show all availabe tools. If the start widget is called,
10 | // this will call a corresponding input widget where a user can modify all necessary parameters.
11 | // If done, the input widget will be replaced with a progress widget showing the current progress.
12 | // The progress widget calls a separate thread performing the main operation in the background.
13 | class MainWindow : public QMainWindow
14 | {
15 | Q_OBJECT
16 |
17 | public:
18 | MainWindow();
19 |
20 | private slots:
21 | // Used to switch between start, input and progress widget
22 | void
23 | setStartWidget();
24 |
25 | void
26 | setInputWidget(Utils::UI::TOOL_ID mode);
27 |
28 | void
29 | setProgressWidget(Utils::UI::TOOL_ID mode);
30 |
31 | private:
32 | void
33 | closeEvent(QCloseEvent *event) override;
34 |
35 | private:
36 | // Parameters storing all configurations done by a user in the input widgets.
37 | // The parameters are transferred to the progress widget and their thread.
38 | Parameters::BagToVideoParameters m_parametersBagToVideo;
39 | Parameters::VideoToBagParameters m_parametersVideoToBag;
40 | Parameters::AdvancedParameters m_parametersBagToPCDs;
41 | Parameters::PCDsToBagParameters m_parametersPCDsToBag;
42 | Parameters::BagToImagesParameters m_parametersBagToImages;
43 | Parameters::TF2ToFileParameters m_parametersTF2ToFile;
44 | Parameters::EditBagParameters m_parametersEditBag;
45 | Parameters::MergeBagsParameters m_parametersMergeBags;
46 | Parameters::RecordBagParameters m_parametersRecordBag;
47 | Parameters::DummyBagParameters m_parametersDummyBag;
48 | Parameters::CompressBagParameters m_parametersCompressBag;
49 | Parameters::CompressBagParameters m_parametersDecompressBag;
50 |
51 | Parameters::PublishParameters m_parametersPublishVideo;
52 | Parameters::PublishParameters m_parametersPublishImages;
53 |
54 | Parameters::SendTF2Parameters m_parametersSendTF2;
55 | // Parameters for settings dialog
56 | Parameters::DialogParameters m_dialogParameters;
57 |
58 | static constexpr int DEFAULT_WIDTH = 450;
59 | static constexpr int DEFAULT_HEIGHT = 600;
60 | };
61 |
--------------------------------------------------------------------------------
/src/utils/UtilsUI.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | // Util functions for user interface related things
13 | namespace Utils::UI
14 | {
15 | // Create a larger font for some buttons and widget headers
16 | void
17 | setWidgetFontSize(QWidget* widget,
18 | bool isButton = false);
19 |
20 | [[maybe_unused]] bool
21 | fillComboBoxWithTopics(QPointer comboBox,
22 | const QString& bagDirectory,
23 | const QString& topicType);
24 |
25 | [[nodiscard]] QCheckBox*
26 | createCheckBox(const QString& toolTipText,
27 | bool checkState);
28 |
29 | // Creates a layout of a lineedit along with a tool button
30 | [[nodiscard]] QHBoxLayout*
31 | createLineEditButtonLayout(QPointer lineEdit,
32 | QPointer toolButton);
33 |
34 | // Create a messagebox asking if a user should continue with invalid ROS2 names
35 | [[nodiscard]] bool
36 | continueWithInvalidROS2Names();
37 |
38 | // Create a messagebox asking if a user should continue if a target file is already existing
39 | bool
40 | continueForExistingTarget(const QString& targetDirectory,
41 | const QString& headerTextBeginning,
42 | const QString& targetIdentifier);
43 |
44 | [[nodiscard]] QCheckBox*
45 | createMessageBoxCheckBox(const QString& optionsIdentifier);
46 |
47 | // Creates a messagebox informing of a critical error
48 | void
49 | createCriticalMessageBox(const QString& headerText,
50 | const QString& mainText);
51 |
52 | // Replaces a text appendix with another one
53 | const QString
54 | replaceTextAppendix(const QString& inputText,
55 | const QString& newAppendix);
56 |
57 | // Checks if the application is in dark mode
58 | [[nodiscard]] bool
59 | isDarkMode();
60 |
61 | enum class TOOL_ID {
62 | BAG_TO_VIDEO,
63 | VIDEO_TO_BAG,
64 | BAG_TO_PCDS,
65 | PCDS_TO_BAG,
66 | BAG_TO_IMAGES,
67 | TF2_TO_FILE,
68 | EDIT_BAG,
69 | MERGE_BAGS,
70 | RECORD_BAG,
71 | DUMMY_BAG,
72 | COMPRESS_BAG,
73 | DECOMPRESS_BAG,
74 | PUBLISH_VIDEO,
75 | PUBLISH_IMAGES,
76 | SEND_TF2,
77 | TOPICS_SERVICES_INFO,
78 | BAG_INFO
79 | };
80 |
81 | static constexpr int FONT_SIZE_HEADER = 16;
82 | static constexpr int FONT_SIZE_BUTTON = 14;
83 | }
84 |
--------------------------------------------------------------------------------
/test/utils/UtilsUITest.cpp:
--------------------------------------------------------------------------------
1 | #include "catch_ros2/catch_ros2.hpp"
2 |
3 | #include "UtilsUI.hpp"
4 |
5 | #include
6 | #include
7 |
8 | #include "rclcpp/rclcpp.hpp"
9 |
10 | #include "rosbag2_cpp/writer.hpp"
11 |
12 | #include "sensor_msgs/msg/image.hpp"
13 |
14 | #include
15 |
16 | TEST_CASE("Utils UI Testing", "[utils]") {
17 | SECTION("Font size test") {
18 | auto* const widget = new QWidget;
19 |
20 | Utils::UI::setWidgetFontSize(widget);
21 | auto font = widget->font();
22 | REQUIRE(font.pointSize() == 16);
23 |
24 | Utils::UI::setWidgetFontSize(widget, true);
25 | font = widget->font();
26 | REQUIRE(font.pointSize() == 14);
27 | }
28 | SECTION("Number of topics test") {
29 | const auto bagDirectory = std::filesystem::path("test_bag_file");
30 | std::filesystem::remove_all(bagDirectory);
31 |
32 | rosbag2_cpp::Writer writer;
33 | writer.open(bagDirectory);
34 |
35 | for (auto i = 0; i < 5; i++) {
36 | sensor_msgs::msg::Image imageMessage;
37 | imageMessage.width = 1;
38 | imageMessage.height = 1;
39 | writer.write(imageMessage, "/topic_image_one", rclcpp::Clock().now());
40 | writer.write(imageMessage, "/topic_image_two", rclcpp::Clock().now());
41 | }
42 | writer.close();
43 |
44 | auto* const comboBox = new QComboBox;
45 | Utils::UI::fillComboBoxWithTopics(comboBox, QString::fromStdString(bagDirectory), "sensor_msgs/msg/Image");
46 |
47 | REQUIRE(comboBox->count() == 2);
48 | std::filesystem::remove_all(bagDirectory);
49 | }
50 | SECTION("Checkbox test") {
51 | auto* checkBox = Utils::UI::createCheckBox("This is a tooltip", true);
52 | REQUIRE(checkBox->toolTip() == "This is a tooltip");
53 | REQUIRE(checkBox->checkState() == Qt::Checked);
54 |
55 | checkBox = Utils::UI::createCheckBox("Another tooltip", false);
56 | REQUIRE(checkBox->toolTip() == "Another tooltip");
57 | REQUIRE(checkBox->checkState() == Qt::Unchecked);
58 | }
59 | SECTION("New appendix test") {
60 | const auto textSingleAppendix = "text_with_a_format.mp4";
61 | const auto& newTextSingleAppendix = Utils::UI::replaceTextAppendix(textSingleAppendix, "mkv");
62 |
63 | REQUIRE(QString::compare(newTextSingleAppendix, "text_with_a_format.mkv") == 0);
64 |
65 | const auto textMultipleAppendices = "text_with_a_format.mp4.mkv";
66 | const auto& newTextMultipleAppendices = Utils::UI::replaceTextAppendix(textMultipleAppendices, "avi");
67 | REQUIRE(QString::compare(newTextMultipleAppendices, "text_with_a_format.mp4.avi") == 0);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/ui/HelperWidgets/TopicComboBoxWidget.cpp:
--------------------------------------------------------------------------------
1 | #include "TopicComboBoxWidget.hpp"
2 |
3 | #include "UtilsUI.hpp"
4 |
5 | #include
6 |
7 | TopicComboBoxWidget::TopicComboBoxWidget(Parameters::AdvancedParameters& parameters, const QString& headerText,
8 | const QString& iconPath, const QString& sourceFormLayoutName, const QString& targetFormLayoutName,
9 | const QString& settingsIdentifier, int outputFormat, QWidget *parent) :
10 | AdvancedInputWidget(parameters, headerText, iconPath, sourceFormLayoutName, targetFormLayoutName, settingsIdentifier, outputFormat, parent)
11 | {
12 | m_topicNameComboBox = new QComboBox;
13 | m_topicNameComboBox->setMinimumWidth(200);
14 |
15 | if (!m_parameters.sourceDirectory.isEmpty()) {
16 | QString topicType;
17 |
18 | switch (outputFormat) {
19 | case OUTPUT_VIDEO:
20 | case OUTPUT_IMAGES:
21 | topicType = "sensor_msgs/msg/Image";
22 | break;
23 | case OUTPUT_PCDS:
24 | topicType = "sensor_msgs/msg/PointCloud2";
25 | break;
26 | case OUTPUT_TF_TO_FILE:
27 | topicType = "tf2_msgs/msg/TFMessage";
28 | break;
29 | default:
30 | break;
31 | }
32 |
33 | Utils::UI::fillComboBoxWithTopics(m_topicNameComboBox, m_parameters.sourceDirectory, topicType);
34 |
35 | if (!m_parameters.topicName.isEmpty()) {
36 | m_topicNameComboBox->setCurrentText(m_parameters.topicName);
37 | }
38 | }
39 |
40 | connect(m_topicNameComboBox, &QComboBox::currentTextChanged, this, [this] (const QString& text) {
41 | writeParameterToSettings(m_parameters.topicName, text, m_settings);
42 | });;
43 | }
44 |
45 |
46 | void
47 | TopicComboBoxWidget::findSourceButtonPressed()
48 | {
49 | AdvancedInputWidget::findSourceButtonPressed();
50 |
51 | QString topicType;
52 | switch (m_outputFormat) {
53 | case OUTPUT_VIDEO:
54 | topicType = "sensor_msgs/msg/Image";
55 | break;
56 | case OUTPUT_IMAGES:
57 | topicType = "sensor_msgs/msg/Image";
58 | break;
59 | case OUTPUT_PCDS:
60 | topicType = "sensor_msgs/msg/PointCloud2";
61 | break;
62 | case OUTPUT_TF_TO_FILE:
63 | topicType = "tf2_msgs/msg/TFMessage";
64 | break;
65 | default:
66 | break;
67 | }
68 |
69 | if (const auto containsTopics = Utils::UI::fillComboBoxWithTopics(m_topicNameComboBox, m_sourceLineEdit->text(), topicType); !containsTopics) {
70 | Utils::UI::createCriticalMessageBox("Topic not found!", "The bag file does not contain any corresponding topics!");
71 | return;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/resources/icons/publish_images_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
35 |
--------------------------------------------------------------------------------
/resources/icons/publish_images_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
35 |
--------------------------------------------------------------------------------
/src/ui/InputWidgets/BasicInputWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicSettings.hpp"
4 |
5 | #include
6 | #include
7 |
8 | class LowDiskSpaceWidget;
9 |
10 | class QDialogButtonBox;
11 | class QHBoxLayout;
12 | class QLabel;
13 | class QLineEdit;
14 | class QPushButton;
15 | class QToolButton;
16 |
17 | template
18 | concept InputParameterSetting = (std::same_as && (std::same_as || std::same_as ||
19 | std::same_as || std::same_as || std::same_as));
20 |
21 | // The basic input widget all other input widgets derive from
22 | // Each input widget uses a corresponding parameter and settings member.
23 | // The parameters are used to store all input for reusing if the widget is closed and opened again,
24 | // while the settings are used to write the parameters to file in case the parameters should be
25 | // used after closing and restarting the application.
26 |
27 | // The basic widget provides a few members and functions used by all derived members.
28 | class BasicInputWidget : public QWidget
29 | {
30 | Q_OBJECT
31 |
32 | public:
33 | // Set the header text and top icon automatically
34 | BasicInputWidget(const QString& headerText,
35 | const QString& iconPath,
36 | QWidget* parent = 0);
37 |
38 | signals:
39 | void
40 | back() const;
41 |
42 | void
43 | okPressed() const;
44 |
45 | protected:
46 | void
47 | enableOkButton(bool enable) const;
48 |
49 | void
50 | setPixmapLabelIcon() const;
51 |
52 | void
53 | setLowDiskSpaceWidgetVisibility(const QString& path);
54 |
55 | [[nodiscard]] bool
56 | showLowDiskSpaceMessageBox() const;
57 |
58 | bool
59 | event(QEvent *event) override;
60 |
61 | template
62 | requires InputParameterSetting
63 | void
64 | writeParameterToSettings(T& settingsParameter,
65 | const U& newValue,
66 | BasicSettings& inputSettings) const
67 | {
68 | settingsParameter = newValue;
69 | inputSettings.write();
70 | }
71 |
72 | protected:
73 | QPointer m_lowDiskSpaceWidget;
74 |
75 | QPointer m_headerLabel;
76 | QPointer m_headerPixmapLabel;
77 | // All input widgets need some sort of source file, provide it here
78 | QPointer m_sourceLineEdit;
79 | QPointer m_findSourceButton;
80 | // Also need an ok and cancel button
81 | QPointer m_backButton;
82 | QPointer m_okButton;
83 |
84 | QPointer m_findSourceLayout;
85 | QPointer m_buttonLayout;
86 | QPointer m_dialogButtonBox;
87 |
88 | QString m_iconPath;
89 | // Need to store this so we can possibly show it in the messagebox
90 | float m_remainingSpace;
91 | };
92 |
--------------------------------------------------------------------------------
/src/ui/StartWidget.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "Parameters.hpp"
4 | #include "UtilsUI.hpp"
5 |
6 | #include
7 | #include
8 |
9 | class QLabel;
10 | class QPushButton;
11 | class QToolButton;
12 | class QVBoxLayout;
13 |
14 | // The starting widget showing all available ui tools
15 | class StartWidget : public QWidget
16 | {
17 | Q_OBJECT
18 | public:
19 | explicit
20 | StartWidget(Parameters::DialogParameters& dialogParameters,
21 | QWidget* parent = 0);
22 |
23 | signals:
24 | void
25 | toolRequested(Utils::UI::TOOL_ID id);
26 |
27 | private slots:
28 | void
29 | openSettingsDialog();
30 |
31 | private:
32 | void
33 | replaceWidgets(QWidget* fromWidget,
34 | QWidget* toWidget,
35 | int widgetIdentifier,
36 | bool otherItemVisibility);
37 |
38 | QPointer
39 | createToolButton(const QString& buttonText,
40 | const QString& tooltipText = "") const;
41 |
42 | void
43 | setButtonIcons();
44 |
45 | bool
46 | event(QEvent *event) override;
47 |
48 | private:
49 | QPointer m_headerLabel;
50 |
51 | // Buttons for tools
52 | QPointer m_conversionToolsButton;
53 | QPointer m_bagToolsButton;
54 | QPointer m_publishingToolsButton;
55 | QPointer m_infoToolsButton;
56 |
57 | QPointer m_bagToVideoPushButton;
58 | QPointer m_videoToBagPushButton;
59 | QPointer m_bagToPCDsPushButton;
60 | QPointer m_PCDsToBagPushButton;
61 | QPointer m_bagToImagesPushButton;
62 | QPointer m_tf2ToFilePushButton;
63 |
64 | QPointer m_editBagButton;
65 | QPointer m_mergeBagsButton;
66 | QPointer m_recordBagButton;
67 | QPointer m_dummyBagButton;
68 | QPointer m_compressBagButton;
69 | QPointer m_decompressBagButton;
70 |
71 | QPointer m_publishVideoButton;
72 | QPointer m_publishImagesButton;
73 | QPointer m_sendTF2Button;
74 |
75 | QPointer m_topicServiceInfoButton;
76 | QPointer m_bagInfoButton;
77 |
78 | // Widgets for other elements
79 | QPointer m_settingsButton;
80 | QPointer m_backButton;
81 | QPointer m_versionLabel;
82 |
83 | QPointer m_mainLayout;
84 |
85 | Parameters::DialogParameters& m_dialogParameters;
86 |
87 | // Used to remember which widget was active when we switch to the input widget, but cancel
88 | inline static int m_widgetOnInstantiation = 0;
89 |
90 | static constexpr int WIDGET_OVERALL = 0;
91 | static constexpr int WIDGET_CONVERSION = 1;
92 | static constexpr int WIDGET_BAG = 2;
93 | static constexpr int WIDGET_PUBLISHING = 3;
94 | static constexpr int WIDGET_INFO = 4;
95 | };
96 |
--------------------------------------------------------------------------------
/src/ros/thread/VideoToBagThread.cpp:
--------------------------------------------------------------------------------
1 | #include "VideoToBagThread.hpp"
2 |
3 | #include
4 |
5 | #include "rclcpp/rclcpp.hpp"
6 | #include "rosbag2_cpp/writer.hpp"
7 | #include "sensor_msgs/msg/image.hpp"
8 |
9 | #include
10 |
11 | #ifdef ROS_HUMBLE
12 | #include
13 | #else
14 | #include
15 | #endif
16 |
17 | VideoToBagThread::VideoToBagThread(const Parameters::VideoToBagParameters& parameters, bool useHardwareAcceleration,
18 | QObject* parent) :
19 | BasicThread(parameters.sourceDirectory, parameters.topicName, parent),
20 | m_parameters(parameters), m_useHardwareAcceleration(useHardwareAcceleration)
21 | {
22 | }
23 |
24 |
25 | void
26 | VideoToBagThread::run()
27 | {
28 | auto videoCapture = cv::VideoCapture(m_sourceDirectory, cv::CAP_ANY, {
29 | cv::CAP_PROP_HW_ACCELERATION, m_useHardwareAcceleration ? cv::VIDEO_ACCELERATION_ANY : cv::VIDEO_ACCELERATION_NONE
30 | });
31 | if (!videoCapture.isOpened()) {
32 | emit failed();
33 | return;
34 | }
35 |
36 | const auto targetDirectoryStd = m_parameters.targetDirectory.toStdString();
37 | if (std::filesystem::exists(targetDirectoryStd)) {
38 | std::filesystem::remove_all(targetDirectoryStd);
39 | }
40 |
41 | const auto frameCount = videoCapture.get(cv::CAP_PROP_FRAME_COUNT);
42 | const auto finalFPS = m_parameters.useCustomFPS ? m_parameters.fps : videoCapture.get(cv::CAP_PROP_FPS);
43 | auto timeStamp = rclcpp::Clock(RCL_ROS_TIME).now();
44 | const auto duration = rclcpp::Duration::from_seconds(1.0f / (float) finalFPS);
45 |
46 | auto writer = std::make_unique();
47 | writer->open(targetDirectoryStd);
48 | auto iterationCount = 0;
49 |
50 | cv::Mat frame;
51 | std_msgs::msg::Header header;
52 | sensor_msgs::msg::Image message;
53 |
54 | cv_bridge::CvImage cvBridge;
55 | cvBridge.header = header;
56 | cvBridge.encoding = sensor_msgs::image_encodings::BGR8;
57 |
58 | while (true) {
59 | if (isInterruptionRequested()) {
60 | writer->close();
61 | return;
62 | }
63 |
64 | // Capture image
65 | videoCapture >> frame;
66 | if (frame.empty()) {
67 | break;
68 | }
69 |
70 | iterationCount++;
71 | if (m_parameters.exchangeRedBlueValues) {
72 | cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
73 | }
74 |
75 | // Create empty sensor message
76 | timeStamp += duration;
77 | header.stamp = timeStamp;
78 |
79 | // Convert and write image
80 | cvBridge.image = frame;
81 | cvBridge.toImageMsg(message);
82 | writer->write(message, m_topicName, timeStamp);
83 |
84 | emit progressChanged("Writing message " + QString::number(iterationCount) + " of " + QString::number(frameCount) + "...",
85 | ((float) iterationCount / (float) frameCount) * 100);
86 | }
87 |
88 | writer->close();
89 | emit finished();
90 | }
91 |
--------------------------------------------------------------------------------
/src/ros/thread/PublishVideoThread.cpp:
--------------------------------------------------------------------------------
1 | #include "PublishVideoThread.hpp"
2 |
3 | #include
4 |
5 | #ifdef ROS_HUMBLE
6 | #include
7 | #else
8 | #include
9 | #endif
10 |
11 | PublishVideoThread::PublishVideoThread(const Parameters::PublishParameters& parameters, bool useHardwareAcceleration,
12 | QObject* parent) :
13 | BasicThread(parameters.sourceDirectory, parameters.topicName, parent),
14 | m_parameters(parameters), m_useHardwareAcceleration(useHardwareAcceleration)
15 | {
16 | m_node = std::make_shared("publish_video");
17 | m_publisher = m_node->create_publisher(m_topicName, 10);
18 | }
19 |
20 |
21 | void
22 | PublishVideoThread::run()
23 | {
24 | auto videoCapture = cv::VideoCapture(m_sourceDirectory, cv::CAP_ANY, {
25 | cv::CAP_PROP_HW_ACCELERATION, m_useHardwareAcceleration ? cv::VIDEO_ACCELERATION_ANY : cv::VIDEO_ACCELERATION_NONE
26 | });
27 | if (!videoCapture.isOpened()) {
28 | emit failed();
29 | return;
30 | }
31 |
32 | cv::Mat frame;
33 | sensor_msgs::msg::Image message;
34 |
35 | std_msgs::msg::Header header;
36 | cv_bridge::CvImage cvBridge;
37 | cvBridge.header = header;
38 | cvBridge.encoding = sensor_msgs::image_encodings::BGR8;
39 |
40 | const int rate = ((1000 / (float) videoCapture.get(cv::CAP_PROP_FPS)) * 1000);
41 | auto iterator = 0;
42 | const auto frameCount = videoCapture.get(cv::CAP_PROP_FRAME_COUNT);
43 |
44 | auto timer = m_node->create_wall_timer(std::chrono::microseconds(rate),
45 | [this, &iterator, &videoCapture, &frame, &message, &cvBridge, frameCount] {
46 | if (isInterruptionRequested()) {
47 | return;
48 | }
49 |
50 | // Loop if set
51 | if (iterator == frameCount && m_parameters.loop) {
52 | videoCapture.set(cv::CAP_PROP_POS_FRAMES, 0);
53 | iterator = 0;
54 | }
55 |
56 | // Capture image
57 | videoCapture >> frame;
58 | if (frame.empty()) {
59 | return;
60 | }
61 |
62 | if (m_parameters.exchangeRedBlueValues) {
63 | cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
64 | }
65 | if (m_parameters.scale) {
66 | cv::resize(frame, frame, cv::Size(m_parameters.width, m_parameters.height), 0, 0);
67 | }
68 |
69 | // Convert and write image
70 | cvBridge.image = frame;
71 | cvBridge.toImageMsg(message);
72 |
73 | m_publisher->publish(message);
74 |
75 | emit progressChanged("Publishing image " + QString::number(iterator + 1) + " of " + QString::number(frameCount) + "...", PROGRESS);
76 | iterator++;
77 | });
78 |
79 | auto executor = std::make_shared();
80 | executor->add_node(m_node);
81 |
82 | while (!isInterruptionRequested()) {
83 | executor->spin_once();
84 | }
85 |
86 | timer->cancel();
87 | emit finished();
88 | }
89 |
--------------------------------------------------------------------------------
/src/ros/thread/TF2ToFileThread.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "BasicThread.hpp"
4 | #include "Parameters.hpp"
5 |
6 | #include
7 |
8 | #include "tf2_msgs/msg/tf_message.hpp"
9 |
10 | #include "yaml-cpp/yaml.h"
11 |
12 | template
13 | concept NodeParameter = std::same_as || std::same_as;
14 |
15 | // Used to send nonstatic transformations at a custom rate
16 | class TF2ToFileThread : public BasicThread {
17 | Q_OBJECT
18 | public:
19 | explicit
20 | TF2ToFileThread(const Parameters::TF2ToFileParameters& parameters,
21 | QObject* parent = nullptr);
22 |
23 | void
24 | run() override;
25 |
26 | private:
27 | // Both YAML node and QJsonObject are very similar in terms of array handling, so
28 | // templatize to avoid messy boilerplate code for json and yaml handling
29 | template
30 | requires NodeParameter
31 | void
32 | fillNode(std::shared_ptr tfMessage,
33 | T& finalNode) const
34 | {
35 | for (size_t i = 0; i < tfMessage->transforms.size(); i++) {
36 | const auto& currentTransform = tfMessage->transforms.at(i);
37 | T iterationNode;
38 |
39 | T translation;
40 | translation["x"] = currentTransform.transform.translation.x;
41 | translation["y"] = currentTransform.transform.translation.y;
42 | translation["z"] = currentTransform.transform.translation.z;
43 |
44 | T rotation;
45 | rotation["x"] = currentTransform.transform.rotation.x;
46 | rotation["y"] = currentTransform.transform.rotation.y;
47 | rotation["z"] = currentTransform.transform.rotation.z;
48 | rotation["w"] = currentTransform.transform.rotation.w;
49 |
50 | iterationNode["translation"] = translation;
51 | iterationNode["rotation"] = rotation;
52 |
53 | if constexpr (std::is_same_v) {
54 | iterationNode["header_frame_id"] = QString::fromStdString(currentTransform.header.frame_id);
55 | iterationNode["child_frame_id"] = QString::fromStdString(currentTransform.child_frame_id);
56 | if (m_parameters.keepTimestamps) {
57 | iterationNode["header_stamp"] = QString::number(currentTransform.header.stamp.nanosec / 1e9, 'f', 6);
58 | }
59 |
60 | finalNode["transform_" + QString::number(i)] = iterationNode;
61 | } else {
62 | iterationNode["header_frame_id"] = currentTransform.header.frame_id;
63 | iterationNode["child_frame_id"] = currentTransform.child_frame_id;
64 | if (m_parameters.keepTimestamps) {
65 | iterationNode["header_stamp"] = currentTransform.header.stamp.nanosec / 1e9;
66 | }
67 |
68 | finalNode["transform_" + std::to_string(i)] = iterationNode;
69 | }
70 | }
71 | }
72 |
73 | private:
74 | const Parameters::TF2ToFileParameters& m_parameters;
75 | };
76 |
--------------------------------------------------------------------------------
/resources/resources.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | gifs/compressing_black.gif
4 | gifs/compressing_white.gif
5 | gifs/decompressing_black.gif
6 | gifs/decompressing_white.gif
7 | gifs/merging_black.gif
8 | gifs/merging_white.gif
9 | gifs/publishing_black.gif
10 | gifs/publishing_white.gif
11 | gifs/recording_black.gif
12 | gifs/recording_white.gif
13 | gifs/sending_tf2_black.gif
14 | gifs/sending_tf2_white.gif
15 |
16 | icons/bag_info_black.svg
17 | icons/bag_info_white.svg
18 | icons/bag_to_pcd_black.svg
19 | icons/bag_to_pcd_white.svg
20 | icons/bag_to_images_black.svg
21 | icons/bag_to_images_white.svg
22 | icons/bag_to_video_black.svg
23 | icons/bag_to_video_white.svg
24 | icons/bag_tools_black.svg
25 | icons/bag_tools_white.svg
26 | icons/compress_bag_black.svg
27 | icons/compress_bag_white.svg
28 | icons/conversion_tools_black.svg
29 | icons/conversion_tools_white.svg
30 | icons/decompress_bag_black.svg
31 | icons/decompress_bag_white.svg
32 | icons/dummy_bag_black.svg
33 | icons/dummy_bag_white.svg
34 | icons/edit_bag_black.svg
35 | icons/edit_bag_white.svg
36 | icons/gear_black.svg
37 | icons/gear_white.svg
38 | icons/info_tools_black.svg
39 | icons/info_tools_white.svg
40 | icons/main.svg
41 | icons/merge_bags_black.svg
42 | icons/merge_bags_white.svg
43 | icons/minus_black.svg
44 | icons/minus_white.svg
45 | icons/pcd_to_bag_black.svg
46 | icons/pcd_to_bag_white.svg
47 | icons/plus_black.svg
48 | icons/plus_white.svg
49 | icons/publishing_tools_black.svg
50 | icons/publishing_tools_white.svg
51 | icons/publish_images_black.svg
52 | icons/publish_images_white.svg
53 | icons/publish_video_black.svg
54 | icons/publish_video_white.svg
55 | icons/record_bag_black.svg
56 | icons/record_bag_white.svg
57 | icons/send_tf2_black.svg
58 | icons/send_tf2_white.svg
59 | icons/topics_services_info_black.svg
60 | icons/topics_services_info_white.svg
61 | icons/tf2_to_file_black.svg
62 | icons/tf2_to_file_white.svg
63 | icons/video_to_bag_black.svg
64 | icons/video_to_bag_white.svg
65 | icons/warning.svg
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/utils/UtilsROS.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "NodeWrapper.hpp"
4 |
5 | #include
6 | #include
7 |
8 | #include "rclcpp/rclcpp.hpp"
9 | #include "rosbag2_cpp/writer.hpp"
10 | #include "rosbag2_storage/bag_metadata.hpp"
11 |
12 | #include "std_msgs/msg/int32.hpp"
13 | #include "std_msgs/msg/string.hpp"
14 |
15 | #include
16 |
17 | template
18 | concept WriteMessageParameter = (std::same_as && std::same_as) ||
19 | (std::same_as && std::same_as);
20 |
21 | // ROS related util functions
22 | namespace Utils::ROS
23 | {
24 | // Sends a static transformation using tf broadcaster
25 | // @NOTE: For whatever reason, just creating and spinning a local node
26 | // does not work here, we have to spin a global node
27 | void
28 | sendStaticTransformation(const std::array& translation,
29 | const std::array& rotation,
30 | std::shared_ptr nodeWrapper);
31 |
32 | // If a directory contains a valid ROS bag
33 | [[nodiscard]] bool
34 | doesDirectoryContainBagFile(const QString& bagDirectory);
35 |
36 | // If a directory contains a valid compressed ROS bag
37 | [[nodiscard]] bool
38 | doesDirectoryContainCompressedBagFile(const QString& bagDirectory);
39 |
40 | // Show all current topic names and types
41 | std::vector > >
42 | getTopicInformation();
43 |
44 | // Show all current service names and types
45 | std::map >
46 | getServiceNamesAndTypes();
47 |
48 | // Returns the metadata stored for a ROS bag
49 | [[nodiscard]] rosbag2_storage::BagMetadata
50 | getBagMetadata(const QString& bagDirectory);
51 |
52 | // Returns a topic in a bag file, if existent
53 | std::optional
54 | getTopicInBag(const QString& bagDirectory,
55 | const QString& topicName);
56 |
57 | // If a ROS bag contains a certain topic
58 | [[nodiscard]] bool
59 | doesBagContainTopicName(const QString& bagDirectory,
60 | const QString& topicName);
61 |
62 | // Message count for a ROS bag topic
63 | [[nodiscard]] std::optional
64 | getTopicMessageCount(const QString& bagDirectory,
65 | const QString& topicName);
66 |
67 | [[nodiscard]] std::optional
68 | getTopicMessageCount(const std::string& bagDirectory,
69 | const std::string& topicName);
70 |
71 | // Get a ROS bag topic's type
72 | [[nodiscard]] std::optional
73 | getTopicType(const QString& bagDirectory,
74 | const QString& topicName);
75 |
76 | // Returns the first topic in a bag file with a certain type
77 | [[nodiscard]] std::optional
78 | getFirstTopicWithCertainType(const QString& bagDirectory,
79 | const QString& typeName);
80 |
81 | // Returns all video bag topics stored in a ROS bag file
82 | [[nodiscard]] QVector