display_topics.cpp
Go to the documentation of this file.
2 
3 namespace topics_rviz_plugin
4 {
5 DisplayTopics::DisplayTopics(QWidget *parent) :
6  rviz::Panel(parent)
7 {
8  connect(this, &DisplayTopics::enable, this, &DisplayTopics::setEnabled);
9  Q_EMIT enable(false);
10  setObjectName("Topics RViz plugin");
11  setName(objectName());
12 
13  qRegisterMetaType<QMessageBox::Icon>();
15 
16  layout_ = new QVBoxLayout();
17  QWidget *scroll_widget = new QWidget;
18  scroll_widget->setLayout(layout_);
19  QScrollArea *scroll_area = new QScrollArea;
20  scroll_area->setWidget(scroll_widget);
21  scroll_area->setWidgetResizable(true);
22  scroll_area->setFrameShape(QFrame::NoFrame);
23  QVBoxLayout *main_layout = new QVBoxLayout(this);
24  main_layout->addWidget(scroll_area);
25 
26  QPushButton *topics = new QPushButton("Topics");
27  connect(topics, &QPushButton::clicked, this, &DisplayTopics::topics);
28 
29  QPushButton *settings = new QPushButton("Settings");
30  settings->setIcon(QIcon::fromTheme("preferences-system"));
31  connect(settings, &QPushButton::clicked, this, &DisplayTopics::settings);
32 
33  QHBoxLayout *buttons = new QHBoxLayout;
34  buttons->addWidget(topics);
35  buttons->addWidget(settings);
36  layout_->addLayout(buttons);
37 
38  table_ = new QTableWidget;
39  table_->insertColumn(0);
40  table_->insertColumn(0);
41  QStringList labels;
42  labels.push_back("Topic");
43  labels.push_back("Value");
44  table_->setHorizontalHeaderLabels(labels);
45  table_->setShowGrid(false);
46  layout_->addWidget(table_);
47  Q_EMIT enable(true);
48 
49  connect(table_->horizontalHeader(), &QHeaderView::sectionResized, this, &DisplayTopics::configChanged);
50  connect(table_->verticalHeader(), &QHeaderView::sectionResized, this, &DisplayTopics::configChanged);
51 }
52 
54 {
55  nh_.shutdown();
56 }
57 
58 void DisplayTopics::load(const rviz::Config &config)
59 {
60  rviz::Panel::load(config);
61 
62  unsigned i(0);
63  while (1)
64  {
65  QString topic_name;
66  QString topic_type;
67  int refresh_rate_ms(0);
68 
69  if (!config.mapGetString("topic_" + QString::number(i) + "_name", &topic_name))
70  break;
71 
72  if (!config.mapGetString("topic_" + QString::number(i) + "_type", &topic_type))
73  break;
74 
75  // Not mandatory
76  config.mapGetInt("topic_" + QString::number(i) + "_refresh_rate", &refresh_rate_ms);
77 
78  TopicDetails details;
79  details.type = topic_type.toStdString();
80  details.refresh_rate = ros::Duration(((double) refresh_rate_ms) / 1e3);
81 
82  displayed_topics_.insert(std::pair<std::string, TopicDetails>(topic_name.toStdString(), details));
83  ++i;
84  }
85 
86  bool tmp_bool(false);
87  if (config.mapGetBool("short_topic_names", &tmp_bool))
88  short_topic_names_ = tmp_bool;
89  else
90  short_topic_names_ = false;
91 
92  if (!displayed_topics_.empty())
94 
95  QString table_header_state_str;
96  if (config.mapGetString("horizontal_table_header", &table_header_state_str))
97  {
98  table_->horizontalHeader()->restoreState(QByteArray::fromHex(qPrintable(table_header_state_str)));
99  }
100 
101  if (config.mapGetString("vertical_table_header", &table_header_state_str))
102  {
103  table_->verticalHeader()->restoreState(QByteArray::fromHex(qPrintable(table_header_state_str)));
104  }
105 }
106 
108 {
109  rviz::Panel::save(config);
110  unsigned i(0);
111  for (auto topic : displayed_topics_)
112  {
113  config.mapSetValue("topic_" + QString::number(i) + "_name", QString::fromStdString(topic.first));
114  config.mapSetValue("topic_" + QString::number(i) + "_type", QString::fromStdString(topic.second.type));
115  config.mapSetValue("topic_" + QString::number(i) + "_refresh_rate", topic.second.refresh_rate.toNSec() / 1e6);
116  ++i;
117  }
118 
119  QByteArray table_header_state(table_->horizontalHeader()->saveState());
120  // Must be saved as hex
121  config.mapSetValue("horizontal_table_header", table_header_state.toHex());
122 
123  table_header_state = table_->verticalHeader()->saveState();
124  // Must be saved as hex
125  config.mapSetValue("vertical_table_header", table_header_state.toHex());
126 
127  config.mapSetValue("short_topic_names", short_topic_names_);
128 }
129 
131 {
132  Q_EMIT enable(false);
133 
135  if (!ros::master::getTopics(topics))
136  {
137  Q_EMIT displayMessageBox("Error getting topics", "Could not retrieve the topics names.", "",
138  QMessageBox::Icon::Critical);
139  Q_EMIT enable(true);
140  return;
141  }
142 
143  ros::master::V_TopicInfo supported_topics;
144  for (auto topic : topics)
145  {
146  if (topic.datatype == "std_msgs/Bool" ||
147  topic.datatype == "std_msgs/Int8" ||
148  topic.datatype == "std_msgs/UInt8" ||
149  topic.datatype == "std_msgs/Int16" ||
150  topic.datatype == "std_msgs/UInt16" ||
151  topic.datatype == "std_msgs/Int32" ||
152  topic.datatype == "std_msgs/UInt32" ||
153  topic.datatype == "std_msgs/Int64" ||
154  topic.datatype == "std_msgs/UInt64" ||
155  topic.datatype == "std_msgs/Float32" ||
156  topic.datatype == "std_msgs/Float64" ||
157  topic.datatype == "std_msgs/String" ||
158  topic.datatype == "std_msgs/Time" ||
159  topic.datatype == "std_msgs/Duration")
160  supported_topics.emplace_back(topic);
161  }
162 
163  if (supported_topics.empty())
164  {
165  Q_EMIT enable(false);
166 
167  QDialog *no_topics_dialog = new QDialog(this);
168  no_topics_dialog->setWindowTitle("No supported topic");
169  QVBoxLayout *layout = new QVBoxLayout;
170  no_topics_dialog->setLayout(layout);
171  layout->addWidget(
172  new QLabel("Error with topics, no supported topics found.\n"
173  "- Ok will clear the topics displayed\n- Cancel will not change the displayed topics"));
174 
175  QDialogButtonBox *button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
176  no_topics_dialog->layout()->addWidget(button_box);
177 
178  connect(button_box, &QDialogButtonBox::accepted, no_topics_dialog, &QDialog::accept);
179  connect(button_box, &QDialogButtonBox::rejected, no_topics_dialog, &QDialog::reject);
180 
181  if (!no_topics_dialog->exec())
182  {
183  Q_EMIT enable(true);
184  return;
185  }
186 
187  displayed_topics_.clear();
188  Q_EMIT configChanged();
190  Q_EMIT enable(true);
191  return;
192  }
193 
194  QDialog *pick_topics_dialog = new QDialog(this);
195  pick_topics_dialog->setWindowTitle("Pick displayed topics");
196 
197  QGridLayout *layout = new QGridLayout();
198  QWidget *scroll_widget = new QWidget;
199  scroll_widget->setLayout(layout);
200  QScrollArea *scroll_area = new QScrollArea;
201  scroll_area->setWidget(scroll_widget);
202  scroll_area->setWidgetResizable(true);
203  scroll_area->setFrameShape(QFrame::NoFrame);
204  QVBoxLayout *dialog_layout = new QVBoxLayout(pick_topics_dialog);
205  dialog_layout->addWidget(scroll_area);
206 
207  unsigned row(0), col(0);
208 
209  layout->addWidget(new QLabel("Only supported built-in types are displayed"), row ,col);
210  ++row;
211  std::sort(supported_topics.begin(), supported_topics.end(), [](ros::master::TopicInfo a, ros::master::TopicInfo b)
212  {
213  return a.name < b.name;
214  });
215 
216  std::vector<QCheckBox *> topic_buttons;
217  for (auto topic : supported_topics)
218  {
219  QCheckBox *radio_button = new QCheckBox;
220 
221  if (displayed_topics_.find(topic.name) != displayed_topics_.end())
222  radio_button->setChecked(true);
223 
224  topic_buttons.emplace_back(radio_button);
225  radio_button->setText(QString::fromStdString(topic.name));
226  radio_button->setObjectName(QString::fromStdString(topic.name));
227  radio_button->setToolTip(QString::fromStdString(topic.datatype));
228  layout->addWidget(radio_button, row, col);
229  ++row;
230  if (row > 20)
231  {
232  ++col;
233  row = 1; // Don't start at 0, it's the label
234  }
235  }
236 
237  QDialogButtonBox *button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
238  layout->addWidget(button_box, layout->rowCount() + 1, col);
239 
240  connect(button_box, &QDialogButtonBox::accepted, pick_topics_dialog, &QDialog::accept);
241  connect(button_box, &QDialogButtonBox::rejected, pick_topics_dialog, &QDialog::reject);
242 
243  if (!pick_topics_dialog->exec())
244  {
245  Q_EMIT enable(true);
246  return;
247  }
248 
249  Q_EMIT configChanged();
250 
251  std::map<std::string, TopicDetails> displayed_topics_old(displayed_topics_);
252  displayed_topics_.clear();
253  for (auto button : topic_buttons)
254  {
255  if (!button->isChecked())
256  continue;
257 
258  TopicDetails details;
259 
260  // If topic was already displayed before, copy its details
261  for (auto &topic: displayed_topics_old)
262  {
263  if (topic.first == button->objectName().toStdString())
264  details= topic.second;
265  }
266 
267  details.type = button->toolTip().toStdString();
268  displayed_topics_.insert(std::pair<std::string, TopicDetails>(button->objectName().toStdString(),
269  details));
270  }
271 
273  Q_EMIT enable(true);
274 }
275 
276 
278 {
279  Q_EMIT enable(false);
280 
281  QDialog *dialog(new QDialog(this));
282  dialog->setWindowTitle("Display topics - Settings");
283  QVBoxLayout *layout = new QVBoxLayout(dialog);
284 
285  QCheckBox *short_topic_names(new QCheckBox("Short topic names"));
286  short_topic_names->setChecked(short_topic_names_);
287  layout->addWidget(short_topic_names);
288 
289  // Topic refresh rate
290  layout->addStretch(1);
291  QLabel* maximum_refresh_rate(new QLabel("<b>Maximum refresh rate</b>"));
292  layout->addWidget(maximum_refresh_rate);
293 
294  QGridLayout *grid(new QGridLayout);
295  std::size_t i(0);
296  std::vector<QSpinBox *> topic_spinboxes;
297  for (auto &topic: displayed_topics_)
298  {
299  QLabel *topic_name(new QLabel(QString::fromStdString(topic.first)));
300  topic_name->setToolTip(QString::fromStdString(topic.second.type));
301  grid->addWidget(topic_name, i, 0);
302  QSpinBox *refresh_rate(new QSpinBox);
303  topic_spinboxes.emplace_back(refresh_rate);
304  refresh_rate->setObjectName(QString::fromStdString(topic.first));
305  refresh_rate->setToolTip("If set to zero the displayed value is updated every time a value is received.");
306  refresh_rate->setSingleStep(100);
307  refresh_rate->setRange(0, 10000);
308  refresh_rate->setSuffix(" ms");
309  grid->addWidget(refresh_rate, i, 1);
310  ++i;
311 
312  // Retrieve value from the displayed topics
313  for (auto &topic_info: topic_infos_)
314  {
315  if (topic_info->topic_name_ != topic.first)
316  continue;
317  refresh_rate->setValue(topic_info->maximumRefreshRate().toSec() * 1e3);
318  break;
319  }
320  }
321 
322  layout->addLayout(grid);
323  layout->addStretch(1);
324 
325  QDialogButtonBox *button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
326  layout->addWidget(button_box);
327 
328  connect(button_box, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
329  connect(button_box, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
330 
331  if (!dialog->exec())
332  {
333  Q_EMIT enable(true);
334  return;
335  }
336 
337  short_topic_names_ = short_topic_names->isChecked();
338 
339  // Change refresh rates
340  for (auto &spinbox: topic_spinboxes)
341  {
342  for (auto &topic: displayed_topics_)
343  {
344  if (topic.first != spinbox->objectName().toStdString())
345  continue;
346 
347  const ros::Duration d(((double) spinbox->value()) / 1e3); // milliseconds to seconds
348  topic.second.refresh_rate = d;
349  break;
350  }
351  }
352 
354  Q_EMIT configChanged();
355  Q_EMIT enable(true);
356 }
357 
359 {
360  table_->setRowCount(0);
361  table_->setRowCount(displayed_topics_.size());
362 
363  topic_infos_.clear();
364  for (auto topic : displayed_topics_)
365  {
366  std::shared_ptr<TopicInfo> topic_info(new TopicInfo(topic.first, topic.second.type, topic.second.refresh_rate));
367  topic_infos_.emplace_back(topic_info);
368  }
369 
370  unsigned i(0);
371  for (auto topic_info : topic_infos_)
372  {
373  if (short_topic_names_)
374  {
375  // Names always start with /
376  // Names never ends with /
377  QString topic_name(topic_info->label_.get()->text());
378  int n = topic_name.lastIndexOf("/");
379  if (n != 0)
380  topic_name.remove(0, n + 1);
381 
382  QLabel *topic_label(new QLabel(topic_name));
383  table_->setCellWidget(i, 0, topic_label);
384  }
385  else
386  table_->setCellWidget(i, 0, topic_info->label_.get());
387  table_->setCellWidget(i, 1, topic_info->display_.get());
388  ++i;
389  }
390 }
391 
393  const QString text,
394  const QString info,
395  const QMessageBox::Icon icon)
396 {
397  const bool old_state(isEnabled());
398  setEnabled(false);
399  QMessageBox msg_box;
400  msg_box.setWindowTitle(title);
401  msg_box.setText(text);
402  msg_box.setInformativeText(info);
403  msg_box.setIcon(icon);
404  msg_box.setStandardButtons(QMessageBox::Ok);
405  msg_box.exec();
406  setEnabled(old_state);
407 }
408 
409 }
410 
d
ROSCPP_DECL bool getTopics(V_TopicInfo &topics)
std::vector< std::shared_ptr< TopicInfo > > topic_infos_
virtual void save(Config config) const
bool mapGetString(const QString &key, QString *value_out) const
void configChanged()
std::vector< TopicInfo > V_TopicInfo
void mapSetValue(const QString &key, QVariant value)
virtual void setName(const QString &name)
virtual void load(const rviz::Config &config)
virtual void save(rviz::Config config) const
DisplayTopics(QWidget *parent=NULL)
void displayMessageBox(const QString, const QString, const QString, const QMessageBox::Icon)
bool mapGetBool(const QString &key, bool *value_out) const
void displayMessageBoxHandler(const QString title, const QString text, const QString info="", const QMessageBox::Icon icon=QMessageBox::Icon::Information)
virtual void load(const Config &config)
bool mapGetInt(const QString &key, int *value_out) const
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
std::map< std::string, TopicDetails > displayed_topics_


topics_rviz_plugin
Author(s): Victor Lamoine - Institut Maupertuis
autogenerated on Mon Feb 28 2022 23:53:11