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_object_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 #include "rviz/uniform_string_stream.h"
00052 
00053 static const std::string PROPERTY_GRID_CONFIG("Property Grid State");
00054 static const std::string PROPERTY_GRID_SPLITTER("Property Grid Splitter");
00055 
00056 namespace rviz
00057 {
00058 
00059 DisplaysPanel::DisplaysPanel( QWidget* parent )
00060   : QWidget( parent )
00061   , manager_( NULL )
00062 {
00063   tree_with_help_ = new PropertyTreeWithHelp;
00064   property_grid_ = tree_with_help_->getTree();
00065   property_grid_->setDragEnabled( true );
00066   property_grid_->setAcceptDrops( true );
00067   property_grid_->setAnimated( true );
00068 
00069   QPushButton* add_button = new QPushButton( "Add" );
00070   add_button->setShortcut( QKeySequence( QString( "Ctrl+N" )));
00071   add_button->setToolTip( "Add a new display, Ctrl+N" );
00072   remove_button_ = new QPushButton( "Remove" );
00073   remove_button_->setShortcut( QKeySequence( QString( "Ctrl+X" )));
00074   remove_button_->setToolTip( "Remove displays, Ctrl+X" );
00075   remove_button_->setEnabled( false );
00076   rename_button_ = new QPushButton( "Rename" );
00077   rename_button_->setShortcut( QKeySequence( QString( "Ctrl+R" )));
00078   rename_button_->setToolTip( "Rename a display, Ctrl+R" );
00079   rename_button_->setEnabled( false );
00080 
00081   QHBoxLayout* button_layout = new QHBoxLayout;
00082   button_layout->addWidget( add_button );
00083   button_layout->addWidget( remove_button_ );
00084   button_layout->addWidget( rename_button_ );
00085 
00086   QVBoxLayout* layout = new QVBoxLayout;
00087   layout->addWidget( tree_with_help_ );
00088   layout->addLayout( button_layout );
00089 
00090   setLayout( layout );
00091 
00092   connect( add_button, SIGNAL( clicked( bool )), this, SLOT( onNewDisplay() ));
00093   connect( remove_button_, SIGNAL( clicked( bool )), this, SLOT( onDeleteDisplay() ));
00094   connect( rename_button_, SIGNAL( clicked( bool )), this, SLOT( onRenameDisplay() ));
00095   connect( property_grid_, SIGNAL( itemSelectionChanged() ), this, SLOT( onSelectionChanged() ));
00096   connect( property_grid_, SIGNAL( orderChanged() ), this, SLOT( renumberDisplays() ));
00097 
00098   QTimer* timer = new QTimer( this );
00099   connect( timer, SIGNAL( timeout() ), this, SLOT( onStateChangedTimer() ));
00100   timer->start( 200 );
00101 }
00102 
00103 DisplaysPanel::~DisplaysPanel()
00104 {
00105 }
00106 
00107 void DisplaysPanel::initialize( VisualizationManager* manager )
00108 {
00109   manager_ = manager;
00110   connect( manager_, SIGNAL( displayAdding( DisplayWrapper* )), this, SLOT( onDisplayAdding( DisplayWrapper* )));
00111   connect( manager_, SIGNAL( displayAdded( DisplayWrapper* )), this, SLOT( onDisplayAdded( DisplayWrapper* )));
00112   connect( manager_, SIGNAL( displayRemoved( DisplayWrapper* )), this, SLOT( onDisplayRemoved( DisplayWrapper* )));
00113   connect( manager_, SIGNAL( displaysConfigLoaded( const boost::shared_ptr<Config>& )),
00114            this, SLOT( readFromConfig( const boost::shared_ptr<Config>& )));
00115   connect( manager_, SIGNAL( displaysConfigSaved( const boost::shared_ptr<Config>& )),
00116            this, SLOT( writeToConfig( const boost::shared_ptr<Config>& )));
00117 
00118   manager_->getPropertyManager()->setPropertyTreeWidget( property_grid_ );
00119 
00120   sortDisplays();
00121 }
00122 
00123 void DisplaysPanel::sortDisplays()
00124 {
00125   property_grid_->sortItems( 0, Qt::AscendingOrder );
00126 }
00127 
00128 DisplayWrapper* DisplaysPanel::displayWrapperFromItem( QTreeWidgetItem* selected_item )
00129 {
00130   DisplayWrapper* selected_display = 0;
00131 
00132   PropertyWidgetItem* pwi = dynamic_cast<PropertyWidgetItem*>( selected_item );
00133   if( pwi )
00134   {
00135     PropertyBase* property = pwi->getProperty();
00136 
00137     void* user_data = property->getUserData();
00138     if( user_data )
00139     {
00140       DisplayWrapper* wrapper = reinterpret_cast<DisplayWrapper*>( user_data );
00141 
00142       if( manager_->isValidDisplay( wrapper ))
00143       {
00144         selected_display = manager_->getDisplayWrapper( wrapper->getName() );
00145       }
00146       else
00147       {
00148         DisplayWrapper* wrapper = manager_->getDisplayWrapper( reinterpret_cast<Display*>( user_data ));
00149 
00150         if( wrapper )
00151         {
00152           selected_display = wrapper;
00153         }
00154       }
00155     }
00156   }
00157   return selected_display;
00158 }
00159 
00160 void DisplaysPanel::onNewDisplay()
00161 {
00162   // Get the list of current display names, so we can enforce that the
00163   // new display has a unique name.
00164   S_string current_display_names;
00165   manager_->getDisplayNames(current_display_names);
00166 
00167   std::string lookup_name;
00168   std::string display_name;
00169 
00170   NewObjectDialog* dialog = new NewObjectDialog( manager_->getDisplayClassLoader(),
00171                                                  "Display",
00172                                                  current_display_names,
00173                                                  &lookup_name,
00174                                                  &display_name );
00175   if( dialog->exec() == QDialog::Accepted )
00176   {
00177     manager_->createDisplay( lookup_name, display_name, true );
00178   }
00179   activateWindow(); // Force keyboard focus back on main window.
00180 }
00181 
00182 void DisplaysPanel::onDeleteDisplay()
00183 {
00184   std::set<DisplayWrapper*> displays_to_delete = getSelectedDisplays();
00185 
00186   std::set<DisplayWrapper*>::iterator di;
00187   for( di = displays_to_delete.begin(); di != displays_to_delete.end(); di++ )
00188   {
00189     manager_->removeDisplay( *di );
00190   }
00191 }
00192 
00193 std::set<DisplayWrapper*> DisplaysPanel::getSelectedDisplays()
00194 {
00195   std::set<DisplayWrapper*> displays;
00196 
00197   QList<QTreeWidgetItem*> selection = property_grid_->selectedItems();
00198   QList<QTreeWidgetItem*>::iterator si;
00199   for( si = selection.begin(); si != selection.end(); si++ )
00200   {
00201     DisplayWrapper* selected = displayWrapperFromItem( *si );
00202     if( selected )
00203     {
00204       displays.insert( selected );
00205     }
00206   }
00207   return displays;
00208 }
00209 
00210 void DisplaysPanel::onSelectionChanged()
00211 {
00212   std::set<DisplayWrapper*> displays = getSelectedDisplays();
00213 
00214   int num_displays_selected = displays.size();
00215 
00216   remove_button_->setEnabled( num_displays_selected > 0 );
00217   rename_button_->setEnabled( num_displays_selected == 1 );
00218 }
00219 
00220 void DisplaysPanel::onRenameDisplay()
00221 {
00222   std::set<DisplayWrapper*> displays = getSelectedDisplays();
00223   if( displays.size() == 0 )
00224   {
00225     return;
00226   }
00227   DisplayWrapper* display_to_rename = *(displays.begin());
00228 
00229   if( !display_to_rename )
00230   {
00231     return;
00232   }
00233 
00234   bool ok = true;
00235   QString new_name;
00236   std::string new_name_std;
00237   QString old_name = QString::fromStdString( display_to_rename->getName() );
00238   do
00239   {
00240     QString prompt;
00241 
00242     if (!ok)
00243     {
00244       prompt = "That name is already taken.  Please try another.";
00245     }
00246     else
00247     {
00248       prompt = "New Name?";
00249     }
00250     new_name = QInputDialog::getText( this, "Rename Display", prompt, QLineEdit::Normal, old_name );
00251 
00252     ok = true;
00253     if( new_name.isEmpty() || new_name == old_name )
00254     {
00255       return;
00256     }
00257 
00258     new_name_std = new_name.toStdString();
00259 
00260     // Make sure the new name is not already taken
00261     M_DisplayToIndex::iterator it = display_map_.begin();
00262     M_DisplayToIndex::iterator end = display_map_.end();
00263     for (; it != end; ++it)
00264     {
00265       DisplayWrapper* wrapper = (*it).first;
00266       if( wrapper->getName() == new_name_std )
00267       {
00268         ok = false;
00269         break;
00270       }
00271     }
00272   } while (!ok);
00273 
00274   display_to_rename->setName( new_name_std );
00275   renumberDisplays();
00276 }
00277 
00278 void DisplaysPanel::setDisplayCategoryLabel(const DisplayWrapper* wrapper, int index)
00279 {
00280   char buf[1024];
00281   snprintf( buf, 1024, "%02d. %s (%s)", index + 1, wrapper->getName().c_str(), wrapper->getClassDisplayName().c_str());
00282   wrapper->getCategory().lock()->setLabel(buf);
00283 }
00284 
00285 void DisplaysPanel::setDisplayCategoryColor(const DisplayWrapper* wrapper)
00286 {
00287   CategoryPropertyPtr cat = wrapper->getCategory().lock();
00288 
00289   if (!wrapper->isLoaded())
00290   {
00291     cat->setToError();
00292   }
00293   else if ( wrapper->getDisplay()->isEnabled() )
00294   {
00295     switch (wrapper->getDisplay()->getStatus())
00296     {
00297     case status_levels::Ok:
00298       cat->setToOK();
00299       break;
00300     case status_levels::Warn:
00301       cat->setToWarn();
00302       break;
00303     case status_levels::Error:
00304       cat->setToError();
00305       break;
00306     }
00307   }
00308   else
00309   {
00310     cat->setToDisabled();
00311   }
00312 
00313   PropertyWidgetItem* item = cat->getWidgetItem();
00314   if( item )
00315   {
00316     bool ign = property_grid_->setIgnoreChanges( true );
00317     item->setFlags( item->flags() | Qt::ItemIsDragEnabled );
00318     property_grid_->setIgnoreChanges( ign );
00319   }
00320 }
00321 
00322 void DisplaysPanel::onStateChangedTimer()
00323 {
00324   S_Display local_displays;
00325   {
00326     boost::mutex::scoped_lock lock(state_changed_displays_mutex_);
00327     local_displays.swap(state_changed_displays_);
00328   }
00329 
00330   S_Display::iterator it = local_displays.begin();
00331   S_Display::iterator end = local_displays.end();
00332   for (; it != end; ++it)
00333   {
00334     Display* display = *it;
00335     DisplayWrapper* wrapper = manager_->getDisplayWrapper(display);
00336     if (!wrapper)
00337     {
00338       continue;
00339     }
00340 
00341     M_DisplayToIndex::iterator it = display_map_.find(wrapper);
00342     if (it == display_map_.end())
00343     {
00344       continue;
00345     }
00346 
00347     int index = it->second;
00348     setDisplayCategoryColor(wrapper);
00349     setDisplayCategoryLabel(wrapper, index);
00350   }
00351 }
00352 
00353 void DisplaysPanel::onDisplayStateChanged( Display* display )
00354 {
00355   // This can be called from different threads, so we have to push this to the GUI update thread
00356   boost::mutex::scoped_lock lock(state_changed_displays_mutex_);
00357   state_changed_displays_.insert(display);
00358 }
00359 
00360 void DisplaysPanel::onDisplayCreated( DisplayWrapper* wrapper )
00361 {
00362   connect( wrapper->getDisplay(), SIGNAL( stateChanged( Display* )), this, SLOT( onDisplayStateChanged( Display* )));
00363 
00364   setDisplayCategoryColor(wrapper);
00365 
00366   update();
00367 }
00368 
00369 void DisplaysPanel::onDisplayDestroyed( DisplayWrapper* wrapper )
00370 {
00371   M_DisplayToIndex::iterator it = display_map_.find(wrapper);
00372   if (it == display_map_.end())
00373   {
00374     return;
00375   }
00376 
00377   setDisplayCategoryColor(wrapper);
00378 
00379 
00380   int index = it->second;
00381   setDisplayCategoryLabel(wrapper, index);
00382 
00383   update();
00384 }
00385 
00386 void DisplaysPanel::onDisplayAdding( DisplayWrapper* wrapper )
00387 {
00388   connect( wrapper, SIGNAL( displayCreated( DisplayWrapper* )), this, SLOT( onDisplayCreated( DisplayWrapper* )));
00389   connect( wrapper, SIGNAL( displayDestroyed( DisplayWrapper* )), this, SLOT( onDisplayDestroyed( DisplayWrapper* )));
00390 }
00391 
00392 void DisplaysPanel::onDisplayAdded( DisplayWrapper* wrapper )
00393 {
00394   int index = display_map_.size();
00395   bool inserted = display_map_.insert(std::make_pair(wrapper, index)).second;
00396   ROS_ASSERT(inserted);
00397   setDisplayCategoryLabel(wrapper, index);
00398   setDisplayCategoryColor(wrapper);
00399 }
00400 
00401 void DisplaysPanel::onDisplayRemoved( DisplayWrapper* wrapper )
00402 {
00403   M_DisplayToIndex::iterator it = display_map_.find(wrapper);
00404   ROS_ASSERT(it != display_map_.end());
00405 
00406   uint32_t index = it->second;
00407 
00408   display_map_.erase(it);
00409 
00410   it = display_map_.begin();
00411   M_DisplayToIndex::iterator end = display_map_.end();
00412   for (;it != end; ++it)
00413   {
00414     if (it->second > index)
00415     {
00416       --it->second;
00417       setDisplayCategoryLabel(it->first, it->second);
00418     }
00419   }
00420 
00421   sortDisplays();
00422 }
00423 
00424 void DisplaysPanel::readFromConfig(const boost::shared_ptr<Config>& config)
00425 {
00426   std::string grid_state;
00427   if ( config->get( PROPERTY_GRID_CONFIG, &grid_state ) )
00428   {
00429     property_grid_->restoreEditableState( grid_state );
00430   }
00431 
00432   std::string sizes_string;
00433   if ( config->get( PROPERTY_GRID_SPLITTER, &sizes_string ) )
00434   {
00435     QList<int> sizes;
00436 
00437     UniformStringStream iss( sizes_string );
00438     int size;
00439     iss >> size;
00440     sizes.push_back( size );
00441     char c;
00442     iss >> c; // skip the ','
00443     iss >> size;
00444     sizes.push_back( size );
00445     tree_with_help_->setSizes( sizes );
00446   }
00447 }
00448 
00449 void DisplaysPanel::writeToConfig(const boost::shared_ptr<Config>& config)
00450 {
00451   config->set( PROPERTY_GRID_CONFIG, property_grid_->saveEditableState() );
00452   QList<int> sizes = tree_with_help_->sizes();
00453   UniformStringStream sizes_stream;
00454   sizes_stream << sizes.at( 0 ) << ',' << sizes.at( 1 );
00455   config->set( PROPERTY_GRID_SPLITTER, sizes_stream.str() );
00456 }
00457 
00458 void DisplaysPanel::renumberDisplays()
00459 {
00460   V_DisplayWrapper new_wrapper_list;
00461 
00462   int display_number = 0;
00463   display_map_.clear();
00464   for( int i = 0; i < property_grid_->topLevelItemCount(); i++ )
00465   {
00466     DisplayWrapper* wrapper = displayWrapperFromItem( property_grid_->topLevelItem( i ));
00467     if( wrapper )
00468     {
00469       setDisplayCategoryLabel( wrapper, display_number );
00470       display_map_[ wrapper ] = display_number;
00471       display_number++;
00472 
00473       new_wrapper_list.push_back( wrapper );
00474     }
00475   }
00476 
00477   // Swap our new vector of DisplayWrappers in for the original, so the order of Displays gets saved.
00478   V_DisplayWrapper& wrapper_list = manager_->getDisplays();
00479   wrapper_list.swap( new_wrapper_list );
00480   manager_->notifyConfigChanged();
00481 }
00482 
00483 } // namespace rviz


rviz
Author(s): Dave Hershberger, Josh Faust
autogenerated on Mon Jan 6 2014 11:54:32