displays_panel.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
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 the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <QTimer>
00031 #include <QHBoxLayout>
00032 #include <QVBoxLayout>
00033 #include <QPushButton>
00034 #include <QInputDialog>
00035 
00036 #include <set>
00037 
00038 #include <boost/bind.hpp>
00039 
00040 #include "displays_panel.h"
00041 #include "visualization_manager.h"
00042 #include "display.h"
00043 #include "display_wrapper.h"
00044 #include "new_display_dialog.h"
00045 #include "properties/property.h"
00046 #include "properties/property_manager.h"
00047 #include "properties/property_tree_with_help.h"
00048 #include "properties/property_tree_widget.h"
00049 #include "properties/property_widget_item.h"
00050 #include "config.h"
00051 
00052 static const std::string PROPERTY_GRID_CONFIG("Property Grid State");
00053 static const std::string PROPERTY_GRID_SPLITTER("Property Grid Splitter");
00054 
00055 namespace rviz
00056 {
00057 
00058 DisplaysPanel::DisplaysPanel( QWidget* parent )
00059   : QWidget( parent )
00060   , manager_( NULL )
00061 {
00062   tree_with_help_ = new PropertyTreeWithHelp;
00063   property_grid_ = tree_with_help_->getTree();
00064   property_grid_->setDragEnabled( true );
00065   property_grid_->setAcceptDrops( true );
00066 
00067   QPushButton* add_button = new QPushButton( "Add" );
00068   add_button->setShortcut( QKeySequence( QString( "Ctrl+N" )));
00069   add_button->setToolTip( "Add a new display, Ctrl+N" );
00070   remove_button_ = new QPushButton( "Remove" );
00071   remove_button_->setShortcut( QKeySequence( QString( "Ctrl+X" )));
00072   remove_button_->setToolTip( "Remove displays, Ctrl+X" );
00073   remove_button_->setEnabled( false );
00074   rename_button_ = new QPushButton( "Rename" );
00075   rename_button_->setShortcut( QKeySequence( QString( "Ctrl+R" )));
00076   rename_button_->setToolTip( "Rename a display, Ctrl+R" );
00077   rename_button_->setEnabled( false );
00078 
00079   QHBoxLayout* button_layout = new QHBoxLayout;
00080   button_layout->addWidget( add_button );
00081   button_layout->addWidget( remove_button_ );
00082   button_layout->addWidget( rename_button_ );
00083 
00084   QVBoxLayout* layout = new QVBoxLayout;
00085   layout->addWidget( tree_with_help_ );
00086   layout->addLayout( button_layout );
00087 
00088   setLayout( layout );
00089 
00090   connect( add_button, SIGNAL( clicked( bool )), this, SLOT( onNewDisplay() ));
00091   connect( remove_button_, SIGNAL( clicked( bool )), this, SLOT( onDeleteDisplay() ));
00092   connect( rename_button_, SIGNAL( clicked( bool )), this, SLOT( onRenameDisplay() ));
00093   connect( property_grid_, SIGNAL( itemSelectionChanged() ), this, SLOT( onSelectionChanged() ));
00094   connect( property_grid_, SIGNAL( orderChanged() ), this, SLOT( renumberDisplays() ));
00095 
00096   QTimer* timer = new QTimer( this );
00097   connect( timer, SIGNAL( timeout() ), this, SLOT( onStateChangedTimer() ));
00098   timer->start( 200 );
00099 }
00100 
00101 DisplaysPanel::~DisplaysPanel()
00102 {
00103 }
00104 
00105 void DisplaysPanel::initialize( VisualizationManager* manager )
00106 {
00107   manager_ = manager;
00108   connect( manager_, SIGNAL( displayAdding( DisplayWrapper* )), this, SLOT( onDisplayAdding( DisplayWrapper* )));
00109   connect( manager_, SIGNAL( displayAdded( DisplayWrapper* )), this, SLOT( onDisplayAdded( DisplayWrapper* )));
00110   connect( manager_, SIGNAL( displayRemoved( DisplayWrapper* )), this, SLOT( onDisplayRemoved( DisplayWrapper* )));
00111   connect( manager_, SIGNAL( displaysConfigLoaded( const boost::shared_ptr<Config>& )),
00112            this, SLOT( readFromConfig( const boost::shared_ptr<Config>& )));
00113   connect( manager_, SIGNAL( displaysConfigSaved( const boost::shared_ptr<Config>& )),
00114            this, SLOT( writeToConfig( const boost::shared_ptr<Config>& )));
00115 
00116   manager_->getPropertyManager()->setPropertyTreeWidget( property_grid_ );
00117 
00118   sortDisplays();
00119 }
00120 
00121 void DisplaysPanel::sortDisplays()
00122 {
00123   property_grid_->sortItems( 0, Qt::AscendingOrder );
00124 }
00125 
00126 DisplayWrapper* DisplaysPanel::displayWrapperFromItem( QTreeWidgetItem* selected_item )
00127 {
00128   DisplayWrapper* selected_display = 0;
00129 
00130   PropertyWidgetItem* pwi = dynamic_cast<PropertyWidgetItem*>( selected_item );
00131   if( pwi )
00132   {
00133     PropertyBase* property = pwi->getProperty();
00134 
00135     void* user_data = property->getUserData();
00136     if( user_data )
00137     {
00138       DisplayWrapper* wrapper = reinterpret_cast<DisplayWrapper*>( user_data );
00139 
00140       if( manager_->isValidDisplay( wrapper ))
00141       {
00142         selected_display = manager_->getDisplayWrapper( wrapper->getName() );
00143       }
00144       else
00145       {
00146         DisplayWrapper* wrapper = manager_->getDisplayWrapper( reinterpret_cast<Display*>( user_data ));
00147 
00148         if( wrapper )
00149         {
00150           selected_display = wrapper;
00151         }
00152       }
00153     }
00154   }
00155   return selected_display;
00156 }
00157 
00158 void DisplaysPanel::onNewDisplay()
00159 {
00160   // Get the list of current display names, so we can enforce that the
00161   // new display has a unique name.
00162   S_string current_display_names;
00163   manager_->getDisplayNames(current_display_names);
00164 
00165   std::string lookup_name;
00166   std::string display_name;
00167 
00168   NewDisplayDialog* dialog = new NewDisplayDialog( manager_->getDisplayClassLoader(),
00169                                                    current_display_names,
00170                                                    &lookup_name,
00171                                                    &display_name );
00172   if( dialog->exec() == QDialog::Accepted )
00173   {
00174     manager_->createDisplay( lookup_name, display_name, true );
00175   }
00176 }
00177 
00178 void DisplaysPanel::onDeleteDisplay()
00179 {
00180   std::set<DisplayWrapper*> displays_to_delete = getSelectedDisplays();
00181 
00182   std::set<DisplayWrapper*>::iterator di;
00183   for( di = displays_to_delete.begin(); di != displays_to_delete.end(); di++ )
00184   {
00185     manager_->removeDisplay( *di );
00186   }
00187 }
00188 
00189 std::set<DisplayWrapper*> DisplaysPanel::getSelectedDisplays()
00190 {
00191   std::set<DisplayWrapper*> displays;
00192 
00193   QList<QTreeWidgetItem*> selection = property_grid_->selectedItems();
00194   QList<QTreeWidgetItem*>::iterator si;
00195   for( si = selection.begin(); si != selection.end(); si++ )
00196   {
00197     DisplayWrapper* selected = displayWrapperFromItem( *si );
00198     if( selected )
00199     {
00200       displays.insert( selected );
00201     }
00202   }
00203   return displays;
00204 }
00205 
00206 void DisplaysPanel::onSelectionChanged()
00207 {
00208   std::set<DisplayWrapper*> displays = getSelectedDisplays();
00209 
00210   int num_displays_selected = displays.size();
00211 
00212   remove_button_->setEnabled( num_displays_selected > 0 );
00213   rename_button_->setEnabled( num_displays_selected == 1 );
00214 }
00215 
00216 void DisplaysPanel::onRenameDisplay()
00217 {
00218   std::set<DisplayWrapper*> displays = getSelectedDisplays();
00219   if( displays.size() == 0 )
00220   {
00221     return;
00222   }
00223   DisplayWrapper* display_to_rename = *(displays.begin());
00224 
00225   if( !display_to_rename )
00226   {
00227     return;
00228   }
00229 
00230   bool ok = true;
00231   QString new_name;
00232   std::string new_name_std;
00233   QString old_name = QString::fromStdString( display_to_rename->getName() );
00234   do
00235   {
00236     QString prompt;
00237 
00238     if (!ok)
00239     {
00240       prompt = "That name is already taken.  Please try another.";
00241     }
00242     else
00243     {
00244       prompt = "New Name?";
00245     }
00246     new_name = QInputDialog::getText( this, "Rename Display", prompt, QLineEdit::Normal, old_name );
00247 
00248     ok = true;
00249     if( new_name.isEmpty() || new_name == old_name )
00250     {
00251       return;
00252     }
00253 
00254     new_name_std = new_name.toStdString();
00255 
00256     // Make sure the new name is not already taken
00257     M_DisplayToIndex::iterator it = display_map_.begin();
00258     M_DisplayToIndex::iterator end = display_map_.end();
00259     for (; it != end; ++it)
00260     {
00261       DisplayWrapper* wrapper = (*it).first;
00262       if( wrapper->getName() == new_name_std )
00263       {
00264         ok = false;
00265         break;
00266       }
00267     }
00268   } while (!ok);
00269 
00270   display_to_rename->setName( new_name_std );
00271   renumberDisplays();
00272 }
00273 
00274 void DisplaysPanel::setDisplayCategoryLabel(const DisplayWrapper* wrapper, int index)
00275 {
00276   char buf[1024];
00277   snprintf( buf, 1024, "%02d. %s (%s)", index + 1, wrapper->getName().c_str(), wrapper->getClassDisplayName().c_str());
00278   wrapper->getCategory().lock()->setLabel(buf);
00279 }
00280 
00281 void DisplaysPanel::setDisplayCategoryColor(const DisplayWrapper* wrapper)
00282 {
00283   CategoryPropertyPtr cat = wrapper->getCategory().lock();
00284 
00285   if (!wrapper->isLoaded())
00286   {
00287     cat->setToError();
00288   }
00289   else if ( wrapper->getDisplay()->isEnabled() )
00290   {
00291     switch (wrapper->getDisplay()->getStatus())
00292     {
00293     case status_levels::Ok:
00294       cat->setToOK();
00295       break;
00296     case status_levels::Warn:
00297       cat->setToWarn();
00298       break;
00299     case status_levels::Error:
00300       cat->setToError();
00301       break;
00302     }
00303   }
00304   else
00305   {
00306     cat->setToDisabled();
00307   }
00308 
00309   PropertyWidgetItem* item = cat->getWidgetItem();
00310   if( item )
00311   {
00312     bool ign = property_grid_->setIgnoreChanges( true );
00313     item->setFlags( item->flags() | Qt::ItemIsDragEnabled );
00314     property_grid_->setIgnoreChanges( ign );
00315   }
00316 }
00317 
00318 void DisplaysPanel::onStateChangedTimer()
00319 {
00320   S_Display local_displays;
00321   {
00322     boost::mutex::scoped_lock lock(state_changed_displays_mutex_);
00323     local_displays.swap(state_changed_displays_);
00324   }
00325 
00326   S_Display::iterator it = local_displays.begin();
00327   S_Display::iterator end = local_displays.end();
00328   for (; it != end; ++it)
00329   {
00330     Display* display = *it;
00331     DisplayWrapper* wrapper = manager_->getDisplayWrapper(display);
00332     if (!wrapper)
00333     {
00334       continue;
00335     }
00336 
00337     M_DisplayToIndex::iterator it = display_map_.find(wrapper);
00338     if (it == display_map_.end())
00339     {
00340       continue;
00341     }
00342 
00343     int index = it->second;
00344     setDisplayCategoryColor(wrapper);
00345     setDisplayCategoryLabel(wrapper, index);
00346   }
00347 }
00348 
00349 void DisplaysPanel::onDisplayStateChanged( Display* display )
00350 {
00351   // This can be called from different threads, so we have to push this to the GUI update thread
00352   boost::mutex::scoped_lock lock(state_changed_displays_mutex_);
00353   state_changed_displays_.insert(display);
00354 }
00355 
00356 void DisplaysPanel::onDisplayCreated( DisplayWrapper* wrapper )
00357 {
00358   connect( wrapper->getDisplay(), SIGNAL( stateChanged( Display* )), this, SLOT( onDisplayStateChanged( Display* )));
00359 
00360   setDisplayCategoryColor(wrapper);
00361 
00362   update();
00363 }
00364 
00365 void DisplaysPanel::onDisplayDestroyed( DisplayWrapper* wrapper )
00366 {
00367   M_DisplayToIndex::iterator it = display_map_.find(wrapper);
00368   if (it == display_map_.end())
00369   {
00370     return;
00371   }
00372 
00373   setDisplayCategoryColor(wrapper);
00374 
00375 
00376   int index = it->second;
00377   setDisplayCategoryLabel(wrapper, index);
00378 
00379   update();
00380 }
00381 
00382 void DisplaysPanel::onDisplayAdding( DisplayWrapper* wrapper )
00383 {
00384   connect( wrapper, SIGNAL( displayCreated( DisplayWrapper* )), this, SLOT( onDisplayCreated( DisplayWrapper* )));
00385   connect( wrapper, SIGNAL( displayDestroyed( DisplayWrapper* )), this, SLOT( onDisplayDestroyed( DisplayWrapper* )));
00386 }
00387 
00388 void DisplaysPanel::onDisplayAdded( DisplayWrapper* wrapper )
00389 {
00390   int index = display_map_.size();
00391   bool inserted = display_map_.insert(std::make_pair(wrapper, index)).second;
00392   ROS_ASSERT(inserted);
00393   setDisplayCategoryLabel(wrapper, index);
00394   setDisplayCategoryColor(wrapper);
00395 }
00396 
00397 void DisplaysPanel::onDisplayRemoved( DisplayWrapper* wrapper )
00398 {
00399   M_DisplayToIndex::iterator it = display_map_.find(wrapper);
00400   ROS_ASSERT(it != display_map_.end());
00401 
00402   uint32_t index = it->second;
00403 
00404   display_map_.erase(it);
00405 
00406   it = display_map_.begin();
00407   M_DisplayToIndex::iterator end = display_map_.end();
00408   for (;it != end; ++it)
00409   {
00410     if (it->second > index)
00411     {
00412       --it->second;
00413       setDisplayCategoryLabel(it->first, it->second);
00414     }
00415   }
00416 
00417   sortDisplays();
00418 }
00419 
00420 void DisplaysPanel::readFromConfig(const boost::shared_ptr<Config>& config)
00421 {
00422   std::string grid_state;
00423   if ( config->get( PROPERTY_GRID_CONFIG, &grid_state ) )
00424   {
00425     property_grid_->restoreEditableState( grid_state );
00426   }
00427 
00428   std::string sizes_string;
00429   if ( config->get( PROPERTY_GRID_SPLITTER, &sizes_string ) )
00430   {
00431     QList<int> sizes;
00432 
00433     std::istringstream iss( sizes_string );
00434     int size;
00435     iss >> size;
00436     sizes.push_back( size );
00437     char c;
00438     iss >> c; // skip the ','
00439     iss >> size;
00440     sizes.push_back( size );
00441     tree_with_help_->setSizes( sizes );
00442   }
00443 }
00444 
00445 void DisplaysPanel::writeToConfig(const boost::shared_ptr<Config>& config)
00446 {
00447   config->set( PROPERTY_GRID_CONFIG, property_grid_->saveEditableState() );
00448   QList<int> sizes = tree_with_help_->sizes();
00449   std::ostringstream sizes_stream;
00450   sizes_stream << sizes.at( 0 ) << ',' << sizes.at( 1 );
00451   config->set( PROPERTY_GRID_SPLITTER, sizes_stream.str() );
00452 }
00453 
00454 void DisplaysPanel::renumberDisplays()
00455 {
00456   int display_number = 0;
00457   display_map_.clear();
00458   for( int i = 0; i < property_grid_->topLevelItemCount(); i++ )
00459   {
00460     DisplayWrapper* wrapper = displayWrapperFromItem( property_grid_->topLevelItem( i ));
00461     if( wrapper )
00462     {
00463       setDisplayCategoryLabel( wrapper, display_number );
00464       display_map_[ wrapper ] = display_number;
00465       display_number++;
00466     }
00467   }
00468   sortDisplays();
00469 }
00470 
00471 } // namespace rviz


rviz_qt
Author(s): Dave Hershberger
autogenerated on Fri Dec 6 2013 20:56:52