select_frame_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_frame_dialog.h>
00030 
00031 #include <algorithm>
00032 #include <set>
00033 
00034 #include <QListWidget>
00035 #include <QLineEdit>
00036 #include <QVBoxLayout>
00037 #include <QHBoxLayout>
00038 #include <QPushButton>
00039 #include <QLabel>
00040 #include <QTimerEvent>
00041 
00042 #include <tf/transform_listener.h>
00043 
00044 namespace mapviz
00045 {
00046 std::string SelectFrameDialog::selectFrame(
00047   boost::shared_ptr<tf::TransformListener> tf_listener,
00048   QWidget *parent)
00049 {
00050   SelectFrameDialog dialog(tf_listener, parent);
00051   dialog.allowMultipleFrames(false);
00052   if (dialog.exec() == QDialog::Accepted) {
00053     return dialog.selectedFrame();
00054   } else {
00055     return "";
00056   }
00057 }
00058 
00059 std::vector<std::string> SelectFrameDialog::selectFrames(
00060   boost::shared_ptr<tf::TransformListener> tf_listener,
00061   QWidget *parent)
00062 {
00063   SelectFrameDialog dialog(tf_listener, parent);
00064   dialog.allowMultipleFrames(true);
00065   if (dialog.exec() == QDialog::Accepted) {
00066     return dialog.selectedFrames();
00067   } else {
00068     return std::vector<std::string>();
00069   }
00070 }
00071 
00072 SelectFrameDialog::SelectFrameDialog(
00073   boost::shared_ptr<tf::TransformListener> tf_listener,
00074   QWidget *parent)
00075   :
00076   tf_(tf_listener),
00077   ok_button_(new QPushButton("&Ok")),
00078   cancel_button_(new QPushButton("&Cancel")),
00079   list_widget_(new QListWidget()),
00080   name_filter_(new QLineEdit())
00081 {
00082   QHBoxLayout *filter_box = new QHBoxLayout();
00083   filter_box->addWidget(new QLabel("Filter:"));
00084   filter_box->addWidget(name_filter_);
00085 
00086   QHBoxLayout *button_box = new QHBoxLayout();
00087   button_box->addStretch(1);
00088   button_box->addWidget(cancel_button_);
00089   button_box->addWidget(ok_button_);
00090 
00091   QVBoxLayout *vbox = new QVBoxLayout();
00092   vbox->addWidget(list_widget_);
00093   vbox->addLayout(filter_box);
00094   vbox->addLayout(button_box);
00095   setLayout(vbox);
00096 
00097   connect(ok_button_, SIGNAL(clicked(bool)),
00098           this, SLOT(accept()));
00099   connect(cancel_button_, SIGNAL(clicked(bool)),
00100           this, SLOT(reject()));
00101   connect(name_filter_, SIGNAL(textChanged(const QString &)),
00102           this, SLOT(updateDisplayedFrames()));
00103 
00104   ok_button_->setDefault(true);
00105   
00106   allowMultipleFrames(false);
00107   setWindowTitle("Select frames...");
00108 
00109   resize(600, 600);
00110   
00111   fetch_frames_timer_id_ = startTimer(1000);
00112   fetchFrames();
00113 }
00114 
00115 void SelectFrameDialog::timerEvent(QTimerEvent *event)
00116 {  
00117   if (event->timerId() == fetch_frames_timer_id_) {
00118     fetchFrames();
00119   }
00120 }
00121 
00122 void SelectFrameDialog::closeEvent(QCloseEvent *event)
00123 {
00124   // We don't need to keep making requests from the ROS master.
00125   killTimer(fetch_frames_timer_id_);
00126   QDialog::closeEvent(event);
00127 }
00128 
00129 void SelectFrameDialog::allowMultipleFrames(
00130   bool allow)
00131 {
00132   if (allow) {
00133     list_widget_->setSelectionMode(QAbstractItemView::MultiSelection);
00134   } else {
00135     list_widget_->setSelectionMode(QAbstractItemView::SingleSelection);
00136   }
00137 }
00138 
00139 std::string SelectFrameDialog::selectedFrame() const
00140 {
00141   std::vector<std::string> selection = selectedFrames();
00142   if (selection.empty()) {
00143     return "";
00144   } else {
00145     return selection.front();
00146   }
00147 }
00148 
00149 std::vector<std::string> SelectFrameDialog::selectedFrames() const
00150 {
00151   QModelIndexList qt_selection = list_widget_->selectionModel()->selectedIndexes();
00152 
00153   std::vector<std::string> selection;
00154   selection.resize(qt_selection.size());
00155   for (int i = 0; i < qt_selection.size(); i++) {
00156     if (!qt_selection[i].isValid()) {
00157       continue;
00158     }
00159     
00160     int row = qt_selection[i].row();
00161     if (row < 0 || static_cast<size_t>(row) >= displayed_frames_.size()) {
00162       continue;
00163     }
00164     
00165     selection[i] = displayed_frames_[row];
00166   }
00167 
00168   return selection;
00169 }
00170 
00171 void SelectFrameDialog::fetchFrames()
00172 {
00173   if (!tf_) { return; }
00174   
00175   known_frames_.clear();
00176   tf_->getFrameStrings(known_frames_);
00177   std::sort(known_frames_.begin(), known_frames_.end());
00178   updateDisplayedFrames();
00179 }
00180 
00181 std::vector<std::string> SelectFrameDialog::filterFrames(
00182   const std::vector<std::string> &frames) const
00183 {
00184   QString frame_filter = name_filter_->text();
00185   std::vector<std::string> filtered;
00186 
00187   for (size_t i = 0; i < frames.size(); i++) {
00188     QString frame_name = QString::fromStdString(frames[i]);
00189     if (!frame_filter.isEmpty() &&
00190         !frame_name.contains(frame_filter, Qt::CaseInsensitive)) {
00191       continue;
00192     }
00193 
00194     filtered.push_back(frames[i]);
00195   }
00196   
00197   return filtered;  
00198 }
00199 
00200 void SelectFrameDialog::updateDisplayedFrames()
00201 {
00202   std::vector<std::string> next_displayed_frames = filterFrames(known_frames_);
00203   
00204   // It's a lot more work to keep track of the additions/removals like
00205   // this compared to resetting the QListWidget's items each time, but
00206   // it allows Qt to properly track the selection and current items
00207   // across updates, which results in much less frustration for the user.
00208   
00209   std::set<std::string> prev_names;
00210   prev_names.insert(displayed_frames_.begin(), displayed_frames_.end());
00211   
00212   std::set<std::string> next_names;
00213   next_names.insert(next_displayed_frames.begin(), next_displayed_frames.end());
00214 
00215   std::set<std::string> added_names;
00216   std::set_difference(next_names.begin(), next_names.end(),
00217                       prev_names.begin(), prev_names.end(),
00218                       std::inserter(added_names, added_names.end()));
00219 
00220   std::set<std::string> removed_names;
00221   std::set_difference(prev_names.begin(), prev_names.end(),
00222                       next_names.begin(), next_names.end(),
00223                       std::inserter(removed_names, removed_names.end()));
00224 
00225   // Remove all the removed names
00226   size_t removed = 0;
00227   for (size_t i = 0; i < displayed_frames_.size(); i++) {
00228     if (removed_names.count(displayed_frames_[i]) == 0) {
00229       continue;
00230     }
00231 
00232     QListWidgetItem *item = list_widget_->takeItem(i - removed);
00233     delete item;
00234     removed++;
00235   }
00236 
00237   // Now we can add the new items.
00238   for (size_t i = 0; i < next_displayed_frames.size(); i++) {
00239     if (added_names.count(next_displayed_frames[i]) == 0) {
00240       continue;
00241     }
00242 
00243     list_widget_->insertItem(i, QString::fromStdString(next_displayed_frames[i]));
00244     if (list_widget_->count() == 1) {
00245       list_widget_->setCurrentRow(0);
00246     }
00247   }
00248 
00249   displayed_frames_.swap(next_displayed_frames);  
00250 }
00251 }  // namespace mapviz


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