34 #include <QActionGroup> 
   36 #include <QApplication> 
   37 #include <QCoreApplication> 
   38 #include <QCloseEvent> 
   39 #include <QDesktopServices> 
   40 #include <QDockWidget> 
   42 #include <QFileDialog> 
   45 #include <QMessageBox> 
   48 #include <QToolButton> 
   52 #include <QToolButton> 
   53 #include <QHBoxLayout> 
   56 #include <boost/algorithm/string/split.hpp> 
   57 #include <boost/algorithm/string/trim.hpp> 
   58 #include <boost/bind/bind.hpp> 
   59 #include <boost/filesystem.hpp> 
   65 #include <OgreRenderWindow.h> 
   66 #include <OgreMeshManager.h> 
   98 namespace fs = boost::filesystem;
 
  100 #define CONFIG_EXTENSION "rviz" 
  101 #define CONFIG_EXTENSION_WILDCARD "*." CONFIG_EXTENSION 
  102 #define RECENT_CONFIG_COUNT 10 
  107   : QMainWindow(parent)
 
  108   , render_panel_(nullptr)
 
  109   , show_help_action_(nullptr)
 
  111   , file_menu_(nullptr)
 
  112   , recent_configs_menu_(nullptr)
 
  116   , toolbar_actions_(nullptr)
 
  117   , show_choose_new_master_option_(false)
 
  118   , toolbar_separator_(nullptr)
 
  119   , remove_tool_menu_(nullptr)
 
  120   , initialized_(false)
 
  123   , post_load_timer_(new QTimer(this))
 
  125   , toolbar_visible_(true)
 
  140   QToolButton* reset_button = 
new QToolButton();
 
  141   reset_button->setText(
"Reset");
 
  142   reset_button->setContentsMargins(0, 0, 0, 0);
 
  143   statusBar()->addPermanentWidget(reset_button, 0);
 
  144   connect(reset_button, &QToolButton::clicked, 
this, &VisualizationFrame::VisualizationFrame::reset);
 
  148   connect(
this, &VisualizationFrame::VisualizationFrame::statusUpdate, 
status_label_, &QLabel::setText);
 
  153   statusBar()->addPermanentWidget(
fps_label_, 0);
 
  156   setWindowTitle(
"RViz[*]");
 
  184   if (wall_diff.
toSec() > 1.0)
 
  191       fps_label_->setText(QString::number(
int(fps)) + QString(
" fps"));
 
  215   Ogre::MeshManager::getSingleton().removeAll();
 
  223     QApplication::exit(255);
 
  249   QIcon app_icon(QString::fromStdString((fs::path(
package_path_) / 
"icons/package.png").
string()));
 
  250   setWindowIcon(app_icon);
 
  257     connect(
this, &VisualizationFrame::VisualizationFrame::statusUpdate, 
splash_,
 
  258             [
this](
const QString& message) { 
splash_->showMessage(message); });
 
  264   QCoreApplication::processEvents();
 
  273   QCoreApplication::processEvents();
 
  275   QWidget* central_widget = 
new QWidget(
this);
 
  276   QHBoxLayout* central_layout = 
new QHBoxLayout;
 
  277   central_layout->setSpacing(0);
 
  278   central_layout->setContentsMargins(0, 0, 0, 0);
 
  291           &VisualizationFrame::VisualizationFrame::hideLeftDock);
 
  302           &VisualizationFrame::VisualizationFrame::hideRightDock);
 
  308   central_widget->setLayout(central_layout);
 
  311   QCoreApplication::processEvents();
 
  316   QCoreApplication::processEvents();
 
  321   QCoreApplication::processEvents();
 
  323   setCentralWidget(central_widget);
 
  326   QCoreApplication::processEvents();
 
  331           &VisualizationFrame::VisualizationFrame::exitFullScreen);
 
  334   QCoreApplication::processEvents();
 
  339   QCoreApplication::processEvents();
 
  348           &VisualizationFrame::VisualizationFrame::refreshTool);
 
  350           &VisualizationFrame::VisualizationFrame::indicateToolIsCurrent);
 
  355   QCoreApplication::processEvents();
 
  357   if (display_config_file != 
"")
 
  367   QCoreApplication::processEvents();
 
  377           &VisualizationFrame::VisualizationFrame::updateFps);
 
  379           &VisualizationFrame::VisualizationFrame::statusUpdate);
 
  384   home_dir_ = QDir::toNativeSeparators(QDir::homePath()).toStdString();
 
  392     ROS_ERROR(
"Moving file [%s] out of the way to recreate it as a directory.", 
config_dir_.c_str());
 
  411     QString last_config_dir, last_image_dir;
 
  412     if (
config.mapGetString(
"Last Config Dir", &last_config_dir) &&
 
  413         config.mapGetString(
"Last Image Dir", &last_image_dir))
 
  419     Config recent_configs_list = 
