select_topic_dialog.cpp
Go to the documentation of this file.
00001 // *****************************************************************************
00002 //
00003 // Copyright (c) 2014, Southwest Research Institute® (SwRI®)
00004 // All rights reserved.
00005 //
00006 // Redistribution and use in source and binary forms, with or without
00007 // modification, are permitted provided that the following conditions are met:
00008 //     * Redistributions of source code must retain the above copyright
00009 //       notice, this list of conditions and the following disclaimer.
00010 //     * Redistributions in binary form must reproduce the above copyright
00011 //       notice, this list of conditions and the following disclaimer in the
00012 //       documentation and/or other materials provided with the distribution.
00013 //     * Neither the name of Southwest Research Institute® (SwRI®) nor the
00014 //       names of its contributors may be used to endorse or promote products
00015 //       derived from this software without specific prior written permission.
00016 //
00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 // ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 //
00028 // *****************************************************************************
00029 #include <mapviz/select_topic_dialog.h>
00030 
00031 #include <algorithm>
00032 
00033 #include <QListWidget>
00034 #include <QLineEdit>
00035 #include <QVBoxLayout>
00036 #include <QHBoxLayout>
00037 #include <QPushButton>
00038 #include <QLabel>
00039 #include <QTimerEvent>
00040 
00041 namespace mapviz
00042 {
00043 ros::master::TopicInfo SelectTopicDialog::selectTopic(
00044   const std::string &datatype,                                 
00045   QWidget *parent)
00046 {
00047   std::vector<std::string> datatypes;
00048   datatypes.push_back(datatype);
00049   return selectTopic(datatypes, parent);
00050 }
00051 
00052 ros::master::TopicInfo SelectTopicDialog::selectTopic(
00053   const std::string &datatype1,
00054   const std::string &datatype2,
00055   QWidget *parent)
00056 {
00057   std::vector<std::string> datatypes;
00058   datatypes.push_back(datatype1);
00059   datatypes.push_back(datatype2);
00060   return selectTopic(datatypes, parent);
00061 }
00062 
00063 ros::master::TopicInfo SelectTopicDialog::selectTopic(
00064   const std::vector<std::string> &datatypes,
00065   QWidget *parent)
00066 {
00067   SelectTopicDialog dialog(parent);
00068   dialog.allowMultipleTopics(false);
00069   dialog.setDatatypeFilter(datatypes);
00070   if (dialog.exec() == QDialog::Accepted) {
00071     return dialog.selectedTopic();
00072   } else {
00073     return ros::master::TopicInfo();
00074   }
00075 }
00076 
00077 std::vector<ros::master::TopicInfo> SelectTopicDialog::selectTopics(
00078   const std::string &datatype,                                 
00079   QWidget *parent)
00080 {
00081   std::vector<std::string> datatypes;
00082   datatypes.push_back(datatype);
00083   return selectTopics(datatypes, parent);  
00084 }
00085 
00086 std::vector<ros::master::TopicInfo> SelectTopicDialog::selectTopics(
00087   const std::string &datatype1,
00088   const std::string &datatype2,
00089   QWidget *parent)
00090 {
00091   std::vector<std::string> datatypes;
00092   datatypes.push_back(datatype1);
00093   datatypes.push_back(datatype2);
00094   return selectTopics(datatypes, parent);  
00095 }
00096 
00097 std::vector<ros::master::TopicInfo> SelectTopicDialog::selectTopics(
00098   const std::vector<std::string> &datatypes,
00099   QWidget *parent)
00100 {
00101   SelectTopicDialog dialog(parent);
00102   dialog.allowMultipleTopics(true);
00103   dialog.setDatatypeFilter(datatypes);
00104   if (dialog.exec() == QDialog::Accepted) {
00105     return dialog.selectedTopics();
00106   } else {
00107     return std::vector<ros::master::TopicInfo>();
00108   }
00109 }
00110 
00111 SelectTopicDialog::SelectTopicDialog(QWidget *parent)
00112   :
00113   ok_button_(new QPushButton("&Ok")),
00114   cancel_button_(new QPushButton("&Cancel")),
00115   list_widget_(new QListWidget()),
00116   name_filter_(new QLineEdit())
00117 {
00118   QHBoxLayout *filter_box = new QHBoxLayout();
00119   filter_box->addWidget(new QLabel("Filter:"));
00120   filter_box->addWidget(name_filter_);
00121 
00122   QHBoxLayout *button_box = new QHBoxLayout();
00123   button_box->addStretch(1);
00124   button_box->addWidget(cancel_button_);
00125   button_box->addWidget(ok_button_);
00126 
00127   QVBoxLayout *vbox = new QVBoxLayout();
00128   vbox->addWidget(list_widget_);
00129   vbox->addLayout(filter_box);
00130   vbox->addLayout(button_box);
00131   setLayout(vbox);
00132 
00133   connect(ok_button_, SIGNAL(clicked(bool)),
00134           this, SLOT(accept()));
00135   connect(cancel_button_, SIGNAL(clicked(bool)),
00136           this, SLOT(reject()));
00137   connect(name_filter_, SIGNAL(textChanged(const QString &)),
00138           this, SLOT(updateDisplayedTopics()));
00139 
00140   ok_button_->setDefault(true);
00141   
00142   allowMultipleTopics(false);
00143   setWindowTitle("Select topics...");
00144 
00145   fetch_topics_timer_id_ = startTimer(1000);
00146   fetchTopics();
00147 }
00148 
00149 void SelectTopicDialog::timerEvent(QTimerEvent *event)
00150 {
00151   if (event->timerId() == fetch_topics_timer_id_) {
00152     fetchTopics();
00153   }
00154 }
00155 
00156 void SelectTopicDialog::closeEvent(QCloseEvent *event)
00157 {
00158   // We don't need to keep making requests from the ROS master.
00159   killTimer(fetch_topics_timer_id_);
00160   QDialog::closeEvent(event);
00161 }
00162 
00163 void SelectTopicDialog::allowMultipleTopics(
00164   bool allow)
00165 {
00166   if (allow) {
00167     list_widget_->setSelectionMode(QAbstractItemView::MultiSelection);
00168   } else {
00169     list_widget_->setSelectionMode(QAbstractItemView::SingleSelection);
00170   }
00171 }
00172 
00173 void SelectTopicDialog::setDatatypeFilter(
00174   const std::vector<std::string> &datatypes)
00175 {
00176   allowed_datatypes_.clear();
00177   for (size_t i = 0; i < datatypes.size(); i++) {
00178     allowed_datatypes_.insert(datatypes[i]);
00179   }
00180   updateDisplayedTopics();
00181 }
00182 
00183 ros::master::TopicInfo SelectTopicDialog::selectedTopic() const
00184 {
00185   std::vector<ros::master::TopicInfo> selection = selectedTopics();
00186   if (selection.empty()) {
00187     return ros::master::TopicInfo();
00188   } else {
00189     return selection.front();
00190   }
00191 }
00192 
00193 std::vector<ros::master::TopicInfo> SelectTopicDialog::selectedTopics() const
00194 {
00195   QModelIndexList qt_selection = list_widget_->selectionModel()->selectedIndexes();
00196 
00197   std::vector<ros::master::TopicInfo> selection;
00198   selection.resize(qt_selection.size());
00199   for (int i = 0; i < qt_selection.size(); i++) {
00200     if (!qt_selection[i].isValid()) {
00201       continue;
00202     }
00203     
00204     int row = qt_selection[i].row();
00205     if (row < 0 || static_cast<size_t>(row) >= displayed_topics_.size()) {
00206       continue;
00207     }
00208     
00209     selection[i] = displayed_topics_[row];
00210   }
00211 
00212   return selection;
00213 }
00214 
00215 static bool topicSort(const ros::master::TopicInfo &info1,
00216                       const ros::master::TopicInfo &info2)
00217 {
00218   return info1.name < info2.name;
00219 }
00220 
00221 void SelectTopicDialog::fetchTopics()
00222 {
00223   ros::master::getTopics(known_topics_);
00224   std::sort(known_topics_.begin(), known_topics_.end(), topicSort);
00225   updateDisplayedTopics();
00226 }
00227 
00228 std::vector<ros::master::TopicInfo> SelectTopicDialog::filterTopics(
00229   const std::vector<ros::master::TopicInfo> &topics) const
00230 {
00231   QString topic_filter = name_filter_->text();
00232   std::vector<ros::master::TopicInfo> filtered;
00233 
00234   for (size_t i = 0; i < topics.size(); i++) {
00235     if (!allowed_datatypes_.empty() &&
00236         allowed_datatypes_.count(topics[i].datatype) == 0) {
00237       continue;
00238     }
00239 
00240     QString topic_name = QString::fromStdString(topics[i].name);
00241     if (!topic_filter.isEmpty() &&
00242         !topic_name.contains(topic_filter, Qt::CaseInsensitive)) {
00243       continue;
00244     }
00245 
00246     filtered.push_back(topics[i]);
00247   }
00248   
00249   return filtered;  
00250 }
00251 
00252 void SelectTopicDialog::updateDisplayedTopics()
00253 {
00254   std::vector<ros::master::TopicInfo> next_displayed_topics = filterTopics(known_topics_);
00255   
00256   // It's a lot more work to keep track of the additions/removals like
00257   // this compared to resetting the QListWidget's items each time, but
00258   // it allows Qt to properly track the selection and current items
00259   // across updates, which results in much less frustration for the user.
00260   
00261   std::set<std::string> prev_names;
00262   for (size_t i = 0; i < displayed_topics_.size(); i++) {
00263     prev_names.insert(displayed_topics_[i].name);
00264   }
00265   
00266   std::set<std::string> next_names;
00267   for (size_t i = 0; i < next_displayed_topics.size(); i++) {
00268     next_names.insert(next_displayed_topics[i].name);
00269   }
00270 
00271   std::set<std::string> added_names;
00272   std::set_difference(next_names.begin(), next_names.end(),
00273                       prev_names.begin(), prev_names.end(),
00274                       std::inserter(added_names, added_names.end()));
00275 
00276   std::set<std::string> removed_names;
00277   std::set_difference(prev_names.begin(), prev_names.end(),
00278                       next_names.begin(), next_names.end(),
00279                       std::inserter(removed_names, removed_names.end()));
00280 
00281   // Remove all the removed names
00282   size_t removed = 0;
00283   for (size_t i = 0; i < displayed_topics_.size(); i++) {
00284     if (removed_names.count(displayed_topics_[i].name) == 0) {
00285       continue;
00286     }
00287 
00288     QListWidgetItem *item = list_widget_->takeItem(i - removed);
00289     delete item;
00290     removed++;
00291   }
00292 
00293   // Now we can add the new items.
00294   for (size_t i = 0; i < next_displayed_topics.size(); i++) {
00295     if (added_names.count(next_displayed_topics[i].name) == 0) {
00296       continue;
00297     }
00298 
00299     list_widget_->insertItem(i, QString::fromStdString(next_displayed_topics[i].name));
00300     if (list_widget_->count() == 1) {
00301       list_widget_->setCurrentRow(0);
00302     }
00303   }
00304 
00305   displayed_topics_.swap(next_displayed_topics);  
00306 }
00307 }  // namespace mapviz


mapviz
Author(s): Marc Alban
autogenerated on Thu Aug 24 2017 02:45:59