visualization_frame.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2012, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <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   // make toolbar with plugin tools
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     // allow/disallow docking to this area for all widgets
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   // if a dock widget becomes visible and is resting inside the
00485   // left or right dock area, we want to unhide the whole area
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(); // Force keyboard focus back on main window.
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   // Check if we have unsaved changes to the current config the same
00607   // as we do during exit, with the same option to cancel.
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   // load panel dock widget states (collapsed or not)
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   // save panel dock widget states (collapsed or not)
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   // First destroy any existing custom panels.
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   // Then load the ones in the config.
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       // This is kind of ridiculous - should just be something like
00801       // createPanel() and addPanel() so I can do load() without this
00802       // qobject_cast.
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 ); // Not really necessary, but gives an empty list if there are no entries, instead of an Empty config node.
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     // show_help_action_ is a toggle action, so trigger() changes its
01069     // state.  Therefore we must force it to the opposite state from
01070     // what we want before we call trigger().  (I think.)
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   // This should only be called as a SLOT from a QAction in the
01094   // "delete panel" submenu, so the sender will be one of the QActions
01095   // stored as "delete_action" in a PanelRecord.  This code looks for
01096   // a delete_action in custom_panels_ matching sender() and removes
01097   // the panel associated with it.
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 ); // QMainWindow::saveState() needs objectName to be set.
01155   addDockWidget( area, dock );
01156 
01157   // we want to know when that panel becomes visible
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   // repair/update visibility status
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 } // end namespace rviz


rviz
Author(s): Dave Hershberger, David Gossow, Josh Faust
autogenerated on Mon Oct 6 2014 07:26:36