0

I've built a Qt GUI in python from code (not from the Qt editor, so I don't use qml files). I want it to be able to subscribe to ROS2 topics and update the GUI according to the messages received, and publish data when a button is clicked. The problem is that Qt requires app.exec(), and ROS requires node.spin() to be run, both of which are infinite loops. There seem to be some tutorials online for ROS + Qt, but all of them are focused on ROS1. I can't find any example code for ROS2 + Qt.

How can I integrate my Qt GUI with ROS2?

robbertfm
  • 9
  • 1
  • 2

2 Answers2

0

I used a class which is both a QObject and a rclcpp::Node:

class RosWorker : public QObject, public rclcpp::Node

The RosWorker receives "messages" via public slots from Qt and sends them to ROS via rclcpp::Publisher::publish(). It receives ROS messages with the classical callback principle and sends them to Qt with public signals.

Then, I create an instance of RosWorker ("std::shared_ptr<RosWorker> node_;") in my MainWindow class (inheriting QMainWindow) and run the node in a separate thread:

void
MainWindow::rosSpinThread()
{
  rclcpp::spin(node_);
  rclcpp::shutdown();
}

with

MainWindow::MainWindow
{
  ros_spin_thread_ = std::thread{std::bind(&MainWindow::rosSpinThread, this)};
}

Moreover, you need to declare ROS messages types as custom types, e.g.:

Q_DECLARE_METATYPE(geometry_msg::msg::Pose)  // Outside of any namespace.

and

qRegisterMetaType<geometry_msg::msg::Pose>();  // In MainWindow's constructor.

My first intuition was to have a class inheriting from rclcpp::Node only with an instance of MainWindow but didn't manage to have it work that way.

0

The way I did it (not sure if this is the correct or most elegant way, but it worked for my HMI application), is to create a basic design in QT Designer. This is because I had(have) zero knowledge of designing GUIs. Then, once I am happy with the design, I use pyuic5 to export the design as a Python class. I am sure that there exists some tool (maybe uic5) for C++ as well that does just that. Once you have your class (maybe a header file), you can create a ROS2 Node that contains an object of the created GUI class. This way, you can access the attributes of the GUI class from your ROS2 Node.

Once you are able to do that, the task becomes quite simple. I did mine in Python, so for me it was to connect all the push buttons to their respective publishers by adding the <pushButtonName>.clicked.connect(<my-ros2-publisher-callback>). For subscribers that need to update a TextLabel object, you can modify it using the <labelName>.setText() function.

Service calls work the same way as the push buttons work, and for the main node, you can use a MultiThreadedExecutor to spin your ROS2 Node in one thread and have your GUI spinning in the main thread.

I have not tried to implement this in C++, though I think this way should work. The reason I like my approach is that it helps encapsulate the GUI development and the ROS2 Node development, and as I said before, I have no knowledge regarding GUI development, so I end up using the drag and drop functionality provided by Qt Designer.

Hope this helps !

ssarkar
  • 33
  • 1
  • 9