config.mapGetChild(
"Recent Configs");
 
  421     int num_recent = recent_configs_list.
listLength();
 
  422     for (
int i = 0; i < num_recent; i++)
 
  438   Config recent_configs_list = 
config.mapMakeChild(
"Recent Configs");
 
  457   QAction* file_menu_open_action =
 
  459   this->addAction(file_menu_open_action);
 
  460   QAction* file_menu_save_action =
 
  462   this->addAction(file_menu_save_action);
 
  463   QAction* file_menu_save_as_action = 
file_menu_->addAction(
 
  465   this->addAction(file_menu_save_as_action);
 
  476                         QKeySequence(
"Ctrl+P"));
 
  478   QAction* file_menu_quit_action =
 
  479       file_menu_->addAction(
"&Quit", 
this, &VisualizationFrame::close, QKeySequence(
"Ctrl+Q"));
 
  480   file_menu_quit_action->setObjectName(
"actQuit");
 
  481   this->addAction(file_menu_quit_action);
 
  488   QAction* fullscreen_action =
 
  490   fullscreen_action->setCheckable(
true);
 
  496   QMenu* help_menu = menuBar()->addMenu(
"&Help");
 
  499   help_menu->addSeparator();
 
  506   font.setPointSize(font.pointSizeF() * 0.9);
 
  512   toolbar_->setContentsMargins(0, 0, 0, 0);
 
  514   toolbar_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
 
  522   QToolButton* add_tool_button = 
new QToolButton();
 
  523   add_tool_button->setToolTip(
"Add a new tool");
 
  524   add_tool_button->setIcon(
loadPixmap(
"package://rviz/icons/plus.png"));
 
  525   toolbar_->addWidget(add_tool_button);
 
  526   connect(add_tool_button, &QToolButton::clicked, 
this,
 
  527           &VisualizationFrame::VisualizationFrame::openNewToolDialog);
 
  530   QToolButton* remove_tool_button = 
new QToolButton();
 
  532   remove_tool_button->setPopupMode(QToolButton::InstantPopup);
 
  533   remove_tool_button->setToolTip(
"Remove a tool from the toolbar");
 
  534   remove_tool_button->setIcon(
loadPixmap(
"package://rviz/icons/minus.png"));
 
  535   toolbar_->addWidget(remove_tool_button);
 
  537           &VisualizationFrame::VisualizationFrame::onToolbarRemoveTool);
 
  539   QMenu* button_style_menu = 
new QMenu(
toolbar_);
 
  540   QAction* action_tool_button_icon_only = 
new QAction(
"Icon only", 
toolbar_actions_);
 
  541   action_tool_button_icon_only->setData(Qt::ToolButtonIconOnly);
 
  542   button_style_menu->addAction(action_tool_button_icon_only);
 
  543   QAction* action_tool_button_text_only = 
new QAction(
"Text only", 
toolbar_actions_);
 
  544   action_tool_button_text_only->setData(Qt::ToolButtonTextOnly);
 
  545   button_style_menu->addAction(action_tool_button_text_only);
 
  546   QAction* action_tool_button_text_beside_icon = 
new QAction(
"Text beside icon", 
toolbar_actions_);
 
  547   action_tool_button_text_beside_icon->setData(Qt::ToolButtonTextBesideIcon);
 
  548   button_style_menu->addAction(action_tool_button_text_beside_icon);
 
  549   QAction* action_tool_button_text_under_icon = 
new QAction(
"Text under icon", 
toolbar_actions_);
 
  550   action_tool_button_text_under_icon->setData(Qt::ToolButtonTextUnderIcon);
 
  551   button_style_menu->addAction(action_tool_button_text_under_icon);
 
  553   QToolButton* button_style_button = 
