00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
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
00161
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
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
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;
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 }