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