new QToolButton();
 
  554   button_style_button->setMenu(button_style_menu);
 
  555   button_style_button->setPopupMode(QToolButton::InstantPopup);
 
  556   button_style_button->setToolTip(
"Set toolbar style");
 
  557   button_style_button->setIcon(
loadPixmap(
"package://rviz/icons/visibility.svg"));
 
  558   toolbar_->addWidget(button_style_button);
 
  559   connect(button_style_menu, &QMenu::triggered, 
this,
 
  560           &VisualizationFrame::VisualizationFrame::onButtonStyleTool);
 
  565   QList<PanelDockWidget*> dock_widgets = findChildren<PanelDockWidget*>();
 
  567   for (QList<PanelDockWidget*>::iterator it = dock_widgets.begin(); it != dock_widgets.end(); it++)
 
  569     Qt::DockWidgetArea curr_area = dockWidgetArea(*it);
 
  570     if (area == curr_area)
 
  572       (*it)->setCollapsed(hide);
 
  577       (*it)->setAllowedAreas((*it)->allowedAreas() & ~area);
 
  581       (*it)->setAllowedAreas((*it)->allowedAreas() | area);
 
  610     QDockWidget* dock_widget = 
dynamic_cast<QDockWidget*
>(sender());
 
  613       Qt::DockWidgetArea area = dockWidgetArea(dock_widget);
 
  614       if (area == Qt::LeftDockWidgetArea)
 
  618       if (area == Qt::RightDockWidgetArea)
 
  631   if (dialog.exec() == QDialog::Accepted)
 
  634     preferences_ = boost::make_shared<Preferences>(temp_preferences);
 
  642   QString display_name;
 
  648   if (dialog->exec() == QDialog::Accepted)
 
  653       connect(dock, &QDockWidget::dockLocationChanged, 
this,
 
  654               &VisualizationFrame::VisualizationFrame::onDockPanelChange);
 
  669   if (dialog->exec() == QDialog::Accepted)
 
  683   for (; it != end; ++it)
 
  687       std::string display_name = *it;
 
  690         display_name += 
" (default)";
 
  694         display_name = (
"~" / fs::path(display_name.substr(
home_dir_.size()))).
string();
 
  696       QString qdisplay_name = QString::fromStdString(display_name);
 
  697       QAction* action = 
