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);