select_service_dialog.cpp
Go to the documentation of this file.
00001 // *****************************************************************************
00002 //
00003 // Copyright (c) 2015, 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 Southwest Research Institute® BE LIABLE 
00021 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
00022 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
00023 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
00024 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00025 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
00026 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00027 // DAMAGE.
00028 //
00029 // *****************************************************************************
00030 
00031 #include <mapviz/select_service_dialog.h>
00032 
00033 #include <QCloseEvent>
00034 #include <QHBoxLayout>
00035 #include <QLabel>
00036 #include <QLineEdit>
00037 #include <QListWidget>
00038 #include <QMessageBox>
00039 #include <QPushButton>
00040 #include <QTimerEvent>
00041 #include <QVBoxLayout>
00042 
00043 #include <rosapi/Services.h>
00044 #include <rosapi/ServicesForType.h>
00045 #include <QtGui/QMessageBox>
00046 
00047 namespace mapviz
00048 {
00049   void ServiceUpdaterThread::run()
00050   {
00051     ros::ServiceClient client;
00052 
00053     if (allowed_datatype_.empty())
00054     {
00055       client = nh_.serviceClient<rosapi::Services>("/rosapi/services");
00056     }
00057     else
00058     {
00059       client = nh_.serviceClient<rosapi::ServicesForType>("/rosapi/services_for_type");
00060     }
00061 
00062     if (!client.waitForExistence(ros::Duration(1)))
00063     {
00064       // Check to see whether the rosapi services are actually running.
00065       Q_EMIT fetchingFailed(tr("Unable to list ROS services.  Is rosapi_node running?"));
00066       return;
00067     }
00068 
00069     if (allowed_datatype_.empty())
00070     {
00071       rosapi::Services srv;
00072 
00073       ROS_DEBUG("Listing all services.");
00074       if (client.call(srv))
00075       {
00076         Q_EMIT servicesFetched(srv.response.services);
00077       }
00078     }
00079     else {
00080       rosapi::ServicesForType srv;
00081       srv.request.type = allowed_datatype_;
00082 
00083       ROS_DEBUG("Listing services for type %s", srv.request.type.c_str());
00084       if (client.call(srv))
00085       {
00086         Q_EMIT servicesFetched(srv.response.services);
00087       }
00088       else
00089       {
00090         // If there are any dead or unreachable nodes that provide services, even if
00091         // they're not of the service type we're looking for, the services_for_type
00092         // service will have an error and not return anything.  Super annoying.
00093         Q_EMIT fetchingFailed(tr("Unable to list ROS services.  You may have " \
00094                               "dead nodes; try running \"rosnode cleanup\"."));
00095       }
00096     }
00097   }
00098 
00099   std::string SelectServiceDialog::selectService(const std::string& datatype, QWidget* parent)
00100   {
00101     SelectServiceDialog dialog(datatype, parent);
00102     dialog.setDatatypeFilter(datatype);
00103     if (dialog.exec() == QDialog::Accepted) {
00104       return dialog.selectedService();
00105     } else {
00106       return "";
00107     }
00108   }
00109 
00110   SelectServiceDialog::SelectServiceDialog(const std::string& datatype, QWidget* parent)
00111       :
00112       QDialog(parent),
00113       allowed_datatype_(datatype),
00114       cancel_button_(new QPushButton("&Cancel")),
00115       list_widget_(new QListWidget()),
00116       name_filter_(new QLineEdit()),
00117       ok_button_(new QPushButton("&Ok"))
00118   {
00119     QHBoxLayout *filter_box = new QHBoxLayout();
00120     filter_box->addWidget(new QLabel("Filter:"));
00121     filter_box->addWidget(name_filter_);
00122 
00123     QHBoxLayout *button_box = new QHBoxLayout();
00124     button_box->addStretch(1);
00125     button_box->addWidget(cancel_button_);
00126     button_box->addWidget(ok_button_);
00127 
00128     QVBoxLayout *vbox = new QVBoxLayout();
00129     vbox->addWidget(list_widget_);
00130     vbox->addLayout(filter_box);
00131     vbox->addLayout(button_box);
00132     setLayout(vbox);
00133 
00134     // This is ugly, but necessary in order to be able to send a std::vector<std::string>
00135     // via a queued signal/slot connection.
00136     qRegisterMetaType<ServiceStringVector>("ServiceStringVector");
00137 
00138     connect(ok_button_, SIGNAL(clicked(bool)),
00139             this, SLOT(accept()));
00140     connect(cancel_button_, SIGNAL(clicked(bool)),
00141             this, SLOT(reject()));
00142     connect(name_filter_, SIGNAL(textChanged(const QString &)),
00143             this, SLOT(updateDisplayedServices()));
00144 
00145     ok_button_->setDefault(true);
00146 
00147     setWindowTitle("Select service...");
00148 
00149     fetch_services_timer_id_ = startTimer(5000);
00150     fetchServices();
00151   }
00152 
00153   SelectServiceDialog::~SelectServiceDialog()
00154   {
00155     if (worker_thread_)
00156     {
00157       // If the thread's parent is destroyed before the thread has finished,
00158       // it will cause a segmentation fault.  We'll wait a few seconds for
00159       // it to finish cleanly, and if that doesn't work, try to force it to
00160       // die and wait a few more.
00161       worker_thread_->wait(5000);
00162       if (worker_thread_->isRunning())
00163       {
00164         worker_thread_->terminate();
00165         worker_thread_->wait(2000);
00166       }
00167     }
00168   }
00169 
00170   void SelectServiceDialog::fetchServices()
00171   {
00172     // If we don't currently have a worker thread or the previous one has
00173     // finished, start a new one.
00174     if (!worker_thread_ || worker_thread_->isFinished())
00175     {
00176       worker_thread_.reset(new ServiceUpdaterThread(nh_, allowed_datatype_, this));
00177       QObject::connect(worker_thread_.get(),
00178                        SIGNAL(servicesFetched(ServiceStringVector)),
00179                        this,
00180                        SLOT(updateKnownServices(ServiceStringVector)));
00181       QObject::connect(worker_thread_.get(),
00182                        SIGNAL(fetchingFailed(const QString)),
00183                        this,
00184                        SLOT(displayUpdateError(const QString)));
00185       worker_thread_->start();
00186     }
00187   }
00188 
00189   void SelectServiceDialog::updateKnownServices(ServiceStringVector services)
00190   {
00191     known_services_ = services;
00192     updateDisplayedServices();
00193   }
00194 
00195   void SelectServiceDialog::displayUpdateError(const QString error_msg)
00196   {
00197     killTimer(fetch_services_timer_id_);
00198     QMessageBox mbox(this->parentWidget());
00199     mbox.setIcon(QMessageBox::Warning);
00200     mbox.setText(error_msg);
00201     mbox.exec();
00202   }
00203 
00204   std::vector<std::string> SelectServiceDialog::filterServices()
00205   {
00206     std::vector<std::string> filtered_services;
00207 
00208     QString filter_text = name_filter_->text();
00209 
00210     Q_FOREACH(const std::string& service, known_services_)
00211     {
00212       if (QString::fromStdString(service).contains(filter_text, Qt::CaseInsensitive))
00213       {
00214         filtered_services.push_back(service);
00215       }
00216     }
00217 
00218     return filtered_services;
00219   }
00220 
00221   void SelectServiceDialog::updateDisplayedServices()
00222   {
00223     std::vector<std::string> next_displayed_services = filterServices();
00224 
00225     // It's a lot more work to keep track of the additions/removals like
00226     // this compared to resetting the QListWidget's items each time, but
00227     // it allows Qt to properly track the selection and current items
00228     // across updates, which results in much less frustration for the user.
00229 
00230     std::set<std::string> prev_names;
00231     for (size_t i = 0; i < displayed_services_.size(); i++) {
00232       prev_names.insert(displayed_services_[i]);
00233     }
00234 
00235     std::set<std::string> next_names;
00236     for (size_t i = 0; i < next_displayed_services.size(); i++) {
00237       next_names.insert(next_displayed_services[i]);
00238     }
00239 
00240     std::set<std::string> added_names;
00241     std::set_difference(next_names.begin(), next_names.end(),
00242                         prev_names.begin(), prev_names.end(),
00243                         std::inserter(added_names, added_names.end()));
00244 
00245     std::set<std::string> removed_names;
00246     std::set_difference(prev_names.begin(), prev_names.end(),
00247                         next_names.begin(), next_names.end(),
00248                         std::inserter(removed_names, removed_names.end()));
00249 
00250     // Remove all the removed names
00251     size_t removed = 0;
00252     for (size_t i = 0; i < displayed_services_.size(); i++) {
00253       if (removed_names.count(displayed_services_[i]) == 0) {
00254         continue;
00255       }
00256 
00257       QListWidgetItem *item = list_widget_->takeItem(i - removed);
00258       delete item;
00259       removed++;
00260     }
00261 
00262     // Now we can add the new items.
00263     for (size_t i = 0; i < next_displayed_services.size(); i++) {
00264       if (added_names.count(next_displayed_services[i]) == 0) {
00265         continue;
00266       }
00267 
00268       list_widget_->insertItem(i, QString::fromStdString(next_displayed_services[i]));
00269       if (list_widget_->count() == 1) {
00270         list_widget_->setCurrentRow(0);
00271       }
00272     }
00273 
00274     displayed_services_.swap(next_displayed_services);
00275   }
00276 
00277   void SelectServiceDialog::setDatatypeFilter(const std::string& datatype)
00278   {
00279     allowed_datatype_ = datatype;
00280     updateDisplayedServices();
00281   }
00282 
00283   std::string SelectServiceDialog::selectedService() const
00284   {
00285     QModelIndex qt_selection = list_widget_->selectionModel()->currentIndex();
00286 
00287     if (qt_selection.isValid()) {
00288       int row = qt_selection.row();
00289       if (row < static_cast<int>(displayed_services_.size())) {
00290         return displayed_services_[row];
00291       }
00292     }
00293 
00294     return "";
00295   }
00296 
00297   void SelectServiceDialog::timerEvent(QTimerEvent* event)
00298   {
00299       if (event->timerId() == fetch_services_timer_id_) {
00300         fetchServices();
00301       }
00302   }
00303 
00304   void SelectServiceDialog::closeEvent(QCloseEvent* event)
00305   {
00306     // We don't need to keep making requests from the ROS master.
00307     killTimer(fetch_services_timer_id_);
00308     QDialog::closeEvent(event);
00309   }
00310 }


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