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_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
00163
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();
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
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
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;
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
00478 V_DisplayWrapper& wrapper_list = manager_->getDisplays();
00479 wrapper_list.swap( new_wrapper_list );
00480 manager_->notifyConfigChanged();
00481 }
00482
00483 }