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 <fstream>
00031
00032 #include <QAction>
00033 #include <QApplication>
00034 #include <QCloseEvent>
00035 #include <QDesktopServices>
00036 #include <QDockWidget>
00037 #include <QDir>
00038 #include <QFileDialog>
00039 #include <QMenu>
00040 #include <QMenuBar>
00041 #include <QMessageBox>
00042 #include <QTimer>
00043 #include <QToolBar>
00044 #include <QToolButton>
00045 #include <QUrl>
00046 #include <QStatusBar>
00047 #include <QLabel>
00048 #include <QToolButton>
00049 #include <QHBoxLayout>
00050
00051 #include <boost/algorithm/string/split.hpp>
00052 #include <boost/algorithm/string/trim.hpp>
00053 #include <boost/bind.hpp>
00054 #include <boost/filesystem.hpp>
00055
00056 #include <ros/console.h>
00057 #include <ros/package.h>
00058 #include <ros/init.h>
00059
00060 #include <OGRE/OgreRenderWindow.h>
00061 #include <OGRE/OgreMeshManager.h>
00062
00063 #include <ogre_helpers/initialization.h>
00064
00065 #include "rviz/displays_panel.h"
00066 #include "rviz/failed_panel.h"
00067 #include "rviz/help_panel.h"
00068 #include "rviz/loading_dialog.h"
00069 #include "rviz/new_object_dialog.h"
00070 #include "rviz/panel_dock_widget.h"
00071 #include "rviz/panel_factory.h"
00072 #include "rviz/render_panel.h"
00073 #include "rviz/screenshot_dialog.h"
00074 #include "rviz/selection/selection_manager.h"
00075 #include "rviz/selection_panel.h"
00076 #include "rviz/splash_screen.h"
00077 #include "rviz/time_panel.h"
00078 #include "rviz/tool.h"
00079 #include "rviz/tool_manager.h"
00080 #include "rviz/tool_properties_panel.h"
00081 #include "rviz/views_panel.h"
00082 #include "rviz/visualization_manager.h"
00083 #include "rviz/widget_geometry_change_detector.h"
00084 #include "rviz/load_resource.h"
00085 #include "rviz/yaml_config_reader.h"
00086 #include "rviz/yaml_config_writer.h"
00087
00088 #include "rviz/visualization_frame.h"
00089
00090 namespace fs = boost::filesystem;
00091
00092 #define CONFIG_EXTENSION "rviz"
00093 #define CONFIG_EXTENSION_WILDCARD "*."CONFIG_EXTENSION
00094 #define RECENT_CONFIG_COUNT 10
00095
00096 #if BOOST_FILESYSTEM_VERSION == 3
00097 #define BOOST_FILENAME_STRING filename().string
00098 #define BOOST_FILE_STRING string
00099 #else
00100 #define BOOST_FILENAME_STRING filename
00101 #define BOOST_FILE_STRING file_string
00102 #endif
00103
00104 namespace rviz
00105 {
00106
00107 VisualizationFrame::VisualizationFrame( QWidget* parent )
00108 : QMainWindow( parent )
00109 , render_panel_(NULL)
00110 , show_help_action_(NULL)
00111 , file_menu_(NULL)
00112 , recent_configs_menu_(NULL)
00113 , toolbar_(NULL)
00114 , manager_(NULL)
00115 , splash_( NULL )
00116 , toolbar_actions_( NULL )
00117 , show_choose_new_master_option_( false )
00118 , add_tool_action_( NULL )
00119 , remove_tool_menu_( NULL )
00120 , initialized_( false )
00121 , geom_change_detector_( new WidgetGeometryChangeDetector( this ))
00122 , loading_( false )
00123 , post_load_timer_( new QTimer( this ))
00124 {
00125 panel_factory_ = new PanelFactory();
00126
00127 installEventFilter( geom_change_detector_ );
00128 connect( geom_change_detector_, SIGNAL( changed() ), this, SLOT( setDisplayConfigModified() ));
00129
00130 post_load_timer_->setSingleShot( true );
00131 connect( post_load_timer_, SIGNAL( timeout() ), this, SLOT( markLoadingDone() ));
00132
00133 package_path_ = ros::package::getPath("rviz");
00134 help_path_ = QString::fromStdString( (fs::path(package_path_) / "help/help.html").BOOST_FILE_STRING() );
00135 splash_path_ = QString::fromStdString( (fs::path(package_path_) / "images/splash.png").BOOST_FILE_STRING() );
00136
00137 QToolButton* reset_button = new QToolButton( );
00138 reset_button->setText( "Reset" );
00139 reset_button->setContentsMargins(0,0,0,0);
00140 statusBar()->addPermanentWidget( reset_button, 0 );
00141 connect( reset_button, SIGNAL( clicked( bool )), this, SLOT( reset() ));
00142
00143 status_label_ = new QLabel("");
00144 statusBar()->addPermanentWidget( status_label_, 1 );
00145 connect( this, SIGNAL( statusUpdate( const QString& )), status_label_, SLOT( setText( const QString& )));
00146
00147 setWindowTitle( "RViz[*]" );
00148 }
00149
00150 VisualizationFrame::~VisualizationFrame()
00151 {
00152 delete manager_;
00153
00154 for( int i = 0; i < custom_panels_.size(); i++ )
00155 {
00156 delete custom_panels_[ i ].dock;
00157 }
00158
00159 delete panel_factory_;
00160 }
00161
00162 void VisualizationFrame::setStatus( const QString & message )
00163 {
00164 Q_EMIT statusUpdate( message );
00165 }
00166
00167 void VisualizationFrame::closeEvent( QCloseEvent* event )
00168 {
00169 if( prepareToExit() )
00170 {
00171 event->accept();
00172 }
00173 else
00174 {
00175 event->ignore();
00176 }
00177 }
00178
00179 void VisualizationFrame::leaveEvent ( QEvent * event )
00180 {
00181 setStatus("");
00182 }
00183
00184 void VisualizationFrame::reset()
00185 {
00186 Ogre::MeshManager::getSingleton().removeAll();
00187 manager_->resetTime();
00188 }
00189
00190 void VisualizationFrame::changeMaster()
00191 {
00192 if( prepareToExit() )
00193 {
00194 QApplication::exit( 255 );
00195 }
00196 }
00197
00198 void VisualizationFrame::setShowChooseNewMaster( bool show )
00199 {
00200 show_choose_new_master_option_ = show;
00201 }
00202
00203 void VisualizationFrame::setHelpPath( const QString& help_path )
00204 {
00205 help_path_ = help_path;
00206 manager_->setHelpPath( help_path_ );
00207 }
00208
00209 void VisualizationFrame::setSplashPath( const QString& splash_path )
00210 {
00211 splash_path_ = splash_path;
00212 }
00213
00214 void VisualizationFrame::initialize(const QString& display_config_file )
00215 {
00216 initConfigs();
00217
00218 loadPersistentSettings();
00219
00220 QIcon app_icon( QString::fromStdString( (fs::path(package_path_) / "icons/package.png").BOOST_FILE_STRING() ) );
00221 setWindowIcon( app_icon );
00222
00223 if( splash_path_ != "" )
00224 {
00225 QPixmap splash_image( splash_path_ );
00226 splash_ = new SplashScreen( splash_image );
00227 splash_->show();
00228 connect( this, SIGNAL( statusUpdate( const QString& )), splash_, SLOT( showMessage( const QString& )));
00229 }
00230 Q_EMIT statusUpdate( "Initializing" );
00231
00232 if( !ros::isInitialized() )
00233 {
00234 int argc = 0;
00235 ros::init( argc, 0, "rviz", ros::init_options::AnonymousName );
00236 }
00237
00238 QWidget* central_widget = new QWidget(this);
00239 QHBoxLayout* central_layout = new QHBoxLayout;
00240 central_layout->setSpacing(0);
00241 central_layout->setMargin(0);
00242
00243 render_panel_ = new RenderPanel( central_widget );
00244
00245 hide_left_dock_button_ = new QToolButton();
00246 hide_left_dock_button_->setContentsMargins(0,0,0,0);
00247 hide_left_dock_button_->setArrowType( Qt::LeftArrow );
00248 hide_left_dock_button_->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ) );
00249 hide_left_dock_button_->setFixedWidth(16);
00250 hide_left_dock_button_->setAutoRaise(true);
00251 hide_left_dock_button_->setCheckable(true);
00252
00253 connect(hide_left_dock_button_, SIGNAL(toggled(bool)), this, SLOT(hideLeftDock(bool)));
00254
00255 hide_right_dock_button_ = new QToolButton();
00256 hide_right_dock_button_->setContentsMargins(0,0,0,0);
00257 hide_right_dock_button_->setArrowType( Qt::RightArrow );
00258 hide_right_dock_button_->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ) );
00259 hide_right_dock_button_->setFixedWidth(16);
00260 hide_right_dock_button_->setAutoRaise(true);
00261 hide_right_dock_button_->setCheckable(true);
00262
00263 connect(hide_right_dock_button_, SIGNAL(toggled(bool)), this, SLOT(hideRightDock(bool)));
00264
00265 central_layout->addWidget( hide_left_dock_button_, 0 );
00266 central_layout->addWidget( render_panel_, 1 );
00267 central_layout->addWidget( hide_right_dock_button_, 0 );
00268
00269 central_widget->setLayout( central_layout );
00270
00271 initMenus();
00272
00273 initToolbars();
00274
00275 setCentralWidget( central_widget );
00276
00277 manager_ = new VisualizationManager( render_panel_, this );
00278 manager_->setHelpPath( help_path_ );
00279
00280 render_panel_->initialize( manager_->getSceneManager(), manager_ );
00281
00282 ToolManager* tool_man = manager_->getToolManager();
00283
00284 connect( manager_, SIGNAL( configChanged() ), this, SLOT( setDisplayConfigModified() ));
00285 connect( tool_man, SIGNAL( toolAdded( Tool* )), this, SLOT( addTool( Tool* )));
00286 connect( tool_man, SIGNAL( toolRemoved( Tool* )), this, SLOT( removeTool( Tool* )));
00287 connect( tool_man, SIGNAL( toolChanged( Tool* )), this, SLOT( indicateToolIsCurrent( Tool* )));
00288
00289 manager_->initialize();
00290
00291 if( display_config_file != "" )
00292 {
00293 loadDisplayConfig( display_config_file );
00294 }
00295 else
00296 {
00297 loadDisplayConfig( QString::fromStdString( default_display_config_file_ ));
00298 }
00299
00300 delete splash_;
00301 splash_ = 0;
00302
00303 manager_->startUpdate();
00304 initialized_ = true;
00305 Q_EMIT statusUpdate( "RViz is ready." );
00306
00307 connect( manager_, SIGNAL( statusUpdate( const QString& )), this, SIGNAL( statusUpdate( const QString& )));
00308 }
00309
00310 void VisualizationFrame::initConfigs()
00311 {
00312 home_dir_ = QDir::toNativeSeparators( QDir::homePath() ).toStdString();
00313
00314 config_dir_ = (fs::path(home_dir_) / ".rviz").BOOST_FILE_STRING();
00315 persistent_settings_file_ = (fs::path(config_dir_) / "persistent_settings").BOOST_FILE_STRING();
00316 default_display_config_file_ = (fs::path(config_dir_) / "default."CONFIG_EXTENSION).BOOST_FILE_STRING();
00317
00318 if( fs::is_regular_file( config_dir_ ))
00319 {
00320 ROS_ERROR("Moving file [%s] out of the way to recreate it as a directory.", config_dir_.c_str());
00321 std::string backup_file = config_dir_ + ".bak";
00322
00323 fs::rename(config_dir_, backup_file);
00324 fs::create_directory(config_dir_);
00325 }
00326 else if (!fs::exists(config_dir_))
00327 {
00328 fs::create_directory(config_dir_);
00329 }
00330 }
00331
00332 void VisualizationFrame::loadPersistentSettings()
00333 {
00334 YamlConfigReader reader;
00335 Config config;
00336 reader.readFile( config, QString::fromStdString( persistent_settings_file_ ));
00337 if( !reader.error() )
00338 {
00339 QString last_config_dir, last_image_dir;
00340 if( config.mapGetString( "Last Config Dir", &last_config_dir ) &&
00341 config.mapGetString( "Last Image Dir", &last_image_dir ))
00342 {
00343 last_config_dir_ = last_config_dir.toStdString();
00344 last_image_dir_ = last_image_dir.toStdString();
00345 }
00346
00347 Config recent_configs_list = config.mapGetChild( "Recent Configs" );
00348 recent_configs_.clear();
00349 int num_recent = recent_configs_list.listLength();
00350 for( int i = 0; i < num_recent; i++ )
00351 {
00352 recent_configs_.push_back( recent_configs_list.listChildAt( i ).getValue().toString().toStdString() );
00353 }
00354 }
00355 else
00356 {
00357 ROS_ERROR( "%s", qPrintable( reader.errorMessage() ));
00358 }
00359 }
00360
00361 void VisualizationFrame::savePersistentSettings()
00362 {
00363 Config config;
00364 config.mapSetValue( "Last Config Dir", QString::fromStdString( last_config_dir_ ));
00365 config.mapSetValue( "Last Image Dir", QString::fromStdString( last_image_dir_ ));
00366 Config recent_configs_list = config.mapMakeChild( "Recent Configs" );
00367 for( D_string::iterator it = recent_configs_.begin(); it != recent_configs_.end(); ++it )
00368 {
00369 recent_configs_list.listAppendNew().setValue( QString::fromStdString( *it ));
00370 }
00371
00372 YamlConfigWriter writer;
00373 writer.writeFile( config, QString::fromStdString( persistent_settings_file_ ));
00374
00375 if( writer.error() )
00376 {
00377 ROS_ERROR( "%s", qPrintable( writer.errorMessage() ));
00378 }
00379 }
00380
00381 void VisualizationFrame::initMenus()
00382 {
00383 file_menu_ = menuBar()->addMenu( "&File" );
00384 file_menu_->addAction( "&Open Config", this, SLOT( onOpen() ), QKeySequence( "Ctrl+O" ));
00385 file_menu_->addAction( "&Save Config", this, SLOT( onSave() ), QKeySequence( "Ctrl+S" ));
00386 file_menu_->addAction( "Save Config &As", this, SLOT( onSaveAs() ));
00387 recent_configs_menu_ = file_menu_->addMenu( "&Recent Configs" );
00388 file_menu_->addAction( "Save &Image", this, SLOT( onSaveImage() ));
00389 if( show_choose_new_master_option_ )
00390 {
00391 file_menu_->addSeparator();
00392 file_menu_->addAction( "Change &Master", this, SLOT( changeMaster() ));
00393 }
00394 file_menu_->addSeparator();
00395 file_menu_->addAction( "&Quit", this, SLOT( close() ), QKeySequence( "Ctrl+Q" ));
00396
00397 view_menu_ = menuBar()->addMenu( "&Panels" );
00398 view_menu_->addAction( "Add &New Panel", this, SLOT( openNewPanelDialog() ));
00399 delete_view_menu_ = view_menu_->addMenu( "&Delete Panel" );
00400 delete_view_menu_->setEnabled( false );
00401 view_menu_->addSeparator();
00402
00403 QMenu* help_menu = menuBar()->addMenu( "&Help" );
00404 help_menu->addAction( "Show &Help panel", this, SLOT( showHelpPanel() ));
00405 help_menu->addAction( "Open rviz wiki in browser", this, SLOT( onHelpWiki() ));
00406 }
00407
00408 void VisualizationFrame::initToolbars()
00409 {
00410 QFont font;
00411 font.setPointSize( font.pointSizeF()*0.9 );
00412
00413
00414
00415 toolbar_ = addToolBar( "Tools" );
00416 toolbar_->setFont( font );
00417 toolbar_->setContentsMargins(0,0,0,0);
00418 toolbar_->setObjectName( "Tools" );
00419 toolbar_->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
00420 toolbar_actions_ = new QActionGroup( this );
00421 connect( toolbar_actions_, SIGNAL( triggered( QAction* )), this, SLOT( onToolbarActionTriggered( QAction* )));
00422 view_menu_->addAction( toolbar_->toggleViewAction() );
00423
00424 add_tool_action_ = new QAction( "", toolbar_actions_ );
00425 add_tool_action_->setToolTip( "Add a new tool" );
00426 add_tool_action_->setIcon( loadPixmap( "package://rviz/icons/plus.png" ) );
00427 toolbar_->addAction( add_tool_action_ );
00428 connect( add_tool_action_, SIGNAL( triggered() ), this, SLOT( openNewToolDialog() ));
00429
00430 remove_tool_menu_ = new QMenu();
00431 QToolButton* remove_tool_button = new QToolButton();
00432 remove_tool_button->setMenu( remove_tool_menu_ );
00433 remove_tool_button->setPopupMode( QToolButton::InstantPopup );
00434 remove_tool_button->setToolTip( "Remove a tool from the toolbar" );
00435 remove_tool_button->setIcon( loadPixmap( "package://rviz/icons/minus.png" ) );
00436 toolbar_->addWidget( remove_tool_button );
00437 connect( remove_tool_menu_, SIGNAL( triggered( QAction* )), this, SLOT( onToolbarRemoveTool( QAction* )));
00438
00439 }
00440
00441 void VisualizationFrame::hideDockImpl( Qt::DockWidgetArea area, bool hide )
00442 {
00443 QList<PanelDockWidget *> dock_widgets = findChildren<PanelDockWidget *>();
00444
00445 for ( QList<PanelDockWidget *>::iterator it=dock_widgets.begin(); it!=dock_widgets.end(); it++ )
00446 {
00447 Qt::DockWidgetArea curr_area = dockWidgetArea ( *it );
00448 if ( area == curr_area )
00449 {
00450 (*it)->setCollapsed(hide);
00451 }
00452
00453 if ( hide )
00454 {
00455 (*it)->setAllowedAreas( (*it)->allowedAreas() & ~area );
00456 }
00457 else
00458 {
00459 (*it)->setAllowedAreas( (*it)->allowedAreas() | area );
00460 }
00461 }
00462 }
00463
00464 void VisualizationFrame::setHideButtonVisibility( bool visible )
00465 {
00466 hide_left_dock_button_->setVisible( visible );
00467 hide_right_dock_button_->setVisible( visible );
00468 }
00469
00470 void VisualizationFrame::hideLeftDock( bool hide )
00471 {
00472 hideDockImpl( Qt::LeftDockWidgetArea, hide );
00473 hide_left_dock_button_->setArrowType( hide ? Qt::RightArrow : Qt::LeftArrow );
00474 }
00475
00476 void VisualizationFrame::hideRightDock( bool hide )
00477 {
00478 hideDockImpl( Qt::RightDockWidgetArea, hide );
00479 hide_right_dock_button_->setArrowType( hide ? Qt::LeftArrow : Qt::RightArrow );
00480 }
00481
00482 void VisualizationFrame::onDockPanelVisibilityChange( bool visible )
00483 {
00484
00485
00486 if ( visible )
00487 {
00488 QDockWidget* dock_widget = dynamic_cast<QDockWidget*>( sender() );
00489 if ( dock_widget )
00490 {
00491 Qt::DockWidgetArea area = dockWidgetArea( dock_widget );
00492 if ( area == Qt::LeftDockWidgetArea )
00493 {
00494 hide_left_dock_button_->setChecked( false );
00495 }
00496 if ( area == Qt::RightDockWidgetArea )
00497 {
00498 hide_right_dock_button_->setChecked( false );
00499 }
00500 }
00501 }
00502
00503 }
00504
00505 void VisualizationFrame::openNewPanelDialog()
00506 {
00507 QString class_id;
00508 QString display_name;
00509 QStringList empty;
00510
00511 NewObjectDialog* dialog = new NewObjectDialog( panel_factory_,
00512 "Panel",
00513 empty,
00514 empty,
00515 &class_id,
00516 &display_name,
00517 this );
00518 manager_->stopUpdate();
00519 if( dialog->exec() == QDialog::Accepted )
00520 {
00521 addPanelByName( display_name, class_id );
00522 }
00523 manager_->startUpdate();
00524 }
00525
00526 void VisualizationFrame::openNewToolDialog()
00527 {
00528 QString class_id;
00529 QStringList empty;
00530 ToolManager* tool_man = manager_->getToolManager();
00531
00532 NewObjectDialog* dialog = new NewObjectDialog( tool_man->getFactory(),
00533 "Tool",
00534 empty,
00535 tool_man->getToolClasses(),
00536 &class_id );
00537 manager_->stopUpdate();
00538 if( dialog->exec() == QDialog::Accepted )
00539 {
00540 tool_man->addTool( class_id );
00541 }
00542 manager_->startUpdate();
00543 activateWindow();
00544 }
00545
00546 void VisualizationFrame::updateRecentConfigMenu()
00547 {
00548 recent_configs_menu_->clear();
00549
00550 D_string::iterator it = recent_configs_.begin();
00551 D_string::iterator end = recent_configs_.end();
00552 for (; it != end; ++it)
00553 {
00554 if( *it != "" )
00555 {
00556 std::string display_name = *it;
00557 if( display_name == default_display_config_file_ )
00558 {
00559 display_name += " (default)";
00560 }
00561 if( display_name.find( home_dir_ ) == 0 )
00562 {
00563 display_name = ("~" / fs::path( display_name.substr( home_dir_.size() ))).BOOST_FILE_STRING();
00564 }
00565 QString qdisplay_name = QString::fromStdString( display_name );
00566 QAction* action = new QAction( qdisplay_name, this );
00567 action->setData( QString::fromStdString( *it ));
00568 connect( action, SIGNAL( triggered() ), this, SLOT( onRecentConfigSelected() ));
00569 recent_configs_menu_->addAction( action );
00570 }
00571 }
00572 }
00573
00574 void VisualizationFrame::markRecentConfig( const std::string& path )
00575 {
00576 D_string::iterator it = std::find( recent_configs_.begin(), recent_configs_.end(), path );
00577 if( it != recent_configs_.end() )
00578 {
00579 recent_configs_.erase( it );
00580 }
00581
00582 recent_configs_.push_front( path );
00583
00584 if( recent_configs_.size() > RECENT_CONFIG_COUNT )
00585 {
00586 recent_configs_.pop_back();
00587 }
00588
00589 updateRecentConfigMenu();
00590 }
00591
00592 void VisualizationFrame::loadDisplayConfig( const QString& qpath )
00593 {
00594 std::string path = qpath.toStdString();
00595 std::string actual_load_path = path;
00596 if( !fs::exists( path ) || fs::is_directory( path ) || fs::is_empty( path ))
00597 {
00598 actual_load_path = (fs::path(package_path_) / "default.rviz").BOOST_FILE_STRING();
00599 if( !fs::exists( actual_load_path ))
00600 {
00601 ROS_ERROR( "Default display config '%s' not found. RViz will be very empty at first.", actual_load_path.c_str() );
00602 return;
00603 }
00604 }
00605
00606
00607
00608 if( !prepareToExit() )
00609 {
00610 return;
00611 }
00612
00613 setWindowModified( false );
00614 loading_ = true;
00615
00616 LoadingDialog* dialog = NULL;
00617 if( initialized_ )
00618 {
00619 dialog = new LoadingDialog( this );
00620 dialog->show();
00621 connect( this, SIGNAL( statusUpdate( const QString& )), dialog, SLOT( showMessage( const QString& )));
00622 }
00623
00624 YamlConfigReader reader;
00625 Config config;
00626 reader.readFile( config, QString::fromStdString( actual_load_path ));
00627 if( !reader.error() )
00628 {
00629 load( config );
00630 }
00631
00632 markRecentConfig( path );
00633
00634 setDisplayConfigFile( path );
00635
00636 last_config_dir_ = fs::path( path ).parent_path().BOOST_FILE_STRING();
00637
00638 delete dialog;
00639
00640 post_load_timer_->start( 1000 );
00641 }
00642
00643 void VisualizationFrame::markLoadingDone()
00644 {
00645 loading_ = false;
00646 }
00647
00648 void VisualizationFrame::setImageSaveDirectory( const QString& directory )
00649 {
00650 last_image_dir_ = directory.toStdString();
00651 }
00652
00653 void VisualizationFrame::setDisplayConfigModified()
00654 {
00655 if( !loading_ )
00656 {
00657 setWindowModified( true );
00658 }
00659 }
00660
00661 void VisualizationFrame::setDisplayConfigFile( const std::string& path )
00662 {
00663 display_config_file_ = path;
00664
00665 std::string title;
00666 if( path == default_display_config_file_ )
00667 {
00668 title = "RViz[*]";
00669 }
00670 else
00671 {
00672 title = fs::path( path ).BOOST_FILENAME_STRING() + "[*] - RViz";
00673 }
00674 setWindowTitle( QString::fromStdString( title ));
00675 }
00676
00677 bool VisualizationFrame::saveDisplayConfig( const QString& path )
00678 {
00679 Config config;
00680 save( config );
00681
00682 YamlConfigWriter writer;
00683 writer.writeFile( config, path );
00684
00685 if( writer.error() )
00686 {
00687 ROS_ERROR( "%s", qPrintable( writer.errorMessage() ));
00688 error_message_ = writer.errorMessage();
00689 return false;
00690 }
00691 else
00692 {
00693 setWindowModified( false );
00694 error_message_ = "";
00695 return true;
00696 }
00697 }
00698
00699 void VisualizationFrame::save( Config config )
00700 {
00701 manager_->save( config.mapMakeChild( "Visualization Manager" ));
00702 savePanels( config.mapMakeChild( "Panels" ));
00703 saveWindowGeometry( config.mapMakeChild( "Window Geometry" ));
00704 }
00705
00706 void VisualizationFrame::load( const Config& config )
00707 {
00708 manager_->load( config.mapGetChild( "Visualization Manager" ));
00709 loadPanels( config.mapGetChild( "Panels" ));
00710 loadWindowGeometry( config.mapGetChild( "Window Geometry" ));
00711 }
00712
00713 void VisualizationFrame::loadWindowGeometry( const Config& config )
00714 {
00715 int x, y;
00716 if( config.mapGetInt( "X", &x ) &&
00717 config.mapGetInt( "Y", &y ))
00718 {
00719 move( x, y );
00720 }
00721
00722 int width, height;
00723 if( config.mapGetInt( "Width", &width ) &&
00724 config.mapGetInt( "Height", &height ))
00725 {
00726 resize( width, height );
00727 }
00728
00729 QString main_window_config;
00730 if( config.mapGetString( "QMainWindow State", &main_window_config ))
00731 {
00732 restoreState( QByteArray::fromHex( qPrintable( main_window_config )));
00733 }
00734
00735
00736 QList<PanelDockWidget *> dock_widgets = findChildren<PanelDockWidget *>();
00737
00738 for ( QList<PanelDockWidget *>::iterator it=dock_widgets.begin(); it!=dock_widgets.end(); it++ )
00739 {
00740 Config itConfig = config.mapGetChild((*it)->windowTitle());
00741
00742 if (itConfig.isValid())
00743 {
00744 (*it)->load(itConfig);
00745 }
00746 }
00747
00748 bool b;
00749 config.mapGetBool( "Hide Left Dock", &b );
00750 hide_left_dock_button_->setChecked( b );
00751 hideLeftDock(b);
00752 config.mapGetBool( "Hide Right Dock", &b );
00753 hideRightDock(b);
00754 hide_right_dock_button_->setChecked( b );
00755 }
00756
00757 void VisualizationFrame::saveWindowGeometry( Config config )
00758 {
00759 config.mapSetValue( "X", x() );
00760 config.mapSetValue( "Y", y() );
00761 config.mapSetValue( "Width", width() );
00762 config.mapSetValue( "Height", height() );
00763
00764 QByteArray window_state = saveState().toHex();
00765 config.mapSetValue( "QMainWindow State", window_state.constData() );
00766
00767 config.mapSetValue( "Hide Left Dock", hide_left_dock_button_->isChecked() );
00768 config.mapSetValue( "Hide Right Dock", hide_right_dock_button_->isChecked() );
00769
00770
00771 QList<PanelDockWidget *> dock_widgets = findChildren<PanelDockWidget *>();
00772
00773 for ( QList<PanelDockWidget *>::iterator it=dock_widgets.begin(); it!=dock_widgets.end(); it++ )
00774 {
00775 (*it)->save(config.mapMakeChild( (*it)->windowTitle() ));
00776 }
00777 }
00778
00779 void VisualizationFrame::loadPanels( const Config& config )
00780 {
00781
00782 for( int i = 0; i < custom_panels_.size(); i++ )
00783 {
00784 delete custom_panels_[ i ].dock;
00785 delete custom_panels_[ i ].delete_action;
00786 }
00787 custom_panels_.clear();
00788
00789
00790 int num_custom_panels = config.listLength();
00791 for( int i = 0; i < num_custom_panels; i++ )
00792 {
00793 Config panel_config = config.listChildAt( i );
00794
00795 QString class_id, name;
00796 if( panel_config.mapGetString( "Class", &class_id ) &&
00797 panel_config.mapGetString( "Name", &name ))
00798 {
00799 QDockWidget* dock = addPanelByName( name, class_id );
00800
00801
00802
00803 if( dock )
00804 {
00805 Panel* panel = qobject_cast<Panel*>( dock->widget() );
00806 if( panel )
00807 {
00808 panel->load( panel_config );
00809 }
00810 }
00811 }
00812 }
00813 }
00814
00815 void VisualizationFrame::savePanels( Config config )
00816 {
00817 config.setType( Config::List );
00818
00819 for( int i = 0; i < custom_panels_.size(); i++ )
00820 {
00821 custom_panels_[ i ].panel->save( config.listAppendNew() );
00822 }
00823 }
00824
00825 bool VisualizationFrame::prepareToExit()
00826 {
00827 if( !initialized_ )
00828 {
00829 return true;
00830 }
00831
00832 savePersistentSettings();
00833
00834 if( isWindowModified() )
00835 {
00836 QMessageBox box( this );
00837 box.setText( "There are unsaved changes." );
00838 box.setInformativeText( QString::fromStdString( "Save changes to " + display_config_file_ + "?" ));
00839 box.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
00840 box.setDefaultButton( QMessageBox::Save );
00841 manager_->stopUpdate();
00842 int result = box.exec();
00843 manager_->startUpdate();
00844 switch( result )
00845 {
00846 case QMessageBox::Save:
00847 if( saveDisplayConfig( QString::fromStdString( display_config_file_ )))
00848 {
00849 return true;
00850 }
00851 else
00852 {
00853 QMessageBox box( this );
00854 box.setWindowTitle( "Failed to save." );
00855 box.setText( getErrorMessage() );
00856 box.setInformativeText( QString::fromStdString( "Save copy of " + display_config_file_ + " to another file?" ));
00857 box.setStandardButtons( QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel );
00858 box.setDefaultButton( QMessageBox::Save );
00859 int result = box.exec();
00860 switch( result )
00861 {
00862 case QMessageBox::Save:
00863 onSaveAs();
00864 return true;
00865 case QMessageBox::Discard:
00866 return true;
00867 default:
00868 return false;
00869 }
00870
00871 }
00872 case QMessageBox::Discard:
00873 return true;
00874 default:
00875 return false;
00876 }
00877 }
00878 else
00879 {
00880 return true;
00881 }
00882 }
00883
00884 void VisualizationFrame::onOpen()
00885 {
00886 manager_->stopUpdate();
00887 QString filename = QFileDialog::getOpenFileName( this, "Choose a file to open",
00888 QString::fromStdString( last_config_dir_ ),
00889 "RViz config files (" CONFIG_EXTENSION_WILDCARD ")" );
00890 manager_->startUpdate();
00891
00892 if( !filename.isEmpty() )
00893 {
00894 std::string path = filename.toStdString();
00895
00896 if( !fs::exists( path ))
00897 {
00898 QString message = filename + " does not exist!";
00899 QMessageBox::critical( this, "Config file does not exist", message );
00900 return;
00901 }
00902
00903 loadDisplayConfig( filename );
00904 }
00905 }
00906
00907 void VisualizationFrame::onSave()
00908 {
00909 if( !initialized_ )
00910 {
00911 return;
00912 }
00913
00914 savePersistentSettings();
00915
00916 if( !saveDisplayConfig( QString::fromStdString( display_config_file_ )))
00917 {
00918 manager_->stopUpdate();
00919 QMessageBox box( this );
00920 box.setWindowTitle( "Failed to save." );
00921 box.setText( getErrorMessage() );
00922 box.setInformativeText( QString::fromStdString( "Save copy of " + display_config_file_ + " to another file?" ));
00923 box.setStandardButtons( QMessageBox::Save | QMessageBox::Cancel );
00924 box.setDefaultButton( QMessageBox::Save );
00925 if( box.exec() == QMessageBox::Save )
00926 {
00927 onSaveAs();
00928 }
00929 manager_->startUpdate();
00930 }
00931 }
00932
00933 void VisualizationFrame::onSaveAs()
00934 {
00935 manager_->stopUpdate();
00936 QString q_filename = QFileDialog::getSaveFileName( this, "Choose a file to save to",
00937 QString::fromStdString( last_config_dir_ ),
00938 "RViz config files (" CONFIG_EXTENSION_WILDCARD ")" );
00939 manager_->startUpdate();
00940
00941 if( !q_filename.isEmpty() )
00942 {
00943 std::string filename = q_filename.toStdString();
00944 fs::path path( filename );
00945 if( path.extension() != "."CONFIG_EXTENSION )
00946 {
00947 filename += "."CONFIG_EXTENSION;
00948 }
00949
00950 if( !saveDisplayConfig( QString::fromStdString( filename )))
00951 {
00952 QMessageBox::critical( this, "Failed to save.", getErrorMessage() );
00953 }
00954
00955 markRecentConfig( filename );
00956 last_config_dir_ = fs::path( filename ).parent_path().BOOST_FILE_STRING();
00957 setDisplayConfigFile( filename );
00958 }
00959 }
00960
00961 void VisualizationFrame::onSaveImage()
00962 {
00963 ScreenshotDialog* dialog = new ScreenshotDialog( this, render_panel_, QString::fromStdString( last_image_dir_ ));
00964 connect( dialog, SIGNAL( savedInDirectory( const QString& )),
00965 this, SLOT( setImageSaveDirectory( const QString& )));
00966 dialog->show();
00967 }
00968
00969 void VisualizationFrame::onRecentConfigSelected()
00970 {
00971 QAction* action = dynamic_cast<QAction*>( sender() );
00972 if( action )
00973 {
00974 std::string path = action->data().toString().toStdString();
00975 if( !path.empty() )
00976 {
00977 if( !fs::exists( path ))
00978 {
00979 QString message = QString::fromStdString( path ) + " does not exist!";
00980 QMessageBox::critical( this, "Config file does not exist", message );
00981 return;
00982 }
00983
00984 loadDisplayConfig( QString::fromStdString( path ));
00985 }
00986 }
00987 }
00988
00989 void VisualizationFrame::addTool( Tool* tool )
00990 {
00991 QAction* action = new QAction( tool->getName(), toolbar_actions_ );
00992 action->setIcon( tool->getIcon() );
00993 action->setIconText( tool->getName() );
00994 action->setCheckable( true );
00995 action->setShortcut( QKeySequence( QString( tool->getShortcutKey() )));
00996 toolbar_->insertAction( add_tool_action_, action );
00997 action_to_tool_map_[ action ] = tool;
00998 tool_to_action_map_[ tool ] = action;
00999
01000 remove_tool_menu_->addAction( tool->getName() );
01001 }
01002
01003 void VisualizationFrame::onToolbarActionTriggered( QAction* action )
01004 {
01005 Tool* tool = action_to_tool_map_[ action ];
01006 if( tool )
01007 {
01008 manager_->getToolManager()->setCurrentTool( tool );
01009 }
01010 }
01011
01012 void VisualizationFrame::onToolbarRemoveTool( QAction* remove_tool_menu_action )
01013 {
01014 QString name = remove_tool_menu_action->text();
01015 for( int i = 0; i < manager_->getToolManager()->numTools(); i++ )
01016 {
01017 Tool* tool = manager_->getToolManager()->getTool( i );
01018 if( tool->getName() == name )
01019 {
01020 manager_->getToolManager()->removeTool( i );
01021 return;
01022 }
01023 }
01024 }
01025
01026 void VisualizationFrame::removeTool( Tool* tool )
01027 {
01028 QAction* action = tool_to_action_map_[ tool ];
01029 if( action )
01030 {
01031 toolbar_actions_->removeAction( action );
01032 toolbar_->removeAction( action );
01033 tool_to_action_map_.erase( tool );
01034 action_to_tool_map_.erase( action );
01035 }
01036 QString tool_name = tool->getName();
01037 QList<QAction*> remove_tool_actions = remove_tool_menu_->actions();
01038 for( int i = 0; i < remove_tool_actions.size(); i++ )
01039 {
01040 QAction* removal_action = remove_tool_actions.at( i );
01041 if( removal_action->text() == tool_name )
01042 {
01043 remove_tool_menu_->removeAction( removal_action );
01044 break;
01045 }
01046 }
01047 }
01048
01049 void VisualizationFrame::indicateToolIsCurrent( Tool* tool )
01050 {
01051 QAction* action = tool_to_action_map_[ tool ];
01052 if( action )
01053 {
01054 action->setChecked( true );
01055 }
01056 }
01057
01058 void VisualizationFrame::showHelpPanel()
01059 {
01060 if( !show_help_action_ )
01061 {
01062 QDockWidget* dock = addPanelByName( "Help", "rviz/Help" );
01063 show_help_action_ = dock->toggleViewAction();
01064 connect( dock, SIGNAL( destroyed( QObject* )), this, SLOT( onHelpDestroyed() ));
01065 }
01066 else
01067 {
01068
01069
01070
01071 show_help_action_->setChecked( false );
01072 show_help_action_->trigger();
01073 }
01074 }
01075
01076 void VisualizationFrame::onHelpDestroyed()
01077 {
01078 show_help_action_ = NULL;
01079 }
01080
01081 void VisualizationFrame::onHelpWiki()
01082 {
01083 QDesktopServices::openUrl( QUrl( "http://www.ros.org/wiki/rviz" ));
01084 }
01085
01086 QWidget* VisualizationFrame::getParentWindow()
01087 {
01088 return this;
01089 }
01090
01091 void VisualizationFrame::onDeletePanel()
01092 {
01093
01094
01095
01096
01097
01098 if( QAction* action = qobject_cast<QAction*>( sender() ))
01099 {
01100 for( int i = 0; i < custom_panels_.size(); i++ )
01101 {
01102 if( custom_panels_[ i ].delete_action == action )
01103 {
01104 delete custom_panels_[ i ].dock;
01105 custom_panels_.removeAt( i );
01106 setDisplayConfigModified();
01107 action->deleteLater();
01108 if( delete_view_menu_->actions().size() == 1 &&
01109 delete_view_menu_->actions().first() == action )
01110 {
01111 delete_view_menu_->setEnabled( false );
01112 }
01113 return;
01114 }
01115 }
01116 }
01117 }
01118
01119 QDockWidget* VisualizationFrame::addPanelByName( const QString& name,
01120 const QString& class_id,
01121 Qt::DockWidgetArea area,
01122 bool floating )
01123 {
01124 QString error;
01125 Panel* panel = panel_factory_->make( class_id, &error );
01126 if( !panel )
01127 {
01128 panel = new FailedPanel( class_id, error );
01129 }
01130 panel->setName( name );
01131 connect( panel, SIGNAL( configChanged() ), this, SLOT( setDisplayConfigModified() ));
01132
01133 PanelRecord record;
01134 record.dock = addPane( name, panel, area, floating );
01135 record.panel = panel;
01136 record.name = name;
01137 record.delete_action = delete_view_menu_->addAction( name, this, SLOT( onDeletePanel() ));
01138 custom_panels_.append( record );
01139 delete_view_menu_->setEnabled( true );
01140
01141 record.panel->initialize( manager_ );
01142
01143 record.dock->setIcon( panel_factory_->getIcon( class_id ) );
01144
01145 return record.dock;
01146 }
01147
01148 PanelDockWidget* VisualizationFrame::addPane( const QString& name, QWidget* panel, Qt::DockWidgetArea area, bool floating )
01149 {
01150 PanelDockWidget *dock;
01151 dock = new PanelDockWidget( name );
01152 dock->setContentWidget( panel );
01153 dock->setFloating( floating );
01154 dock->setObjectName( name );
01155 addDockWidget( area, dock );
01156
01157
01158 connect( dock, SIGNAL( visibilityChanged( bool )), this, SLOT( onDockPanelVisibilityChange( bool ) ));
01159
01160 QAction* toggle_action = dock->toggleViewAction();
01161 view_menu_->addAction( toggle_action );
01162
01163 connect( toggle_action, SIGNAL( triggered( bool )), this, SLOT( setDisplayConfigModified() ));
01164 connect( dock, SIGNAL( closed()), this, SLOT( setDisplayConfigModified() ));
01165
01166 dock->installEventFilter( geom_change_detector_ );
01167
01168
01169 hideLeftDock( area == Qt::LeftDockWidgetArea ? false : hide_left_dock_button_->isChecked() );
01170 hideRightDock( area == Qt::RightDockWidgetArea ? false : hide_right_dock_button_->isChecked() );
01171
01172 return dock;
01173 }
01174
01175 }