visualization_frame.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, 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 <QSplashScreen>
00031 #include <QDockWidget>
00032 #include <QDir>
00033 #include <QCloseEvent>
00034 #include <QToolBar>
00035 #include <QMenuBar>
00036 #include <QMenu>
00037 #include <QMessageBox>
00038 #include <QFileDialog>
00039 #include <QDesktopServices>
00040 #include <QUrl>
00041 
00042 #include <boost/filesystem.hpp>
00043 #include <boost/bind.hpp>
00044 #include <boost/algorithm/string/split.hpp>
00045 #include <boost/algorithm/string/trim.hpp>
00046 
00047 #include <ros/package.h>
00048 #include <ros/console.h>
00049 
00050 #include <ogre_tools/initialization.h>
00051 #include <ogre_tools/render_system.h>
00052 
00053 #include "visualization_frame.h"
00054 #include "render_panel.h"
00055 #include "displays_panel.h"
00056 #include "views_panel.h"
00057 #include "time_panel.h"
00058 #include "selection_panel.h"
00059 #include "tool_properties_panel.h"
00060 #include "visualization_manager.h"
00061 #include "tools/tool.h"
00062 #include "loading_dialog.h"
00063 #include "config.h"
00064 #include "panel_dock_widget.h"
00065 
00067 // #include <gdk/gdk.h>
00068 // #include <gdk/gdkx.h>
00069 
00070 namespace fs = boost::filesystem;
00071 
00072 #define CONFIG_WINDOW_X "/Window/X"
00073 #define CONFIG_WINDOW_Y "/Window/Y"
00074 #define CONFIG_WINDOW_WIDTH "/Window/Width"
00075 #define CONFIG_WINDOW_HEIGHT "/Window/Height"
00076 // I am not trying to preserve peoples' window layouts from wx to Qt,
00077 // just saving the Qt layout in a new config tag.
00078 #define CONFIG_QMAINWINDOW "/QMainWindow"
00079 #define CONFIG_AUIMANAGER_PERSPECTIVE "/AuiManagerPerspective"
00080 #define CONFIG_AUIMANAGER_PERSPECTIVE_VERSION "/AuiManagerPerspectiveVersion"
00081 #define CONFIG_RECENT_CONFIGS "/RecentConfigs"
00082 #define CONFIG_LAST_DIR "/LastConfigDir"
00083 
00084 #define CONFIG_EXTENSION "vcg"
00085 #define CONFIG_EXTENSION_WILDCARD "*."CONFIG_EXTENSION
00086 #define PERSPECTIVE_VERSION 2
00087 
00088 #define RECENT_CONFIG_COUNT 10
00089 
00090 namespace rviz
00091 {
00092 
00093 VisualizationFrame::VisualizationFrame( QWidget* parent )
00094   : QMainWindow( parent )
00095   , render_panel_(NULL)
00096   , displays_panel_(NULL)
00097   , views_panel_(NULL)
00098   , time_panel_(NULL)
00099   , selection_panel_(NULL)
00100   , tool_properties_panel_(NULL)
00101   , file_menu_(NULL)
00102   , recent_configs_menu_(NULL)
00103   , toolbar_(NULL)
00104   , manager_(NULL)
00105   , position_correction_( 0, 0 )
00106   , num_move_events_( 0 )
00107   , toolbar_actions_( NULL )
00108 {
00109   setWindowTitle( "RViz" );
00110 }
00111 
00112 VisualizationFrame::~VisualizationFrame()
00113 {
00114   if( manager_ )
00115   {
00116     manager_->removeAllDisplays();
00117   }
00118 
00119   delete render_panel_;
00120   delete manager_;
00121 }
00122 
00123 void VisualizationFrame::closeEvent( QCloseEvent* event )
00124 {
00125   if( general_config_ )
00126   {
00127     saveConfigs();
00128   }
00129   event->accept();
00130 }
00131 
00132 void VisualizationFrame::onSplashLoadStatus( const std::string& status )
00133 {
00134   splash_->showMessage( QString::fromStdString( status ));
00135 }
00136 
00137 void VisualizationFrame::initialize(const std::string& display_config_file,
00138                                     const std::string& fixed_frame,
00139                                     const std::string& target_frame,
00140                                     const std::string& splash_path,
00141                                     bool verbose )
00142 {
00143   initConfigs();
00144 
00145   int new_x, new_y, new_width, new_height;
00146   general_config_->get( CONFIG_WINDOW_X, &new_x, x() );
00147   general_config_->get( CONFIG_WINDOW_Y, &new_y, y() );
00148   general_config_->get( CONFIG_WINDOW_WIDTH, &new_width, width() );
00149   general_config_->get( CONFIG_WINDOW_HEIGHT, &new_height, height() );
00150 
00151   {
00152     std::string recent;
00153     if( general_config_->get( CONFIG_RECENT_CONFIGS, &recent ))
00154     {
00155       boost::trim( recent );
00156       boost::split( recent_configs_, recent, boost::is_any_of (":"), boost::token_compress_on );
00157     }
00158 
00159     general_config_->get( CONFIG_LAST_DIR, &last_config_dir_ );
00160   }
00161 
00162   move( new_x, new_y );
00163   resize( new_width, new_height );
00164 
00165   package_path_ = ros::package::getPath("rviz_qt");
00166 
00167   std::string final_splash_path = splash_path;
00168 
00169   if ( splash_path.empty() )
00170   {
00171 #if BOOST_FILESYSTEM_VERSION == 3
00172     final_splash_path = (fs::path(package_path_) / "images/splash.png").string();
00173 #else
00174     final_splash_path = (fs::path(package_path_) / "images/splash.png").file_string();
00175 #endif
00176   }
00177   QPixmap splash_image( QString::fromStdString( final_splash_path ));
00178   splash_ = new QSplashScreen( splash_image );
00179   splash_->show();
00180   splash_->showMessage( "Initializing" );
00181 
00182   if( !ros::isInitialized() )
00183   {
00184     int argc = 0;
00185     ros::init( argc, 0, "rviz", ros::init_options::AnonymousName );
00186   }
00187 
00188   render_panel_ = new RenderPanel( ogre_tools::RenderSystem::get(), 0, this );
00189   displays_panel_ = new DisplaysPanel( this );
00190   views_panel_ = new ViewsPanel( this );
00191   time_panel_ = new TimePanel( this );
00192   selection_panel_ = new SelectionPanel( this );
00193   tool_properties_panel_ = new ToolPropertiesPanel( this );
00194 
00195   splash_->showMessage( "Initializing OGRE resources" );
00196   ogre_tools::V_string paths;
00197   paths.push_back( package_path_ + "/ogre_media/textures" );
00198   ogre_tools::initializeResources( paths );
00199 
00200   initMenus();
00201   toolbar_ = addToolBar( "Tools" );
00202   toolbar_->setObjectName( "Tools" );
00203   toolbar_actions_ = new QActionGroup( this );
00204   connect( toolbar_actions_, SIGNAL( triggered( QAction* )), this, SLOT( onToolbarActionTriggered( QAction* )));
00205   view_menu_->addAction( toolbar_->toggleViewAction() );
00206 
00207   setCentralWidget( render_panel_ );
00208 
00209   addPane( "Displays", displays_panel_, Qt::LeftDockWidgetArea, false );
00210   addPane( "Tool Properties", tool_properties_panel_, Qt::RightDockWidgetArea, false );
00211   addPane( "Views", views_panel_, Qt::RightDockWidgetArea, false );
00212   addPane( "Selection", selection_panel_, Qt::RightDockWidgetArea, false );
00213   addPane( "Time", time_panel_, Qt::BottomDockWidgetArea, false );
00214 
00215   manager_ = new VisualizationManager( render_panel_, this );
00216   render_panel_->initialize( manager_->getSceneManager(), manager_ );
00217   displays_panel_->initialize( manager_ );
00218   views_panel_->initialize( manager_ );
00219   time_panel_->initialize(manager_);
00220   selection_panel_->initialize( manager_ );
00221   tool_properties_panel_->initialize( manager_ );
00222 
00223   connect( manager_, SIGNAL( toolAdded( Tool* )), this, SLOT( addTool( Tool* )));
00224   connect( manager_, SIGNAL( toolChanged( Tool* )), this, SLOT( indicateToolIsCurrent( Tool* )));
00225 
00226   manager_->initialize( StatusCallback(), verbose );
00227   manager_->loadGeneralConfig(general_config_, boost::bind( &VisualizationFrame::onSplashLoadStatus, this, _1 ));
00228 
00229   bool display_config_valid = !display_config_file.empty();
00230   if( display_config_valid && !fs::exists( display_config_file ))
00231   {
00232     ROS_ERROR("File [%s] does not exist", display_config_file.c_str());
00233     display_config_valid = false;
00234   }
00235 
00236   if( !display_config_valid )
00237   {
00238     manager_->loadDisplayConfig( display_config_, boost::bind( &VisualizationFrame::onSplashLoadStatus, this, _1 ));
00239   }
00240   else
00241   {
00242     boost::shared_ptr<Config> config( new Config );
00243     config->readFromFile( display_config_file ); 
00244     manager_->loadDisplayConfig( config, boost::bind( &VisualizationFrame::onSplashLoadStatus, this, _1 ));
00245   }
00246 
00247   if( !fixed_frame.empty() )
00248   {
00249     manager_->setFixedFrame( fixed_frame );
00250   }
00251 
00252   if( !target_frame.empty() )
00253   {
00254     manager_->setTargetFrame( target_frame );
00255   }
00256 
00257   splash_->showMessage( "Loading perspective" );
00258 
00259   std::string main_window_config;
00260   if( general_config_->get( CONFIG_QMAINWINDOW, &main_window_config ))
00261   {
00262     restoreState( QByteArray::fromHex( main_window_config.c_str() ));
00263   }
00264 
00265   updateRecentConfigMenu();
00266   if( display_config_valid )
00267   {
00268     markRecentConfig( display_config_file );
00269   }
00270 
00271   delete splash_;
00272   splash_ = 0;
00273 
00274   manager_->startUpdate();
00275 }
00276 
00277 void VisualizationFrame::initConfigs()
00278 {
00279   config_dir_ = QDir::toNativeSeparators( QDir::homePath() ).toStdString();
00280 #if BOOST_FILESYSTEM_VERSION == 3
00281   std::string old_dir = (fs::path(config_dir_) / ".standalone_visualizer").string();
00282   config_dir_ = (fs::path(config_dir_) / ".rviz_qt").string();
00283   general_config_file_ = (fs::path(config_dir_) / "config").string();
00284   display_config_file_ = (fs::path(config_dir_) / "display_config").string();
00285 #else
00286   std::string old_dir = (fs::path(config_dir_) / ".standalone_visualizer").file_string();
00287   config_dir_ = (fs::path(config_dir_) / ".rviz_qt").file_string();
00288   general_config_file_ = (fs::path(config_dir_) / "config").file_string();
00289   display_config_file_ = (fs::path(config_dir_) / "display_config").file_string();
00290 #endif
00291 
00292   if( fs::exists( old_dir ) && !fs::exists( config_dir_ ))
00293   {
00294     ROS_INFO("Migrating old config directory to new location ([%s] to [%s])", old_dir.c_str(), config_dir_.c_str());
00295     fs::rename( old_dir, config_dir_ );
00296   }
00297 
00298   if( fs::is_regular_file( config_dir_ ))
00299   {
00300     ROS_INFO("Migrating old config file to new location ([%s] to [%s])", config_dir_.c_str(), general_config_file_.c_str());
00301     std::string backup_file = config_dir_ + "bak";
00302 
00303     fs::rename(config_dir_, backup_file);
00304     fs::create_directory(config_dir_);
00305     fs::rename(backup_file, general_config_file_);
00306   }
00307   else if (!fs::exists(config_dir_))
00308   {
00309     fs::create_directory(config_dir_);
00310   }
00311 
00312   if (fs::exists(general_config_file_) && !fs::exists(display_config_file_))
00313   {
00314     ROS_INFO("Creating display config from general config");
00315     fs::copy_file(general_config_file_, display_config_file_);
00316   }
00317 
00318   ROS_INFO("Loading general config from [%s]", general_config_file_.c_str());
00319   general_config_.reset( new Config );
00320   general_config_->readFromFile( general_config_file_ );
00321 
00322   ROS_INFO("Loading display config from [%s]", display_config_file_.c_str());
00323   display_config_.reset( new Config );
00324   display_config_->readFromFile( display_config_file_ );
00325 }
00326 
00327 void VisualizationFrame::initMenus()
00328 {
00329   file_menu_ = menuBar()->addMenu( "&File" );
00330   file_menu_->addAction( "&Open Config", this, SLOT( onOpen() ), QKeySequence( "Ctrl+O" ));
00331   file_menu_->addAction( "&Save Config", this, SLOT( onSave() ), QKeySequence( "Ctrl+S" ));
00332   recent_configs_menu_ = file_menu_->addMenu( "&Recent Configs" );
00333   file_menu_->addSeparator();
00334   file_menu_->addAction( "&Quit", this, SLOT( close() ), QKeySequence( "Ctrl+Q" ));
00335 
00336   view_menu_ = menuBar()->addMenu( "&View" );
00337 
00343 
00344   QMenu* help_menu = menuBar()->addMenu( "&Help" );
00345   help_menu->addAction( "Wiki", this, SLOT( onHelpWiki() ));
00346 }
00347 
00348 void VisualizationFrame::updateRecentConfigMenu()
00349 {
00350   recent_configs_menu_->clear();
00351 
00352   D_string::iterator it = recent_configs_.begin();
00353   D_string::iterator end = recent_configs_.end();
00354   for (; it != end; ++it)
00355   {
00356     if( *it != "" )
00357     {
00358       recent_configs_menu_->addAction( QString::fromStdString( *it ), this, SLOT( onRecentConfigSelected() ));
00359     }
00360   }
00361 }
00362 
00363 void VisualizationFrame::markRecentConfig( const std::string& path )
00364 {
00365   D_string::iterator it = std::find( recent_configs_.begin(), recent_configs_.end(), path );
00366   if( it != recent_configs_.end() )
00367   {
00368     recent_configs_.erase( it );
00369   }
00370 
00371   recent_configs_.push_front( path );
00372 
00373   if( recent_configs_.size() > RECENT_CONFIG_COUNT )
00374   {
00375     recent_configs_.pop_back();
00376   }
00377 
00378   updateRecentConfigMenu();
00379 }
00380 
00381 void VisualizationFrame::loadDisplayConfig( const std::string& path )
00382 {
00383   if( !fs::exists( path ))
00384   {
00385     QString message = QString::fromStdString( path  ) + " does not exist!";
00386     QMessageBox::critical( this, "Config file does not exist", message );
00387     return;
00388   }
00389 
00390   manager_->removeAllDisplays();
00391 
00392   LoadingDialog dialog( this );
00393   dialog.show();
00394 
00395   boost::shared_ptr<Config> config( new Config );
00396   config->readFromFile( path );
00397   manager_->loadDisplayConfig( config, boost::bind( &LoadingDialog::setState, &dialog, _1 ));
00398 
00399   markRecentConfig(path);
00400 }
00401 
00402 
00403 void VisualizationFrame::moveEvent( QMoveEvent* event )
00404 {
00405 //  GdkRectangle rect;
00406 //  GdkWindow* gdk_window = gdk_window_foreign_new( winId() ); 
00407 //  gdk_window_get_frame_extents( gdk_window, &rect );
00408 //  printf( "gdk x=%d, y=%d\n", rect.x, rect.y );
00409 // the above works!  should I just use gdk??
00410 
00411   // HACK to work around a bug in Qt-for-X11.  The first time we get a
00412   // moveEvent, the position is that of the top-left corner of the
00413   // window frame.  The second time we get one, the position is the
00414   // top-left corner *inside* the frame.  There is no significant time
00415   // lag between the two calls, certainly no user events, so I just
00416   // remember the first position and diff it with the second position
00417   // and remember the diff as a corrective offset for future geometry
00418   // requests.
00419   //
00420   // This seems like it would be brittle to OS, code changes, etc, so
00421   // sometime I should get something better going here.  Maybe call
00422   // out to gdk (as above), which seems to work right.
00423   switch( num_move_events_ )
00424   {
00425   case 0:
00426     first_position_ = pos();
00427     num_move_events_++;
00428     break;
00429   case 1:
00430     position_correction_ = first_position_ - pos();
00431     num_move_events_++;
00432     break;
00433   }
00434 }
00435 
00436 QRect VisualizationFrame::hackedFrameGeometry()
00437 {
00438   QRect geom = frameGeometry();
00439   geom.moveTopLeft( pos() + position_correction_ );
00440   return geom;
00441 }
00442 
00443 void VisualizationFrame::saveConfigs()
00444 {
00445   ROS_INFO("Saving general config to [%s]", general_config_file_.c_str());
00446   general_config_->clear();
00447   QRect geom = hackedFrameGeometry();
00448   general_config_->set( CONFIG_WINDOW_X, geom.x() );
00449   general_config_->set( CONFIG_WINDOW_Y, geom.y() );
00450   general_config_->set( CONFIG_WINDOW_WIDTH, geom.width() );
00451   general_config_->set( CONFIG_WINDOW_HEIGHT, geom.height() );
00452 
00453   QByteArray window_state = saveState().toHex();
00454   general_config_->set( CONFIG_QMAINWINDOW, std::string( window_state.constData() ));
00455 
00456   {
00457     std::stringstream ss;
00458     D_string::iterator it = recent_configs_.begin();
00459     D_string::iterator end = recent_configs_.end();
00460     for (; it != end; ++it)
00461     {
00462       if (it != recent_configs_.begin())
00463       {
00464         ss << ":";
00465       }
00466       ss << *it;
00467     }
00468 
00469     general_config_->set( CONFIG_RECENT_CONFIGS, ss.str() );
00470   }
00471 
00472   general_config_->set( CONFIG_LAST_DIR, last_config_dir_ );
00473 
00474   manager_->saveGeneralConfig( general_config_ );
00475   general_config_->writeToFile( general_config_file_ );
00476 
00477   ROS_INFO( "Saving display config to [%s]", display_config_file_.c_str() );
00478   display_config_->clear();
00479   manager_->saveDisplayConfig( display_config_ );
00480   display_config_->writeToFile( display_config_file_ );
00481 }
00482 
00483 void VisualizationFrame::onOpen()
00484 {
00485   QString filename = QFileDialog::getOpenFileName( this, "Choose a file to open",
00486                                                    QString::fromStdString( last_config_dir_ ),
00487                                                    "RViz config files (" CONFIG_EXTENSION_WILDCARD ")" );
00488 
00489   if( !filename.isEmpty() )
00490   {
00491     std::string filename_string = filename.toStdString();
00492     loadDisplayConfig( filename_string );
00493     last_config_dir_ = fs::path( filename_string ).parent_path().string();
00494   }
00495 }
00496 
00497 void VisualizationFrame::onSave()
00498 {
00499   QString q_filename = QFileDialog::getSaveFileName( this, "Choose a file to save to",
00500                                                      QString::fromStdString( last_config_dir_ ),
00501                                                      "RViz config files (" CONFIG_EXTENSION_WILDCARD ")" );
00502 
00503   if( !q_filename.isEmpty() )
00504   {
00505     std::string filename = q_filename.toStdString();
00506     fs::path path( filename );
00507     if( path.extension() != "."CONFIG_EXTENSION )
00508     {
00509       filename += "."CONFIG_EXTENSION;
00510     }
00511 
00512     boost::shared_ptr<Config> config( new Config() );
00513     manager_->saveDisplayConfig( config );
00514     config->writeToFile( filename );
00515 
00516     markRecentConfig( filename );
00517 
00518     last_config_dir_ = fs::path( filename ).parent_path().string();
00519   }
00520 }
00521 
00522 void VisualizationFrame::onRecentConfigSelected()
00523 {
00524   QAction* action = dynamic_cast<QAction*>( sender() );
00525   if( action )
00526   {
00527     std::string path = action->text().toStdString();
00528     if( !path.empty() )
00529     {
00530       loadDisplayConfig( path );
00531     }
00532   }
00533 }
00534 
00535 void VisualizationFrame::addTool( Tool* tool )
00536 {
00537   QAction* action = new QAction( QString::fromStdString( tool->getName() ), toolbar_actions_ );
00538   action->setCheckable( true );
00539   action->setShortcut( QKeySequence( QString( tool->getShortcutKey() )));
00540   toolbar_->addAction( action );
00541   action_to_tool_map_[ action ] = tool;
00542   tool_to_action_map_[ tool ] = action;
00543 }
00544 
00545 void VisualizationFrame::onToolbarActionTriggered( QAction* action )
00546 {
00547   Tool* tool = action_to_tool_map_[ action ];
00548   if( tool )
00549   {
00550     manager_->setCurrentTool( tool );
00551   }
00552 }
00553 
00554 void VisualizationFrame::indicateToolIsCurrent( Tool* tool )
00555 {
00556   QAction* action = tool_to_action_map_[ tool ];
00557   if( action )
00558   {
00559     action->setChecked( true );
00560   }
00561 }
00562 
00569 void VisualizationFrame::onHelpWiki()
00570 {
00571   QDesktopServices::openUrl( QUrl( "http://www.ros.org/wiki/rviz" ));
00572 }
00573 
00574 QWidget* VisualizationFrame::getParentWindow()
00575 {
00576   return this;
00577 }
00578 
00579 PanelDockWidget* VisualizationFrame::addPane( const std::string& name, QWidget* panel, Qt::DockWidgetArea area, bool floating )
00580 {
00581   QString q_name = QString::fromStdString( name );
00582   PanelDockWidget *dock;
00583   dock = new PanelDockWidget( q_name, this );
00584   dock->setWidget( panel );
00585   dock->setFloating( floating );
00586   dock->setObjectName( q_name );
00587   addDockWidget( area, dock );
00588   view_menu_->addAction( dock->toggleViewAction() );
00589   return dock;
00590 }
00591 
00592 }


rviz_qt
Author(s): Dave Hershberger
autogenerated on Fri Dec 6 2013 20:56:53