new QAction(qdisplay_name, 
this);
 
  698       action->setData(QString::fromStdString(*it));
 
  699       connect(action, &QAction::triggered, 
this,
 
  700               &VisualizationFrame::VisualizationFrame::onRecentConfigSelected);
 
  726   std::string path = qpath.toStdString();
 
  727   fs::path actual_load_path = path;
 
  728   bool valid_load_path = fs::is_regular_file(actual_load_path);
 
  730   if (!valid_load_path && fs::portable_posix_name(path))
 
  734     actual_load_path = fs::path(
config_dir_) / actual_load_path;
 
  735     if ((valid_load_path = fs::is_regular_file(actual_load_path)))
 
  736       path = actual_load_path.string();
 
  739   if (!valid_load_path)
 
  741     actual_load_path = (fs::path(
package_path_) / 
"default.rviz");
 
  742     if (!fs::is_regular_file(actual_load_path))
 
  744       ROS_ERROR(
"Default display config '%s' not found.  RViz will be very empty at first.",
 
  745                 actual_load_path.c_str());
 
  761   setWindowModified(
false);
 
  764   std::unique_ptr<LoadingDialog> dialog;
 
  769     connect(
this, &VisualizationFrame::VisualizationFrame::statusUpdate, dialog.get(),
 
  773     QCoreApplication::processEvents();
 
  809     if (!isWindowModified())
 
  811       setWindowModified(
true);
 
  827     title = fs::path(path).filename().string() + 
"[*] - RViz";
 
  829   setWindowTitle(QString::fromStdString(title));
 
  849     setWindowModified(
false);
 
  876   if (
config.mapGetInt(
"X", &x) && 
config.mapGetInt(
"Y", &y))
 
  882   if (
config.mapGetInt(
"Width", &width) && 
config.mapGetInt(
"Height", &height))
 
  884     resize(width, height);
 
  887   QString main_window_config;
 
  888   if (
config.mapGetString(
"QMainWindow State", &main_window_config))
 
  890     restoreState(QByteArray::fromHex(qPrintable(main_window_config)));
 
  894   QList<PanelDockWidget*> dock_widgets = findChildren<PanelDockWidget*>();
 
  896   for (QList<PanelDockWidget*>::iterator it = dock_widgets.begin(); it != dock_widgets.end(); it++)
 
  898     Config itConfig = 
config.mapGetChild((*it)->windowTitle());
 
  902       (*it)->load(itConfig);
 
  907   config.mapGetBool(
"Hide Left Dock", &b);
 
  910   config.mapGetBool(
"Hide Right Dock", &b);
 
  917   int tool_button_style;
 
  918   if (
config.mapGetInt(
"toolButtonStyle", &tool_button_style))
 
  920     toolbar_->setToolButtonStyle(
static_cast<Qt::ToolButtonStyle
>(tool_button_style));
 
  926   config.mapSetValue(
"toolButtonStyle", 
static_cast<int>(
toolbar_->toolButtonStyle()));
 
  931   config.mapSetValue(
"X", x());
 
  932   config.mapSetValue(
"Y", y());
 
  933   config.mapSetValue(
"Width", width());
 
  934   config.mapSetValue(
"Height", height());
 
  936   QByteArray window_state = saveState().toHex();
 
  937   config.mapSetValue(
"QMainWindow State", window_state.constData());
 
  943   QList<PanelDockWidget*> dock_widgets = findChildren<PanelDockWidget*>();
 
  945   for (QList<PanelDockWidget*>::iterator it = dock_widgets.begin(); it != dock_widgets.end(); it++)
 
  947     (*it)->save(
config.mapMakeChild((*it)->windowTitle()));
 
  959   int num_custom_panels = 
config.listLength();
 
  960   for (
int i = 0; i < num_custom_panels; i++)
 
  964     QString class_id, name;
 
  973         connect(dock, &QDockWidget::dockLocationChanged, 
this,
 
  974                 &VisualizationFrame::VisualizationFrame::onDockPanelChange);
 
  975         Panel* panel = qobject_cast<Panel*>(dock->widget());
 
  978           panel->
load(panel_config);
 
 1017   if (isWindowModified() && 
preferences_->prompt_save_on_exit)
 
 1019     QMessageBox box(
this);
 
 1020     box.setText(
"There are unsaved changes.");
 
 1021     box.setInformativeText(QString::fromStdString(
"Save changes to " + 
display_config_file_ + 
"?"));
 
 1022     box.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
 
 1023     box.setDefaultButton(QMessageBox::Save);
 
 1025     int result = box.exec();
 
 1029     case QMessageBox::Save:
 
 1036         QMessageBox box(
this);
 
 1037         box.setWindowTitle(
"Failed to save.");
 
 1039         box.setInformativeText(
 
 1041         box.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
 
 1042         box.setDefaultButton(QMessageBox::Save);
 
 1043         int result = box.exec();
 
 1046         case QMessageBox::Save:
 
 1049         case QMessageBox::Discard:
 
 1055     case QMessageBox::Discard:
 
 1070   QString filename = QFileDialog::getOpenFileName(
this, 
"Choose a file to open",
 
 1075   if (!filename.isEmpty())
 
 1077     std::string path = filename.toStdString();
 
 1079     if (!fs::exists(path))
 
 1081       QString message = filename + 
" does not exist!";
 
 1082       QMessageBox::critical(
this, 
"Config file does not exist", message);
 
 1102     QMessageBox box(
this);
 
 1103     box.setWindowTitle(
"Failed to save.");
 
 1105     box.setInformativeText(
 
 1107     box.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
 
 1108     box.setDefaultButton(QMessageBox::Save);
 
 1109     if (box.exec() == QMessageBox::Save)
 
 1120   QString q_filename = QFileDialog::getSaveFileName(
this, 
"Choose a file to save to",
 
 1125   if (!q_filename.isEmpty())
 
 1127     std::string filename = q_filename.toStdString();
 
 1128     fs::path path(filename);
 
 1150           &VisualizationFrame::VisualizationFrame::setImageSaveDirectory);
 
 1156   QAction* action = 
dynamic_cast<QAction*
>(sender());
 
 1159     std::string path = action->data().toString().toStdString();
 
 1162       if (!fs::exists(path))
 
 1164         QString message = QString::fromStdString(path) + 
" does not exist!";
 
 1165         QMessageBox::critical(
this, 
"Config file does not exist", message);
 
 1177   action->setIcon(tool->
getIcon());
 
 1178   action->setIconText(tool->
getName());
 
 1179   action->setCheckable(
true);
 
 1187                    &VisualizationFrame::VisualizationFrame::onToolNameChanged);
 
 1198   it->second->setIconText(name);
 
 1213   QString name = remove_tool_menu_action->text();
 
 1228       static_cast<Qt::ToolButtonStyle
>(button_style_tool_menu_action->data().toInt()));
 
 1241   QString tool_name = tool->
getName();
 
 1243   for (
int i = 0; i < remove_tool_actions.size(); i++)
 
 1245     QAction* removal_action = remove_tool_actions.at(i);
 
 1246     if (removal_action->text() == tool_name)
 
 1257   action->setIcon(tool->
getIcon());
 
 1258   action->setIconText(tool->
getName());
 
 1266     action->setChecked(
true);
 
 1276     connect(dock, &QObject::destroyed, 
this, &VisualizationFrame::VisualizationFrame::onHelpDestroyed);
 
 1295   QDesktopServices::openUrl(QUrl(
"http://wiki.ros.org/rviz"));
 
 1300   QString about_text = QString(
"This is RViz version %1 (%2).\n" 
 1302                                "Compiled against Qt version %3." 
 1304                                "Compiled against OGRE version %4.%5.%6%7 (%8).")
 
 1307                            .arg(QT_VERSION_STR)
 
 1308                            .arg(OGRE_VERSION_MAJOR)
 
 1309                            .arg(OGRE_VERSION_MINOR)
 
 1310                            .arg(OGRE_VERSION_PATCH)
 
 1311                            .arg(OGRE_VERSION_SUFFIX)
 
 1312                            .arg(OGRE_VERSION_NAME);
 
 1314   QMessageBox::about(QApplication::activeWindow(), 
"About", about_text);
 
 1319   QList<QTabBar*> tab_bars = findChildren<QTabBar*>(QString(), Qt::FindDirectChildrenOnly);
 
 1320   for (QList<QTabBar*>::iterator it = tab_bars.begin(); it != tab_bars.end(); it++)
 
 1322     (*it)->setElideMode(Qt::ElideNone);
 
 1338       record.delete_action->deleteLater();
 
 1355   if (QAction* action = qobject_cast<QAction*>(sender()))
 
 1370   Qt::WindowStates state = windowState();
 
 1371   if (full_screen == state.testFlag(Qt::WindowFullScreen))
 
 1378   menuBar()->setVisible(!full_screen);
 
 1380   statusBar()->setVisible(!full_screen);
 
 1384     setWindowState(state | Qt::WindowFullScreen);
 
 1386     setWindowState(state & ~Qt::WindowFullScreen);
 
 1396                                                 const QString& class_id,
 
 1397                                                 Qt::DockWidgetArea area,
 
 1408           &VisualizationFrame::VisualizationFrame::setDisplayConfigModified);
 
 1411   record.
dock = 
addPane(name, panel, area, floating);
 
 1412   record.
panel = panel;
 
 1415   connect(record.
dock, &QObject::destroyed, 
this,
 
 1416           &VisualizationFrame::VisualizationFrame::onPanelDeleted);
 
 1432   addDockWidget(area, dock);
 
 1435   dock->setFloating(floating);
 
 1436   dock->setObjectName(name); 
 
 1439   connect(dock, &PanelDockWidget::visibilityChanged, 
this,
 
 1441   connect(
this, &VisualizationFrame::VisualizationFrame::fullScreenChange, dock,
 
 1444   QAction* toggle_action = dock->toggleViewAction();
 
 1447   connect(toggle_action, &QAction::triggered, 
this,
 
 1448           &VisualizationFrame::VisualizationFrame::setDisplayConfigModified);
 
 1450           &VisualizationFrame::VisualizationFrame::setDisplayConfigModified);