select_topic_dialog.cpp
Go to the documentation of this file.
1 // *****************************************************************************
2 //
3 // Copyright (c) 2014, Southwest Research Institute® (SwRI®)
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Southwest Research Institute® (SwRI®) nor the
14 // names of its contributors may be used to endorse or promote products
15 // derived from this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //
28 // *****************************************************************************
30 
31 #include <algorithm>
32 
33 #include <QListWidget>
34 #include <QLineEdit>
35 #include <QVBoxLayout>
36 #include <QHBoxLayout>
37 #include <QPushButton>
38 #include <QLabel>
39 #include <QTimerEvent>
40 
41 namespace mapviz
42 {
44  const std::string &datatype,
45  QWidget *parent)
46 {
47  std::vector<std::string> datatypes;
48  datatypes.push_back(datatype);
49  return selectTopic(datatypes, parent);
50 }
51 
53  const std::string &datatype1,
54  const std::string &datatype2,
55  QWidget *parent)
56 {
57  std::vector<std::string> datatypes;
58  datatypes.push_back(datatype1);
59  datatypes.push_back(datatype2);
60  return selectTopic(datatypes, parent);
61 }
62 
64  const std::vector<std::string> &datatypes,
65  QWidget *parent)
66 {
67  SelectTopicDialog dialog(parent);
68  dialog.allowMultipleTopics(false);
69  dialog.setDatatypeFilter(datatypes);
70  if (dialog.exec() == QDialog::Accepted) {
71  return dialog.selectedTopic();
72  } else {
73  return ros::master::TopicInfo();
74  }
75 }
76 
77 std::vector<ros::master::TopicInfo> SelectTopicDialog::selectTopics(
78  const std::string &datatype,
79  QWidget *parent)
80 {
81  std::vector<std::string> datatypes;
82  datatypes.push_back(datatype);
83  return selectTopics(datatypes, parent);
84 }
85 
86 std::vector<ros::master::TopicInfo> SelectTopicDialog::selectTopics(
87  const std::string &datatype1,
88  const std::string &datatype2,
89  QWidget *parent)
90 {
91  std::vector<std::string> datatypes;
92  datatypes.push_back(datatype1);
93  datatypes.push_back(datatype2);
94  return selectTopics(datatypes, parent);
95 }
96 
97 std::vector<ros::master::TopicInfo> SelectTopicDialog::selectTopics(
98  const std::vector<std::string> &datatypes,
99  QWidget *parent)
100 {
101  SelectTopicDialog dialog(parent);
102  dialog.allowMultipleTopics(true);
103  dialog.setDatatypeFilter(datatypes);
104  if (dialog.exec() == QDialog::Accepted) {
105  return dialog.selectedTopics();
106  } else {
107  return std::vector<ros::master::TopicInfo>();
108  }
109 }
110 
112  :
113  ok_button_(new QPushButton("&Ok")),
114  cancel_button_(new QPushButton("&Cancel")),
115  list_widget_(new QListWidget()),
116  name_filter_(new QLineEdit())
117 {
118  QHBoxLayout *filter_box = new QHBoxLayout();
119  filter_box->addWidget(new QLabel("Filter:"));
120  filter_box->addWidget(name_filter_);
121 
122  QHBoxLayout *button_box = new QHBoxLayout();
123  button_box->addStretch(1);
124  button_box->addWidget(cancel_button_);
125  button_box->addWidget(ok_button_);
126 
127  QVBoxLayout *vbox = new QVBoxLayout();
128  vbox->addWidget(list_widget_);
129  vbox->addLayout(filter_box);
130  vbox->addLayout(button_box);
131  setLayout(vbox);
132 
133  connect(ok_button_, SIGNAL(clicked(bool)),
134  this, SLOT(accept()));
135  connect(cancel_button_, SIGNAL(clicked(bool)),
136  this, SLOT(reject()));
137  connect(name_filter_, SIGNAL(textChanged(const QString &)),
138  this, SLOT(updateDisplayedTopics()));
139 
140  ok_button_->setDefault(true);
141 
142  allowMultipleTopics(false);
143  setWindowTitle("Select topics...");
144 
145  fetch_topics_timer_id_ = startTimer(1000);
146  fetchTopics();
147 }
148 
149 void SelectTopicDialog::timerEvent(QTimerEvent *event)
150 {
151  if (event->timerId() == fetch_topics_timer_id_) {
152  fetchTopics();
153  }
154 }
155 
156 void SelectTopicDialog::closeEvent(QCloseEvent *event)
157 {
158  // We don't need to keep making requests from the ROS master.
159  killTimer(fetch_topics_timer_id_);
160  QDialog::closeEvent(event);
161 }
162 
164  bool allow)
165 {
166  if (allow) {
167  list_widget_->setSelectionMode(QAbstractItemView::MultiSelection);
168  } else {
169  list_widget_->setSelectionMode(QAbstractItemView::SingleSelection);
170  }
171 }
172 
174  const std::vector<std::string> &datatypes)
175 {
176  allowed_datatypes_.clear();
177  for (size_t i = 0; i < datatypes.size(); i++) {
178  allowed_datatypes_.insert(datatypes[i]);
179  }
181 }
182 
184 {
185  std::vector<ros::master::TopicInfo> selection = selectedTopics();
186  if (selection.empty()) {
187  return ros::master::TopicInfo();
188  } else {
189  return selection.front();
190  }
191 }
192 
193 std::vector<ros::master::TopicInfo> SelectTopicDialog::selectedTopics() const
194 {
195  QModelIndexList qt_selection = list_widget_->selectionModel()->selectedIndexes();
196 
197  std::vector<ros::master::TopicInfo> selection;
198  selection.resize(qt_selection.size());
199  for (int i = 0; i < qt_selection.size(); i++) {
200  if (!qt_selection[i].isValid()) {
201  continue;
202  }
203 
204  int row = qt_selection[i].row();
205  if (row < 0 || static_cast<size_t>(row) >= displayed_topics_.size()) {
206  continue;
207  }
208 
209  selection[i] = displayed_topics_[row];
210  }
211 
212  return selection;
213 }
214 
215 static bool topicSort(const ros::master::TopicInfo &info1,
216  const ros::master::TopicInfo &info2)
217 {
218  return info1.name < info2.name;
219 }
220 
222 {
224  std::sort(known_topics_.begin(), known_topics_.end(), topicSort);
226 }
227 
228 std::vector<ros::master::TopicInfo> SelectTopicDialog::filterTopics(
229  const std::vector<ros::master::TopicInfo> &topics) const
230 {
231  QString topic_filter = name_filter_->text();
232  std::vector<ros::master::TopicInfo> filtered;
233 
234  for (size_t i = 0; i < topics.size(); i++) {
235  if (!allowed_datatypes_.empty() &&
236  allowed_datatypes_.count(topics[i].datatype) == 0) {
237  continue;
238  }
239 
240  QString topic_name = QString::fromStdString(topics[i].name);
241  if (!topic_filter.isEmpty() &&
242  !topic_name.contains(topic_filter, Qt::CaseInsensitive)) {
243  continue;
244  }
245 
246  filtered.push_back(topics[i]);
247  }
248 
249  return filtered;
250 }
251 
253 {
254  std::vector<ros::master::TopicInfo> next_displayed_topics = filterTopics(known_topics_);
255 
256  // It's a lot more work to keep track of the additions/removals like
257  // this compared to resetting the QListWidget's items each time, but
258  // it allows Qt to properly track the selection and current items
259  // across updates, which results in much less frustration for the user.
260 
261  std::set<std::string> prev_names;
262  for (size_t i = 0; i < displayed_topics_.size(); i++) {
263  prev_names.insert(displayed_topics_[i].name);
264  }
265 
266  std::set<std::string> next_names;
267  for (size_t i = 0; i < next_displayed_topics.size(); i++) {
268  next_names.insert(next_displayed_topics[i].name);
269  }
270 
271  std::set<std::string> added_names;
272  std::set_difference(next_names.begin(), next_names.end(),
273  prev_names.begin(), prev_names.end(),
274  std::inserter(added_names, added_names.end()));
275 
276  std::set<std::string> removed_names;
277  std::set_difference(prev_names.begin(), prev_names.end(),
278  next_names.begin(), next_names.end(),
279  std::inserter(removed_names, removed_names.end()));
280 
281  // Remove all the removed names
282  size_t removed = 0;
283  for (size_t i = 0; i < displayed_topics_.size(); i++) {
284  if (removed_names.count(displayed_topics_[i].name) == 0) {
285  continue;
286  }
287 
288  QListWidgetItem *item = list_widget_->takeItem(i - removed);
289  delete item;
290  removed++;
291  }
292 
293  // Now we can add the new items.
294  for (size_t i = 0; i < next_displayed_topics.size(); i++) {
295  if (added_names.count(next_displayed_topics[i].name) == 0) {
296  continue;
297  }
298 
299  list_widget_->insertItem(i, QString::fromStdString(next_displayed_topics[i].name));
300  if (list_widget_->count() == 1) {
301  list_widget_->setCurrentRow(0);
302  }
303  }
304 
305  displayed_topics_.swap(next_displayed_topics);
306 }
307 } // namespace mapviz
std::set< std::string > allowed_datatypes_
string name
static ros::master::TopicInfo selectTopic(const std::string &datatype, QWidget *parent=0)
void setDatatypeFilter(const std::vector< std::string > &datatypes)
ros::master::TopicInfo selectedTopic() const
std::vector< ros::master::TopicInfo > filterTopics(const std::vector< ros::master::TopicInfo > &) const
ROSCPP_DECL bool getTopics(V_TopicInfo &topics)
std::vector< ros::master::TopicInfo > known_topics_
static bool topicSort(const ros::master::TopicInfo &info1, const ros::master::TopicInfo &info2)
std::vector< ros::master::TopicInfo > displayed_topics_
SelectTopicDialog(QWidget *parent=0)
std::vector< ros::master::TopicInfo > selectedTopics() const
static std::vector< ros::master::TopicInfo > selectTopics(const std::string &datatype, QWidget *parent=0)


mapviz
Author(s): Marc Alban
autogenerated on Fri Mar 19 2021 02:44:25