00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <QtGui>
00025 #include <QDir>
00026 #include <QPixmap>
00027 #include <QFont>
00028 #include <QIcon>
00029 #include <QKeySequence>
00030 #include "qt_gui.h"
00031 #include <limits>
00032 #include "glviewer.h"
00033 #include <QList>
00034 #include <QComboBox>
00035
00037 Graphical_UI::Graphical_UI() : filename("quicksave.pcd"), glviewer(NULL)
00038 {
00039 setup();
00040 setWindowTitle(tr("RGBDSLAM"));
00041 }
00043 Graphical_UI::Graphical_UI(QString title) : filename("quicksave.pcd"), glviewer(NULL)
00044 {
00045 setup();
00046 setWindowTitle(title);
00047 }
00048
00050 static void formatImageLabel(QLabel* label){
00051 label->setWordWrap(true);
00052 label->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
00053 label->setMinimumSize(4,100);
00054 label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
00055
00056 if(ParameterServer::instance()->get<bool>("scalable_2d_display")) {
00057 label->setScaledContents(true);
00058 }
00059 }
00060
00061 void Graphical_UI::initTexts(){
00062 infoText = new QString(tr(
00063 "<p><b>RGBDSLAMv2</b> uses visual features to identify corresponding 3D locations "
00064 "in RGB-D data. The correspondences are used to reconstruct the camera motion. "
00065 "The SLAM-backend g2o is used to integrate the transformations between"
00066 "the RGBD-images and compute a globally consistent 6D trajectory.</p>"
00067 "<p></p>"));
00068 licenseText = new QString(tr(
00069 "<p>RGBDSLAMv2 is free software: you can redistribute it and/or modify"
00070 "it under the terms of the GNU General Public License as published by"
00071 "the Free Software Foundation, either version 3 of the License, or"
00072 "(at your option) any later version.</p>"
00073 "<p>RGBDSLAMv2 is distributed in the hope that it will be useful,"
00074 "but WITHOUT ANY WARRANTY; without even the implied warranty of"
00075 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
00076 "GNU General Public License for more details.</p>"
00077 "<p>You should have received a copy of the GNU General Public License"
00078 "along with RGBDSLAMv2. If not, refer to <a href=\"http://www.gnu.org/licenses/\">http://www.gnu.org/licenses</a>.</p>"));
00079 mouseHelpText = new QString(tr(
00080 "<p><b>3D Viewer Mouse Commands:</b>"
00081 "<ul><li><i>Left button:</i> Rotate view around x/y.</li>"
00082 " <li><i>Ctrl + left button:</i> rotate view around x/z.</li>"
00083 " <li><i>Shift + left button:</i> Translate view.</li>"
00084 " <li><i>Wheel:</i> zoom view.</li>"
00085 " <li><i>Ctrl + Wheel:</i> change 3D point size.</li>"
00086 " <li><i>Double click (on background):</i> reset camera position to latest pose.</li>"
00087 " <li><i>Ctrl + Double click (on background):</i> reset camera position to first pose (only works if follow mode is off).</li>"
00088 " <li><i>Double click on object:</i> set pivot to clicked point (only works if follow mode is off).</li>"
00089 "<ul></p>"));
00090 }
00091
00092 void Graphical_UI::setup(){
00093 initTexts();
00094
00095 ParameterServer* ps = ParameterServer::instance();
00096
00097
00098 std::string visual_topic = ps->get<std::string>("topic_image_mono");
00099 QString vl("Waiting for visual image on topic<br/><i>\""); vl += visual_topic.c_str(); vl += "\"</i>";
00100 visual_image_label = new QLabel(vl);
00101 formatImageLabel(visual_image_label);
00102
00103 std::string depth_topic = ps->get<std::string>("topic_image_depth");
00104 QString dl("Waiting for depth image on topic<br/><i>\""); dl += depth_topic.c_str();
00105 dl += "\"</i><br/>";
00106
00107 depth_image_label = new QLabel(dl);
00108 formatImageLabel(depth_image_label);
00109
00110 feature_image_label = new QLabel(tr("<i>Waiting for feature image...</i>"));
00111 formatImageLabel(feature_image_label);
00112
00113 feature_flow_image_label = new QLabel(*mouseHelpText);
00114 formatImageLabel(feature_flow_image_label);
00115
00116 QSplitter* hsplitter = new QSplitter(Qt::Horizontal);
00117 hsplitter->addWidget(visual_image_label);
00118 hsplitter->addWidget(depth_image_label);
00119 hsplitter->addWidget(feature_image_label);
00120 hsplitter->addWidget(feature_flow_image_label);
00121
00122
00123
00124 vsplitter = new QSplitter(Qt::Vertical);
00125 setCentralWidget(vsplitter);
00126
00127
00128 if(ps->get<bool>("use_glwidget")) {
00129 glviewer = new GLViewer(this);
00130 vsplitter->addWidget(glviewer);
00131 }
00132
00133 vsplitter->addWidget(hsplitter);
00134
00135 createMenus();
00136
00137 setupStatusbar();
00138
00139 setMinimumSize(600, 290);
00140 resize(1000, 700);
00141 }
00142
00143 void Graphical_UI::setupStatusbar(){
00144 tmpLabel = new QLabel();
00145 statusBar()->insertWidget(0,tmpLabel, 0);
00146 QString message = tr("Ready for RGB-D SLAM");
00147 statusBar()->showMessage(message);
00148 infoLabel2 = new QLabel(tr("Empty Map"));
00149 infoLabel2->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
00150 infoLabel2->setAlignment(Qt::AlignRight);
00151 statusBar()->addPermanentWidget(infoLabel2, 0);
00152
00153 if(ParameterServer::instance()->get<bool>("start_paused")){
00154 infoLabel = new QLabel(tr("<i>Press Enter or Space to Start</i>"));
00155 } else {
00156 infoLabel = new QLabel(tr("<i>No data yet.</i>"));
00157 }
00158 infoLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
00159 infoLabel->setAlignment(Qt::AlignRight);
00160 statusBar()->addPermanentWidget(infoLabel, 0);
00161
00162 }
00163
00164
00165
00166
00167 void Graphical_UI::setLabelToImage(QLabel* label, QImage image){
00168 if(label->isVisible()){
00169 label->setMaximumHeight(image.size().height());
00170 label->setMinimumHeight(image.size().height()/2);
00171
00172 label->setAlignment(Qt::AlignCenter);
00173 label->setPixmap(QPixmap::fromImage(image));
00174 label->repaint();
00175 }
00176 }
00177
00178 void Graphical_UI::setFeatureImage(QImage qimage){
00179 setLabelToImage(feature_image_label, qimage);
00180 }
00181
00182 void Graphical_UI::setFeatureFlowImage(QImage qimage){
00183 setLabelToImage(feature_flow_image_label, qimage);
00184 }
00185
00186 void Graphical_UI::setVisualImage(QImage qimage){
00187 setLabelToImage(visual_image_label, qimage);
00188 }
00189
00190 void Graphical_UI::setDepthImage(QImage qimage){
00191 setLabelToImage(depth_image_label, qimage);
00192 }
00193
00194 void Graphical_UI::reloadConfig() {
00195 ParameterServer::instance()->getValues();
00196 QString message = tr("Configuration Reloaded");
00197 statusBar()->showMessage(message);
00198 }
00199
00200 void Graphical_UI::resetCmd() {
00201 Q_EMIT reset();
00202 if(ParameterServer::instance()->get<bool>("use_glwidget")) glviewer->reset();
00203 QString message = tr("Graph Reset");
00204 statusBar()->showMessage(message);
00205 infoLabel->setText("A fresh new graph is waiting");
00206 }
00207
00208 void Graphical_UI::setStatus(QString message){
00209 statusBar()->showMessage(message);
00210 }
00211 void Graphical_UI::setInfo2(QString message){
00212 infoLabel2->setText(message);
00213 infoLabel2->repaint();
00214 }
00215 void Graphical_UI::setInfo(QString message){
00216 infoLabel->setText(message);
00217 infoLabel->repaint();
00218 }
00219
00220 void Graphical_UI::saveG2OGraphDialog() {
00221 QString graph_filename = QFileDialog::getSaveFileName(this, "Save G2O Graph to File", "graph.g2o", tr("G2O (*.g2o)"));
00222 QString message;
00223 if(graph_filename.isEmpty()){
00224 message = tr("Filename empty. Aborting save");
00225 } else {
00226 Q_EMIT saveG2OGraph(graph_filename);
00227 message = tr("Saving Graph");
00228 }
00229 statusBar()->showMessage(message);
00230
00231 }
00232
00233 void Graphical_UI::quickSaveAll() {
00234 Q_EMIT saveAllClouds(filename);
00235 QString message = tr("Saving Whole Model to ");
00236 message.append(QDir::currentPath());
00237 message.append(filename);
00238 statusBar()->showMessage(message);
00239
00240 }
00241
00242 void Graphical_UI::openBagFileDialog() {
00243 QString filename = QFileDialog::getOpenFileName(this, "Open Bag File", "", tr("BAG (*.bag)"));
00244 QString message;
00245 if(filename.isEmpty()){
00246 message = tr("Filename empty. Aborting.");
00247 } else {
00248 message = tr("Opening Bagfile ");
00249 message += filename;
00250 Q_EMIT openBagFile(filename);
00251 }
00252 statusBar()->showMessage(message);
00253 }
00254
00255 void Graphical_UI::openPCDFilesDialog() {
00256 QStringList filenamelist = QFileDialog::getOpenFileNames(this, "Open PCD Files", "", tr("PCD (*.pcd)"));
00257 QString message;
00258 if(filenamelist.isEmpty()){
00259 message = tr("Empty file list. Aborting");
00260 } else {
00261 for(int i=0; i < filenamelist.size(); i++){
00262 std::cout << qPrintable(filenamelist.at(i)) << std::endl;
00263 }
00264 Q_EMIT openPCDFiles(filenamelist);
00265 message = tr("Opening PCD Files");
00266 }
00267 statusBar()->showMessage(message);
00268 }
00269 void Graphical_UI::saveFeatures() {
00270 filename = QFileDialog::getSaveFileName(this, "Save Features to File", filename, tr("YAML (*.yml);;XML (*.xml)"));
00271 QString message;
00272 if(filename.isEmpty()){
00273 message = tr("Filename empty. Aborting.");
00274 } else {
00275 message = tr("Saving Features to ");
00276 message += filename;
00277 Q_EMIT saveAllFeatures(filename);
00278 }
00279 statusBar()->showMessage(message);
00280 }
00281
00282 void Graphical_UI::computeOctomap() {
00283 Q_EMIT computeOctomapSig(filename);
00284 }
00285
00286 void Graphical_UI::saveOctomap() {
00287 filename = QFileDialog::getSaveFileName(this, "Create and Save Octomap to File", filename, tr("OcTree (*.ot)"));
00288 QString message;
00289 if(filename.isEmpty()){
00290 message = tr("Filename empty. Aborting.");
00291 } else {
00292 Q_EMIT saveOctomapSig(filename);
00293 QString message = tr("Creating Octomap");
00294 }
00295 statusBar()->showMessage(message);
00296 }
00297
00298 void Graphical_UI::saveAll() {
00299 filename = QFileDialog::getSaveFileName(this, "Save Point CLoud to File", filename, tr("PCD (*.pcd);;PLY (*ply)"));
00300 Q_EMIT saveAllClouds(filename);
00301 QString message = tr("Saving Whole Model");
00302 statusBar()->showMessage(message);
00303 }
00304
00305 void Graphical_UI::showEdgeErrors() {
00306 QString myfilename = QFileDialog::getSaveFileName(this, "Save Current Trajectory Estimate", "trajectory", tr("All Files (*.*)"));
00307 Q_EMIT printEdgeErrors(myfilename);
00308 QString message = tr("Triggering Edge Printing");
00309 statusBar()->showMessage(message);
00310 }
00311
00312 void Graphical_UI::optimizeGraphTrig() {
00313 Q_EMIT optimizeGraph();
00314 QString message = tr("Triggering Optimizer");
00315 statusBar()->showMessage(message);
00316 }
00317
00318 void Graphical_UI::saveVectorGraphic() {
00319 QMessageBox::warning(this, tr("Don't render to pdf while point clouds are shown"), tr("This is meant for rendereing the pose graph. Rendering clouds to pdf will generate huge files!"));
00320 QString myfilename = QFileDialog::getSaveFileName(this, "Save Current 3D Display to Vector Graphic", "pose_graph.pdf", tr("All Files (*.*)"));
00321 if(!myfilename.size() == 0){
00322 glviewer->drawToPS(myfilename);
00323 QString message = tr("Drawing current Display");
00324 statusBar()->showMessage(message);
00325 } else {
00326 QString message = tr("Empty Filename");
00327 }
00328 }
00329 void Graphical_UI::toggleScreencast(bool on) {
00330 if(on){
00331 QString myfilename = QFileDialog::getSaveFileName(this, "Path Prefix for Screencast Frames", "rgbdslam_frame_", tr("All Files (*.*)"));
00332 if(myfilename.size() > 0){
00333 ParameterServer::instance()->set("screencast_path_prefix", std::string(qPrintable(myfilename)));
00334 QString message = tr("Saving screencast frames.");
00335 statusBar()->showMessage(message);
00336 }
00337 }
00338 else {
00339 ParameterServer::instance()->set("screencast_path_prefix", std::string());
00340 }
00341 }
00342 void Graphical_UI::saveTrajectoryDialog() {
00343 QString myfilename = QFileDialog::getSaveFileName(this, "Save Current Trajectory Estimate", "trajectory", tr("All Files (*.*)"));
00344 Q_EMIT saveTrajectory(myfilename);
00345 QString message = tr("Saving current trajectory estimate (and possibly ground truth).");
00346 statusBar()->showMessage(message);
00347 }
00348 void Graphical_UI::saveIndividual() {
00349 QString tmpfilename(filename);
00350 tmpfilename.remove(".pcd", Qt::CaseInsensitive);
00351 tmpfilename.remove(".ply", Qt::CaseInsensitive);
00352
00353 tmpfilename = QFileDialog::getSaveFileName(this, "Save point cloud to one file per node", tmpfilename, tr("PCD (*.pcd)"));
00354 tmpfilename.remove(".pcd", Qt::CaseInsensitive);
00355 Q_EMIT saveIndividualClouds(tmpfilename);
00356 QString message = tr("Saving Model Node-Wise");
00357 statusBar()->showMessage(message);
00358
00359 }
00360
00361 void Graphical_UI::saveBagDialog() {
00362 QString filename = QFileDialog::getSaveFileName(this, "Save Clouds to Bag File", "", tr("BAG (*.bag)"));
00363 Q_EMIT saveBagfile(filename);
00364 QString message = tr("Writing Whole Model to Bagfile");
00365 statusBar()->showMessage(message);
00366 }
00367 void Graphical_UI::sendAll() {
00368 Q_EMIT sendAllClouds();
00369 QString message = tr("Sending Whole Model");
00370 statusBar()->showMessage(message);
00371 }
00372
00373 void Graphical_UI::setRotationGrid() {
00374 bool ok;
00375 double value = QInputDialog::getDouble(this, tr("Set Rotation Stepping in Degree"),
00376 tr("Enter Stepping:"), 1.00, -10000000, 10000000, 2, &ok);
00377 if(ok){ glviewer->setRotationGrid(value); }
00378 }
00379 void Graphical_UI::setStereoShift() {
00380 bool ok;
00381 double value = QInputDialog::getDouble(this, tr("Set Stereo Camera shift"),
00382 tr("Enter Shift:"), 0.10, -10000000, 10000000, 2, &ok);
00383 if(ok){ glviewer->setStereoShift(value); }
00384 }
00385
00386 void Graphical_UI::setParam(QString text){
00387 std::map<std::string, boost::any>& config = ParameterServer::instance()->getConfigData();
00388 std::map<std::string, boost::any>::const_iterator itr;
00389 for (itr = config.begin(); itr != config.end(); ++itr) {
00390 if(itr->first == qPrintable(text)){
00391 bool ok;
00392 if (itr->second.type() == typeid(std::string)) {
00393 QString prev_val = boost::any_cast<std::string>(itr->second).c_str();
00394 QString new_val = QInputDialog::getText(this, text, tr("New Value:"),
00395 QLineEdit::Normal, prev_val, &ok);
00396 if (ok){
00397 ParameterServer::instance()->set(itr->first, std::string(qPrintable(new_val)));
00398 }
00399 }
00400 else if (itr->second.type() == typeid(int)) {
00401 int prev_val = boost::any_cast<int>(itr->second);
00402 int new_val = QInputDialog::getInt(this, text, tr("New Value:"),
00403 prev_val,
00404 std::numeric_limits<int>::min(),
00405 std::numeric_limits<int>::max(), 1, &ok);
00406 if (ok){
00407 ParameterServer::instance()->set(itr->first, new_val);
00408 }
00409 }
00410 else if (itr->second.type() == typeid(double)) {
00411 double prev_val = boost::any_cast<double>(itr->second);
00412 double new_val = QInputDialog::getDouble(this, text, tr("New Value:"),
00413 prev_val,
00414 std::numeric_limits<double>::min(),
00415 std::numeric_limits<double>::max(), 3, &ok);
00416 if (ok){
00417 ParameterServer::instance()->set(itr->first, new_val);
00418 }
00419 }
00420 else if (itr->second.type() == typeid(bool)) {
00421 QStringList truefalse;
00422 truefalse.append("False");
00423 truefalse.append("True");
00424
00425 int prev_val = boost::any_cast<bool>(itr->second);
00426 QString new_val = QInputDialog::getItem(this, text, tr("New Value:"),
00427 truefalse, prev_val, false, &ok);
00428 if (ok){
00429 if(new_val == "False")
00430 ParameterServer::instance()->set(itr->first, false);
00431 else
00432 ParameterServer::instance()->set(itr->first, true);
00433 }
00434 }
00435 }
00436 }
00437 }
00438
00439
00440 void Graphical_UI::setParam() {
00441 std::map<std::string, boost::any>& config = ParameterServer::instance()->getConfigData();
00442 std::map<std::string, boost::any>::const_iterator itr;
00443 QStringList list;
00444 for (itr = config.begin(); itr != config.end(); ++itr) {
00445 QString name(itr->first.c_str());
00446
00447 list.append(name);
00448 }
00449 bool ok = false;
00450 QString name = QInputDialog::getItem(this, tr("Select Parameter to Change"),
00451 tr("Note: Changing parameters during runtime is not honored for all parameters.\n"
00452 "In particular changing parameters used during the initial setup "
00453 "(e.g. topic names) will have no effect.\n\n"
00454 "Options should be set via launchfiles!\n\n"
00455 "This functionality is mostly for quickly trying out the effect of "
00456 "individual parameters.\n\nParameter Name"),
00457 list, 0, false, &ok);
00458 if(ok) setParam(name);
00459 }
00460 void Graphical_UI::toggleFullscreen(bool mode){
00461 this->menuBar()->setVisible(!mode);
00462 this->gridlayout->setMargin(mode?0:5);
00463 this->statusBar()->setVisible(!mode);
00464 if(mode){
00465 this->showFullScreen();
00466 } else {
00467 this->showNormal();
00468 }
00469 }
00470 void Graphical_UI::pruneEdgesWithHighError(){
00471 bool ok;
00472 float value = QInputDialog::getDouble(this, tr("Set Max Edge Error"),
00473 tr("No Text"), 1.00, -10000000, 10000000, 4, &ok);
00474 if(ok){
00475 Q_EMIT pruneEdgesWithErrorAbove(value);
00476 }
00477 }
00478 void Graphical_UI::sendFinished() {
00479
00480 QString message = tr("Finished Sending");
00481 statusBar()->showMessage(message);
00482 }
00483
00484 void Graphical_UI::getOneFrameCmd() {
00485 Q_EMIT getOneFrame();
00486 QString message = tr("Getting a single frame");
00487 statusBar()->showMessage(message);
00488 }
00489 void Graphical_UI::deleteLastFrameCmd() {
00490 Q_EMIT deleteLastFrame();
00491 QString message = tr("Deleting the last node from the graph");
00492 statusBar()->showMessage(message);
00493 }
00494
00495 void Graphical_UI::toggleMappingPriv(bool mapping_on) {
00496 Q_EMIT toggleMapping(mapping_on);
00497 }
00498
00499 void Graphical_UI::triggerCloudFiltering() {
00500 Q_EMIT occupancyFilterClouds();
00501 }
00502 void Graphical_UI::setOctoMapResolution() {
00503 this->setParam("octomap_resolution");
00504 }
00505 void Graphical_UI::toggleOnlineVoxelMapping(bool online) {
00506 ParameterServer::instance()->set("octomap_online_creation", online);
00507 }
00508 void Graphical_UI::toggleCloudStorage(bool storage) {
00509 ParameterServer::instance()->set("store_pointclouds", storage);
00510 }
00511 void Graphical_UI::toggleLandmarkOptimization(bool landmarks) {
00512 ParameterServer::instance()->set("optimize_landmarks", landmarks);
00513 }
00514
00515 void Graphical_UI::pause(bool pause_on) {
00516 Q_EMIT togglePause();
00517 if(pause_on) {
00518 QString message = tr("Processing.");
00519 statusBar()->showMessage(message);
00520 } else {
00521 QString message = tr("Stopped processing.");
00522 statusBar()->showMessage(message);
00523 }
00524 }
00525
00526 void Graphical_UI::help() {
00527 QMessageBox::about(this, tr("Help Menu"), *mouseHelpText );
00528 }
00529 void Graphical_UI::about() {
00530 QMessageBox::about(this, tr("About RGBDSLAM"), *infoText + *licenseText);
00531 }
00532
00533 void Graphical_UI::set2DStream(bool is_on) {
00534 if(is_on){
00535 visual_image_label->show();
00536 depth_image_label->show();
00537 feature_flow_image_label->show();
00538 feature_image_label->show();
00539 QList<int> list;
00540 list.append(1);
00541 list.append(1);
00542 vsplitter->setSizes(list);
00543 } else {
00544 visual_image_label->hide();
00545 depth_image_label->hide();
00546 feature_flow_image_label->hide();
00547 feature_image_label->hide();
00548 QList<int> list;
00549 list.append(1);
00550 list.append(0);
00551 vsplitter->setSizes(list);
00552 }
00553 }
00554
00555
00556
00557
00558
00559
00560
00561
00562 QAction* Graphical_UI::newAction(QMenu* menu, const char* title, const char* statustip, QIcon icon){
00563 QAction *new_action = new QAction(tr(title), this);
00564 new_action->setStatusTip(tr(statustip));
00565 new_action->setIcon(icon);
00566 this->addAction(new_action);
00567 menu->addAction(new_action);
00568 return new_action;
00569 }
00570
00571 QAction* Graphical_UI::newMenuItem(QMenu* menu, const char* title, QObject* receiver, const char* callback, const char* statustip, const char* key_seq_str, QIcon icon){
00572 QAction *new_action = newAction(menu, title, statustip, icon);
00573 new_action->setShortcut(QString(key_seq_str));
00574 connect(new_action, SIGNAL(triggered()), receiver, callback);
00575 return new_action;
00576 }
00577 QAction* Graphical_UI::newMenuItem(QMenu* menu, const char* title, const char* callback_method_of_this_class, const char* statustip, QKeySequence::StandardKey std_key, QIcon icon){
00578 QAction *new_action = newAction(menu, title, statustip, icon);
00579 new_action->setShortcuts(std_key);
00580 connect(new_action, SIGNAL(triggered()), this, callback_method_of_this_class);
00581 return new_action;
00582 }
00583 QAction* Graphical_UI::newMenuItem(QMenu* menu, const char* title, const char* callback_method_of_this_class, const char* statustip, const char* key_seq_str, QIcon icon){
00584 QAction *new_action = newAction(menu, title, statustip, icon);
00585 new_action->setShortcut(QString(key_seq_str));
00586 connect(new_action, SIGNAL(triggered()), this, callback_method_of_this_class);
00587 return new_action;
00588 }
00589 QAction* Graphical_UI::newMenuItem(QMenu* menu, const char* title, QObject* receiver, const char* callback, const char* statustip, bool checked, const char* key_seq_str,QIcon icon){
00590 QAction *new_action = newAction(menu, title, statustip, icon);
00591 new_action->setShortcut(QString(key_seq_str));
00592 new_action->setCheckable(true);
00593 new_action->setChecked(checked);
00594 connect(new_action, SIGNAL(toggled(bool)), receiver, callback);
00595 return new_action;
00596 }
00597
00598 void Graphical_UI::createLoadMenu() {
00599 QMenu* lm = menuBar()->addMenu(tr("&Load"));
00600 newMenuItem(lm,
00601 "&Open PCD files",
00602 SLOT(openPCDFilesDialog()),
00603 "Open one or more pcd files to process",
00604 QKeySequence::Open,
00605 QIcon::fromTheme("document-open"));
00606
00607 newMenuItem(lm,
00608 "Open ROS &bag file",
00609 SLOT(openBagFileDialog()),
00610 "Open a bag file to process",
00611 "Ctrl+B",
00612 QIcon::fromTheme("document-open"));
00613 }
00614
00615 QAction* Graphical_UI::createSaveMenu() {
00616 QMenu* sm = menuBar()->addMenu(tr("&Save"));
00617 newMenuItem(sm,
00618 "&Save",
00619 SLOT(quickSaveAll()),
00620 "Save all stored point clouds with common coordinate frame to a pcd file",
00621 QKeySequence::Save,
00622 QIcon::fromTheme("document-save"));
00623
00624
00625 newMenuItem(sm,
00626 "Save &Feature Map...",
00627 SLOT(saveFeatures()),
00628 "Save all feature positions and descriptions in a common coordinate frame to a yaml or xml file",
00629 "Ctrl+F",
00630 QIcon::fromTheme("document-save"));
00631
00632 QAction* oa = newMenuItem(sm, "Save Octomap...",
00633 SLOT(saveOctomap()),
00634 "Save computed OctoMap",
00635 "",
00636 QIcon::fromTheme("document-save-as"));
00637
00638 newMenuItem(sm, "&Save as Point Cloud ...",
00639 SLOT(saveAll()),
00640 "Save all stored point clouds with common coordinate frame",
00641 QKeySequence::SaveAs,
00642 QIcon::fromTheme("document-save-as"));
00643
00644 newMenuItem(sm, "&Save Point Cloud Node-Wise...",
00645 SLOT(saveIndividual()),
00646 "Save stored point clouds in individual files",
00647 "Ctrl+N",
00648 QIcon::fromTheme("document-save-all"));
00649
00650 newMenuItem(sm, "Save &G2O Graph...",
00651 SLOT(saveG2OGraphDialog()),
00652 "Save G2O graph (e.g. for use with the g2o viewer or external optimization)",
00653 "",
00654 QIcon::fromTheme("document-save"));
00655
00656 newMenuItem(sm, "Save Clouds to &Bag...",
00657 SLOT(saveBagDialog()),
00658 "Save clouds and transforms to bagfile",
00659 "Ctrl+Shift+B");
00660
00661 newMenuItem(sm, "Save Trajectory &Estimate...",
00662 SLOT(saveTrajectoryDialog()),
00663 "Save trajectory estimate (and ground truth trajectory if available) for external evaluation.",
00664 "Ctrl+E");
00665
00666 sm->addSeparator();
00667 newMenuItem(sm, "&Send Model",
00668 SLOT(sendAll()),
00669 "Send out all stored point clouds with corrected transform",
00670 "Ctrl+M",
00671 QIcon::fromTheme("document-send"));
00672
00673 sm->addSeparator();
00674
00675 newMenuItem(sm, "Save &3D as PDF File...",
00676 SLOT(saveVectorGraphic()),
00677 "Write 3D Scene to a PDF File. Warning: Meant for Pose Graphs not for the clouds or octomaps!",
00678 "",
00679 QIcon::fromTheme("application-pdf"));
00680
00681 newMenuItem(sm, "Save Input &Images to Files...",
00682 SLOT(saveAllImages()),
00683 "Write All images shown in the gui to appropriate files",
00684 "",
00685 QIcon::fromTheme("image-x-generic"));
00686
00687 newMenuItem(sm, "Capture Screencast...",
00688 this,
00689 SLOT(toggleScreencast(bool)),
00690 "Dump Screen as Video",
00691 !ParameterServer::instance()->get<std::string>("screencast_path_prefix").empty());
00692
00693 return oa;
00694 }
00695 void Graphical_UI::createProcessingMenu() {
00696 QMenu* pm = menuBar()->addMenu(tr("&Processing"));
00697 newMenuItem(pm, "&Reset",
00698 SLOT(resetCmd()),
00699 "Reset the graph, clear all data collected",
00700 "Ctrl+R",
00701 QIcon::fromTheme("edit-delete"));
00702
00703 newMenuItem(pm, "&Process",
00704 this,
00705 SLOT(pause(bool)),
00706 "Start/stop processing of frames",
00707 !ParameterServer::instance()->get<bool>("start_paused"),
00708 " ",
00709 QIcon::fromTheme("media-playback-start"));
00710
00711 newMenuItem(pm, "Capture One& Frame",
00712 SLOT(getOneFrameCmd()),
00713 "Process one frame only",
00714 QKeySequence::InsertParagraphSeparator);
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724 pm->addSeparator();
00725
00726 newMenuItem(pm, "Clear Cloud Storage",
00727 SIGNAL(clearClouds()),
00728 "Remove Point Clouds from Memory",
00729 "",
00730 QIcon::fromTheme("edit-delete"));
00731
00732 newMenuItem(pm, "Optimize Trajectory &Estimate",
00733 SLOT(optimizeGraphTrig()),
00734 "Compute optimized pose graph with g2o",
00735 "O");
00736
00737 pm->addSeparator();
00738
00739 newMenuItem(pm, "E&xit",
00740 SLOT(close()),
00741 "Exit the application",
00742 "Ctrl+Q",
00743 QIcon::fromTheme("application-exit"));
00744 }
00745
00746 void Graphical_UI::createMenus() {
00747
00748
00749 createProcessingMenu();
00750 createLoadMenu();
00751
00752 QAction* octoMapAction = createSaveMenu();
00753
00754 QMenu *om = menuBar()->addMenu(tr("&OctoMap"));
00755 newMenuItem(om, "Point Cloud Occupancy Filter",
00756 SLOT(triggerCloudFiltering()),
00757 "Remove points from the cloud that fall into unoccupied voxels of the OctoMap");
00758
00759 newMenuItem(om, "Octomap Resolution",
00760 SLOT(setOctoMapResolution()),
00761 "Change the octomap resolution. Clears previously created maps on next update.");
00762
00763 newMenuItem(om, "&Online OctoMapping",
00764 this,
00765 SLOT(toggleOnlineVoxelMapping(bool)),
00766 "Toggle Online/Offline OctoMapping. Make sure to set a low octomap_resolution and/or high cloud_creation_skip_step for online mapping",
00767 ParameterServer::instance()->get<bool>("octomap_online_creation"));
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796 if(ParameterServer::instance()->get<bool>("use_glwidget"))
00797 {
00798 QMenu* v3 = menuBar()->addMenu(tr("&3D View"));
00799
00800 newMenuItem(v3, "&Clear 3D Display",
00801 glviewer,
00802 SLOT(reset()),
00803 "Clears 3D viewer cloud data. Point Clouds are still retained, e.g. for mapping purposes.",
00804 "",
00805 QIcon::fromTheme("edit-delete"));
00806
00807
00808 newMenuItem(v3, "Toggle &3D Display",
00809 glviewer,
00810 SLOT(setVisible(bool)),
00811 "Turn off the OpenGL Display",
00812 true,
00813 "3");
00814
00815 newMenuItem(v3, "&Toggle Triangulation",
00816 glviewer,
00817 SLOT(toggleTriangulation()),
00818 "Switch between surface, wireframe and point cloud",
00819 "T");
00820
00821 newMenuItem(v3, "Follow &Camera",
00822 glviewer,
00823 SLOT(toggleFollowMode(bool)),
00824 "Always use viewpoint of last frame (except zoom)",
00825 true,
00826 "Shift+F");
00827
00828 newMenuItem(v3, "Show Grid",
00829 glviewer,
00830 SLOT(toggleShowGrid(bool)),
00831 "Display XY plane grid",
00832 false);
00833 newMenuItem(v3, "Show Pose TFs",
00834 glviewer,
00835 SLOT(toggleShowTFs(bool)),
00836 "Display pose transformations at axes",
00837 false);
00838 newMenuItem(v3, "Show Pose IDs",
00839 glviewer,
00840 SLOT(toggleShowIDs(bool)),
00841 "Display pose ids at axes",
00842 false,
00843 "I");
00844
00845 newMenuItem(v3, "Show &Poses of Graph",
00846 glviewer,
00847 SLOT(toggleShowPoses(bool)),
00848 "Display poses as axes",
00849 ParameterServer::instance()->get<bool>("show_axis"),
00850 "P");
00851
00852 newMenuItem(v3, "Show &Edges of Graph",
00853 glviewer,
00854 SLOT(toggleShowEdges(bool)),
00855 "Display edges of pose graph as lines",
00856 ParameterServer::instance()->get<bool>("show_axis"),
00857 "E");
00858
00859 newMenuItem(v3, "Stere&o View",
00860 glviewer,
00861 SLOT(toggleStereo(bool)),
00862 "Split screen view with slightly shifted Camera",
00863 false);
00864 newMenuItem(v3, "Show &Feature Locations",
00865 glviewer,
00866 SLOT(toggleShowFeatures(bool)),
00867 "Toggle whether feature locations should be rendered",
00868 false);
00869 newMenuItem(v3, "Show &Octomap",
00870 glviewer,
00871 SLOT(toggleShowOctoMap(bool)),
00872 "Toggle whether octomap should be displayed",
00873 true,
00874 "Ctrl+Shift+O");
00875
00876 newMenuItem(v3, "Show &Clouds",
00877 glviewer,
00878 SLOT(toggleShowClouds(bool)),
00879 "Toggle whether point clouds should be rendered",
00880 true,
00881 "C");
00882
00883 newMenuItem(v3, "Toggle Background",
00884 glviewer,
00885 SLOT(toggleBackgroundColor(bool)),
00886 "Toggle whether background should be white or black",
00887 true,
00888 "B");
00889
00890 newMenuItem(v3, "Set Rotation &Grid",
00891 this,
00892 SLOT(setRotationGrid()),
00893 "Discretize Rotation in Viewer",
00894 "G");
00895
00896 newMenuItem(v3, "Set Stereo Offset",
00897 this,
00898 SLOT(setStereoShift()),
00899 "Set the distance between the virtual cameras for stereo view",
00900 "<");
00901
00902 }
00903
00904
00905
00906
00907 QMenu *v2 = menuBar()->addMenu(tr("&2D View"));
00908
00909 newMenuItem(v2, "Toggle &2D Stream",
00910 this,
00911 SLOT(set2DStream(bool)),
00912 "Turn the Image Stream on/off",
00913 true,
00914 "2");
00915
00916 newMenuItem(v2, "Show Visual Image",
00917 visual_image_label,
00918 SLOT(setVisible(bool)),
00919 "Show/Hide visual (color/monochrome) image.",
00920 true);
00921
00922 newMenuItem(v2, "Show Depth Image",
00923 depth_image_label,
00924 SLOT(setVisible(bool)),
00925 "Show/Hide depth image.",
00926 true);
00927
00928 newMenuItem(v2, "Show Keypoint Image",
00929 feature_image_label,
00930 SLOT(setVisible(bool)),
00931 "Show/Hide image with keypoints.",
00932 false);
00933
00934 newMenuItem(v2, "Show Visual Flow Image",
00935 feature_flow_image_label,
00936 SLOT(setVisible(bool)),
00937 "Show/Hide image with sparse feature flow (arrows).",
00938 true);
00939
00940
00941 QMenu *st = menuBar()->addMenu(tr("&Settings"));
00942
00943 newMenuItem(st, "&Reload Config",
00944 SLOT(reloadConfig()),
00945 "Reload Configuration from Parameter Server.",
00946 "",
00947 QIcon::fromTheme("reload"));
00948
00949 newMenuItem(st, "&View Current Settings",
00950 SLOT(showOptions()),
00951 "Display the currently active options",
00952 "?");
00953
00954 newMenuItem(st, "Set internal &Parameter",
00955 SLOT(setParam()),
00956 "Change a parameter (This will also change the value on the ROS Parameter server)");
00957
00958 newMenuItem(st, "Set Ma&ximum Edge Error",
00959 SLOT(pruneEdgesWithHighError()),
00960 "Set the maximum allowed for edges (reoptimize to see the effect)",
00961 "Ctrl+X");
00962
00963
00964 QMenu *hm= menuBar()->addMenu(tr("&Help"));
00965
00966 newMenuItem(hm, "&Usage Help",
00967 SLOT(help()),
00968 "Show usage information",
00969 QKeySequence::HelpContents,
00970 QIcon::fromTheme("help-contents"));
00971
00972 newMenuItem(hm, "&About RGBDSLAM",
00973 SLOT(about()),
00974 "Show information about RGBDSLAM",
00975 "Ctrl+A",
00976 QIcon::fromTheme("help-about"));
00977
00978 }
00979
00980 GLViewer* Graphical_UI::getGLViewer() {
00981 return glviewer;
00982 }
00983
00984 void Graphical_UI::showOptions(){
00985 QScrollArea* scrollarea = new QScrollArea();
00986 scrollarea->setMinimumWidth(600);
00987 QWidget* scrollarea_content = new QWidget(scrollarea);
00988 scrollarea_content->setMinimumWidth(500);
00989 QGridLayout *gridLayout = new QGridLayout(scrollarea_content);
00990
00991
00992 gridLayout->setVerticalSpacing(10);
00993 gridLayout->setContentsMargins(10,10,10,10);
00994 QLabel* intro = new QLabel("<h3>This is an read-only view of the current settings. To modify RGBDSLAM's settings use the ROS parameter server functionality (i.e. set options either in a launch-file or as a command line parameter).</h3>");
00995 intro->setAlignment(Qt::AlignJustify);
00996 intro->setWordWrap(true);
00997 intro->setMinimumWidth(560);
00998 intro->setMinimumHeight(60);
00999 gridLayout->addWidget(intro,0,0,1,-1, Qt::AlignJustify);
01000
01001 QLabel namevalue_header("Parameter Name and Value");
01002 gridLayout->addWidget(&namevalue_header, 1, 0);
01003 QLabel description_header("Parameter Description");
01004 gridLayout->addWidget(&description_header, 1, 1);
01005
01006
01007 std::map<std::string, boost::any>& config = ParameterServer::instance()->getConfigData();
01008 std::map<std::string, boost::any>::const_iterator itr = config.begin();
01009 for (int row=2; itr != config.end(); ++itr, row+=3) {
01010
01011 QLabel* name_lbl = new QLabel(QString("<b>") + itr->first.c_str() + QString("</b>"), scrollarea_content);
01012 name_lbl->setTextFormat(Qt::RichText);
01013 name_lbl->setAlignment(Qt::AlignLeft);
01014 gridLayout->addWidget(name_lbl, row, 0);
01015 QLabel* description = new QLabel(ParameterServer::instance()->getDescription(itr->first).c_str());
01016 description->setAlignment(Qt::AlignJustify);
01017 description->setWordWrap(true);
01018 gridLayout->addWidget(description, row+1, 0, 1,-1);
01019
01020
01021 QString val_txt;
01022 if (itr->second.type() == typeid(std::string)) {
01023 val_txt = QString::fromStdString(boost::any_cast<std::string>(itr->second));
01024 } else if (itr->second.type() == typeid(int)) {
01025 val_txt = QString::number(boost::any_cast<int>(itr->second));
01026 } else if (itr->second.type() == typeid(double)) {
01027 val_txt = QString::number(boost::any_cast<double>(itr->second));
01028 } else if (itr->second.type() == typeid(bool)) {
01029 val_txt = boost::any_cast<bool>(itr->second)? "True" : "False";
01030 }
01031 QLabel* val_lbl = new QLabel(QString("<tt>") + val_txt + QString("</tt>"), scrollarea_content);
01032 val_lbl->setTextFormat(Qt::RichText);
01033 val_lbl->setLineWidth(1);
01034 val_lbl->setFrameStyle(QFrame::Panel|QFrame::Sunken);
01035
01036 gridLayout->addWidget(val_lbl, row, 1);
01037
01038
01039 QFrame* line = new QFrame(scrollarea_content);
01040 line->setGeometry(QRect(500, 150, 118, 3));
01041 line->setFrameShape(QFrame::HLine);
01042 line->setFrameShadow(QFrame::Sunken);
01043 gridLayout->addWidget(line, row+2, 0, 1,-1);
01044 }
01045
01046
01047 scrollarea_content->setLayout(gridLayout);
01048 scrollarea->setWidget(scrollarea_content);
01049 scrollarea->show();
01050 scrollarea->raise();
01051 scrollarea->activateWindow();
01052 }
01053
01054
01055 void Graphical_UI::showBusy(int id, const char* message, int max){
01056 bool contained = progressbars.contains(id);
01057 QProgressBar* busydialog = NULL;
01058 if(!contained) {
01059 busydialog = new QProgressBar();
01060 busydialog->resize(100, 10);
01061 progressbars[id] = busydialog;
01062 statusBar()->insertPermanentWidget(progressbars.size(), busydialog);
01063 } else {
01064 busydialog = progressbars[id];
01065 }
01066 busydialog->show();
01067 busydialog->setMinimum(0);
01068 busydialog->setMaximum(max);
01069 busydialog->setFormat(QString(message) + " %p%");
01070 }
01071
01072 void Graphical_UI::setBusy(int id, const char* message, int val){
01073 if(progressbars.contains(id)) {
01074 QProgressBar* busydialog = progressbars[id];
01075 busydialog->show();
01076 if(val > busydialog->maximum()){
01077 statusBar()->removeWidget(busydialog);
01078 busydialog->hide();
01079 statusBar()->showMessage(message);
01080 } else {
01081 busydialog->setValue(val);
01082 busydialog->setFormat(QString(message) + " %p%");
01083 busydialog->update();
01084 }
01085 } else {
01086 statusBar()->showMessage(QString("Error: Set Value for non-existing progressbar ")+QString::number(id));
01087 showBusy(id, message, 0);
01088 setBusy(id, message, val);
01089 }
01090 }
01091
01092 void Graphical_UI::saveAllImages() {
01093 QString tmp="~";
01094 QString file_basename = QFileDialog::getSaveFileName(this, "Save all images to file", tmp, tr("PNG (*.png)"));
01095 file_basename.remove(".png", Qt::CaseInsensitive);
01096
01097 QString depth_file =file_basename+"-depth.png";
01098 QString feature_file =file_basename+"-feature.png";
01099 QString flow_file =file_basename+"-flow.png";
01100 QString visual_file =file_basename+"-visual.png";
01101 QString vector_file =file_basename+"-points.ps";
01102 std::cout << visual_file.toStdString() << std::endl;
01103 depth_image_label->pixmap()->save(depth_file);
01104 feature_image_label->pixmap()->save(feature_file);
01105 feature_flow_image_label->pixmap()->save(flow_file);
01106 visual_image_label->pixmap()->save(visual_file);
01107
01108
01109 QString message = tr("Saving all images.");
01110 statusBar()->showMessage(message);
01111 }