00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "rtabmap/gui/StatsToolBox.h"
00029
00030 #include <QHBoxLayout>
00031 #include <QVBoxLayout>
00032 #include <QGridLayout>
00033 #include <QMenu>
00034 #include <QLabel>
00035 #include <QToolButton>
00036 #include <QtCore/QChildEvent>
00037 #include <QtCore/QDir>
00038 #include <QtGui/QContextMenuEvent>
00039 #include <QToolBox>
00040 #include <QDialog>
00041
00042 #include "rtabmap/utilite/UPlot.h"
00043 #include <rtabmap/utilite/ULogger.h>
00044
00045 namespace rtabmap {
00046
00047 StatItem::StatItem(const QString & name, bool cacheOn, const std::vector<float> & x, const std::vector<float> & y, const QString & unit, const QMenu * menu, QGridLayout * grid, QWidget * parent) :
00048 QWidget(parent),
00049 _button(0),
00050 _name(0),
00051 _value(0),
00052 _unit(0),
00053 _menu(0),
00054 _cacheOn(cacheOn)
00055 {
00056 this->setupUi(grid);
00057 _name->setText(name);
00058 if(y.size() == 1 || (y.size() > 1 && _cacheOn))
00059 {
00060 _value->setNum(y[y.size()-1]);
00061 }
00062 else if(y.size() > 1)
00063 {
00064 _value->setText("*");
00065 }
00066 if(cacheOn)
00067 {
00068 _x = x;
00069 _y = y;
00070 }
00071 _unit->setText(unit);
00072 this->updateMenu(menu);
00073 }
00074
00075 StatItem::~StatItem()
00076 {
00077
00078 }
00079
00080 void StatItem::clearCache()
00081 {
00082 _x.clear();
00083 _y.clear();
00084 _value->clear();
00085 }
00086
00087 void StatItem::addValue(float y)
00088 {
00089 if(_cacheOn)
00090 {
00091 _y.push_back(y);
00092 }
00093 _value->setText(QString::number(y, 'g', 3));
00094 Q_EMIT valueAdded(y);
00095 }
00096
00097 void StatItem::addValue(float x, float y)
00098 {
00099 if(_cacheOn)
00100 {
00101 if (_x.size() && x <_x.back())
00102 {
00103 clearCache();
00104 }
00105
00106 _y.push_back(y);
00107 _x.push_back(x);
00108 }
00109
00110 _value->setText(QString::number(y, 'g', 3));
00111 Q_EMIT valueAdded(x,y);
00112 }
00113
00114 void StatItem::setValues(const std::vector<float> & x, const std::vector<float> & y)
00115 {
00116 if(_cacheOn)
00117 {
00118 _x = x;
00119 _y = y;
00120 if(y.size())
00121 {
00122 _value->setNum(y[y.size()-1]);
00123 }
00124 }
00125 else
00126 {
00127 _value->setText("*");
00128 }
00129 Q_EMIT valuesChanged(x,y);
00130 }
00131
00132 QString StatItem::value() const
00133 {
00134 return _value->text();
00135 }
00136
00137 void StatItem::setupUi(QGridLayout * grid)
00138 {
00139 _menu = new QMenu(this);
00140 _menu->addMenu("Add to figure...");
00141 _button = new QToolButton(this);
00142 _button->setIcon(QIcon(":/images/Plot16.png"));
00143 _button->setPopupMode(QToolButton::InstantPopup);
00144 _button->setMenu(_menu);
00145 _name = new QLabel(this);
00146 _name->setTextInteractionFlags(Qt::TextSelectableByMouse);
00147 _name->setWordWrap(true);
00148 _value = new QLabel(this);
00149 _value->setTextInteractionFlags(Qt::TextSelectableByMouse);
00150 _unit = new QLabel(this);
00151
00152 if(grid)
00153 {
00154 int row = grid->rowCount();
00155
00156
00157
00158
00159 grid->addWidget(_button, row, 3);
00160 grid->addWidget(_name, row, 0);
00161 grid->addWidget(_value, row, 1);
00162 grid->addWidget(_unit, row, 2);
00163 }
00164 else
00165 {
00166 QHBoxLayout * layout = new QHBoxLayout(this);
00167 this->setLayout(layout);
00168 layout->addWidget(_button);
00169 layout->addWidget(_name);
00170 layout->addWidget(_value);
00171 layout->addWidget(_unit);
00172 layout->addStretch();
00173 layout->setMargin(0);
00174 }
00175 }
00176
00177 void StatItem::setCacheOn(bool on)
00178 {
00179 _cacheOn = on;
00180 if(!on)
00181 {
00182 _x.clear();
00183 _y.clear();
00184 }
00185 }
00186
00187 void StatItem::updateMenu(const QMenu * menu)
00188 {
00189 _menu->clear();
00190 QAction * action;
00191 QList<QAction *> actions = menu->actions();
00192 QMenu * plotMenu = _menu->addMenu("Add to figure...");
00193 for(int i=0; i<actions.size(); ++i)
00194 {
00195 action = plotMenu->addAction(actions.at(i)->text());
00196 connect(action, SIGNAL(triggered()), this, SLOT(preparePlotRequest()));
00197 }
00198 }
00199
00200 void StatItem::preparePlotRequest()
00201 {
00202 QAction * action = qobject_cast<QAction*>(sender());
00203 if(action)
00204 {
00205 Q_EMIT plotRequested(this, action->text());
00206 }
00207 }
00208
00209
00210
00211
00212
00213
00214
00215
00216 StatsToolBox::StatsToolBox(QWidget * parent) :
00217 QWidget(parent)
00218 {
00219 ULOGGER_DEBUG("");
00220
00221 _statBox = new QToolBox(this);
00222 this->setLayout(new QVBoxLayout());
00223 this->layout()->setMargin(0);
00224 this->layout()->addWidget(_statBox);
00225 _statBox->layout()->setSpacing(0);
00226 _plotMenu = new QMenu(this);
00227 _plotMenu->addAction(tr("<New figure>"));
00228 _workingDirectory = QDir::homePath();
00229 _newFigureMaxItems = 0;
00230 }
00231
00232 StatsToolBox::~StatsToolBox()
00233 {
00234 closeFigures();
00235 }
00236
00237 void StatsToolBox::closeFigures()
00238 {
00239 QMap<QString, QWidget*> figuresTmp = _figures;
00240 for(QMap<QString, QWidget*>::iterator iter = figuresTmp.begin(); iter!=figuresTmp.end(); ++iter)
00241 {
00242 iter.value()->close();
00243 }
00244 }
00245
00246 void StatsToolBox::setCacheOn(bool on)
00247 {
00248 QList<StatItem *> items = _statBox->findChildren<StatItem *>();
00249 for(int i=0; i<items.size(); ++i)
00250 {
00251 items[i]->setCacheOn(on);
00252 }
00253 }
00254
00255 void StatsToolBox::updateStat(const QString & statFullName, bool cacheOn)
00256 {
00257 std::vector<float> vx,vy;
00258 updateStat(statFullName, vx, vy, cacheOn);
00259 }
00260
00261 void StatsToolBox::updateStat(const QString & statFullName, float y, bool cacheOn)
00262 {
00263 std::vector<float> vx,vy(1);
00264 vy[0] = y;
00265 updateStat(statFullName, vx, vy, cacheOn);
00266 }
00267
00268 void StatsToolBox::updateStat(const QString & statFullName, float x, float y, bool cacheOn)
00269 {
00270 std::vector<float> vx(1),vy(1);
00271 vx[0] = x;
00272 vy[0] = y;
00273 updateStat(statFullName, vx, vy, cacheOn);
00274 }
00275
00276 void StatsToolBox::updateStat(const QString & statFullName, const std::vector<float> & x, const std::vector<float> & y, bool cacheOn)
00277 {
00278
00279
00280
00281
00282 StatItem * item = _statBox->findChild<StatItem *>(statFullName);
00283 if(item)
00284 {
00285 item->setCacheOn(cacheOn);
00286 if(y.size() == 1 && x.size() == 1)
00287 {
00288 item->addValue(x[0], y[0]);
00289 }
00290 else if(y.size() == 1 && x.size() == 0)
00291 {
00292 item->addValue(y[0]);
00293 }
00294 else
00295 {
00296 item->setValues(x, y);
00297 }
00298 }
00299 else
00300 {
00301
00302 QStringList list = statFullName.split('/');
00303 QString grp;
00304 QString name;
00305 QString unit;
00306 if(list.size() >= 3)
00307 {
00308 grp = list.at(0);
00309 name = list.at(1);
00310 unit = list.at(2);
00311 for(int i=3; i<list.size(); ++i)
00312 {
00313 unit += "/" + list.at(i);
00314 }
00315 }
00316 else if(list.size() == 2)
00317 {
00318 grp = list.at(0);
00319 name = list.at(1);
00320 }
00321 else if(list.size() == 1)
00322 {
00323 name = list.at(0);
00324 }
00325 else
00326 {
00327 ULOGGER_WARN("A statistic has no name");
00328 return;
00329 }
00330
00331 if(grp.isEmpty())
00332 {
00333 grp = tr("Global");
00334 }
00335
00336 int index = -1;
00337 for(int i=0; i<_statBox->count(); ++i)
00338 {
00339 if(_statBox->itemText(i).compare(grp) == 0)
00340 {
00341 index = i;
00342 break;
00343 }
00344 }
00345
00346 if(index<0)
00347 {
00348 QWidget * newWidget = new QWidget(_statBox);
00349 index = _statBox->addItem(newWidget, grp);
00350 QVBoxLayout * layout = new QVBoxLayout(newWidget);
00351 newWidget->setLayout(layout);
00352 QGridLayout * grid = new QGridLayout();
00353 grid->setVerticalSpacing(2);
00354 grid->setColumnStretch(0, 1);
00355 layout->addLayout(grid);
00356 layout->addStretch();
00357 }
00358
00359 QVBoxLayout * layout = qobject_cast<QVBoxLayout *>(_statBox->widget(index)->layout());
00360 if(!layout)
00361 {
00362 ULOGGER_ERROR("Layout is null ?!?");
00363 return;
00364 }
00365 QGridLayout * grid = qobject_cast<QGridLayout *>(layout->itemAt(0)->layout());
00366 if(!grid)
00367 {
00368 ULOGGER_ERROR("Layout is null ?!?");
00369 return;
00370 }
00371
00372 item = new StatItem(name, cacheOn, x, y, unit, _plotMenu, grid, _statBox->widget(index));
00373 item->setObjectName(statFullName);
00374
00375
00376 connect(item, SIGNAL(plotRequested(const StatItem *, const QString &)), this, SLOT(plot(const StatItem *, const QString &)));
00377 connect(this, SIGNAL(menuChanged(const QMenu *)), item, SLOT(updateMenu(const QMenu *)));
00378 }
00379 }
00380
00381 void StatsToolBox::plot(const StatItem * stat, const QString & plotName)
00382 {
00383 QWidget * fig = _figures.value(plotName, (QWidget*)0);
00384 UPlot * plot = 0;
00385 if(fig)
00386 {
00387 plot = fig->findChild<UPlot *>(plotName);
00388 }
00389 if(plot)
00390 {
00391
00392 if(!plot->contains(stat->objectName()))
00393 {
00394 UPlotCurve * curve = new UPlotCurve(stat->objectName(), plot);
00395 curve->setPen(plot->getRandomPenColored());
00396 connect(stat, SIGNAL(valueAdded(float)), curve, SLOT(addValue(float)));
00397 connect(stat, SIGNAL(valueAdded(float, float)), curve, SLOT(addValue(float, float)));
00398 connect(stat, SIGNAL(valuesChanged(const std::vector<float> &, const std::vector<float> &)), curve, SLOT(setData(const std::vector<float> &, const std::vector<float> &)));
00399 if(stat->value().compare("*") == 0)
00400 {
00401 plot->setMaxVisibleItems(0);
00402 }
00403 if(!stat->yValues().empty())
00404 {
00405 if(stat->xValues().size() == stat->yValues().size())
00406 {
00407 curve->setData(stat->xValues(),stat->yValues());
00408 }
00409 else
00410 {
00411 curve->setData(stat->yValues());
00412 }
00413 }
00414 if(!plot->addCurve(curve))
00415 {
00416 ULOGGER_WARN("Already added to the figure");
00417 }
00418 Q_EMIT figuresSetupChanged();
00419 }
00420 else
00421 {
00422 ULOGGER_WARN("Already added to the figure");
00423 }
00424 plot->activateWindow();
00425 }
00426 else
00427 {
00428
00429 QString id = tr("Figure 0");
00430 if(_plotMenu->actions().size())
00431 {
00432 id = _plotMenu->actions().last()->text();
00433 }
00434 id.replace(tr("Figure "), "");
00435 QString newPlotName = QString(tr("Figure %1")).arg(id.toInt()+1);
00436
00437 QDialog * figure = new QDialog(0, Qt::Window);
00438 _figures.insert(newPlotName, figure);
00439 QHBoxLayout * hLayout = new QHBoxLayout(figure);
00440 hLayout->setContentsMargins(0,0,0,0);
00441 figure->setWindowTitle(newPlotName);
00442 figure->setAttribute(Qt::WA_DeleteOnClose, true);
00443 connect(figure, SIGNAL(destroyed(QObject*)), this, SLOT(figureDeleted(QObject*)));
00444
00445 UPlot * newPlot = new UPlot(figure);
00446 newPlot->setWorkingDirectory(_workingDirectory);
00447 newPlot->setMaxVisibleItems(_newFigureMaxItems);
00448 newPlot->setObjectName(newPlotName);
00449 hLayout->addWidget(newPlot);
00450 _plotMenu->addAction(newPlotName);
00451 figure->setSizeGripEnabled(true);
00452
00453
00454 UPlotCurve * curve = new UPlotCurve(stat->objectName(), newPlot);
00455 curve->setPen(newPlot->getRandomPenColored());
00456 connect(stat, SIGNAL(valueAdded(float)), curve, SLOT(addValue(float)));
00457 connect(stat, SIGNAL(valueAdded(float, float)), curve, SLOT(addValue(float, float)));
00458 connect(stat, SIGNAL(valuesChanged(const std::vector<float> &, const std::vector<float> &)), curve, SLOT(setData(const std::vector<float> &, const std::vector<float> &)));
00459 if(stat->value().compare("*") == 0)
00460 {
00461 newPlot->setMaxVisibleItems(0);
00462 }
00463
00464 if(!stat->yValues().empty())
00465 {
00466 if(stat->xValues().size() == stat->yValues().size())
00467 {
00468 curve->setData(stat->xValues(),stat->yValues());
00469 }
00470 else
00471 {
00472 curve->setData(stat->yValues());
00473 }
00474 }
00475
00476 if(!newPlot->addCurve(curve))
00477 {
00478 ULOGGER_ERROR("Not supposed to be here !?!");
00479 delete curve;
00480 }
00481 figure->show();
00482 Q_EMIT figuresSetupChanged();
00483
00484 Q_EMIT menuChanged(_plotMenu);
00485 }
00486 }
00487
00488 void StatsToolBox::figureDeleted(QObject * obj)
00489 {
00490 if(obj)
00491 {
00492 QWidget * plot = qobject_cast<QWidget*>(obj);
00493 if(plot)
00494 {
00495 _figures.remove(plot->windowTitle());
00496 QList<QAction*> actions = _plotMenu->actions();
00497 for(int i=0; i<actions.size(); ++i)
00498 {
00499 if(actions.at(i)->text().compare(plot->windowTitle()) == 0)
00500 {
00501 _plotMenu->removeAction(actions.at(i));
00502 delete actions[i];
00503 Q_EMIT menuChanged(_plotMenu);
00504 break;
00505 }
00506 }
00507 Q_EMIT figuresSetupChanged();
00508 }
00509 else
00510 {
00511 UERROR("");
00512 }
00513 }
00514 else
00515 {
00516 UERROR("");
00517 }
00518 }
00519
00520 void StatsToolBox::clear()
00521 {
00522 for (QMap<QString, QWidget*>::iterator i = _figures.begin(); i != _figures.end(); ++i)
00523 {
00524 QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
00525 if (plots.size() == 1)
00526 {
00527 QStringList names = plots[0]->curveNames();
00528 plots[0]->clearData();
00529 }
00530 else
00531 {
00532 UERROR("");
00533 }
00534 }
00535 QList<StatItem*> items = _statBox->currentWidget()->findChildren<StatItem*>();
00536 for (int i = 0; i<items.size(); ++i)
00537 {
00538 items[i]->clearCache();
00539 }
00540 }
00541
00542 void StatsToolBox::contextMenuEvent(QContextMenuEvent * event)
00543 {
00544 QMenu topMenu(this);
00545 QMenu * menu = topMenu.addMenu(tr("Add all statistics from tab \"%1\" to...").arg(_statBox->itemText(_statBox->currentIndex())));
00546 QList<QAction* > actions = _plotMenu->actions();
00547 menu->addActions(actions);
00548 QAction * aClearFigures = topMenu.addAction(tr("Clear all figures"));
00549 QAction * action = topMenu.exec(event->globalPos());
00550 QString plotName;
00551 if(action)
00552 {
00553 if(action == aClearFigures)
00554 {
00555 this->clear();
00556 }
00557 else
00558 {
00559 for(int i=0; i<actions.size(); ++i)
00560 {
00561 if(actions.at(i) == action)
00562 {
00563 plotName = actions.at(i)->text();
00564 break;
00565 }
00566 }
00567 }
00568 }
00569
00570 if(!plotName.isEmpty())
00571 {
00572 QList<StatItem*> items = _statBox->currentWidget()->findChildren<StatItem*>();
00573 for(int i=0; i<items.size(); ++i)
00574 {
00575 this->plot(items.at(i), plotName);
00576 if(plotName.compare(tr("<New figure>")) == 0)
00577 {
00578 plotName = _plotMenu->actions().last()->text();
00579 }
00580 }
00581 }
00582 }
00583
00584 void StatsToolBox::getFiguresSetup(QList<int> & curvesPerFigure, QStringList & curveNames)
00585 {
00586 curvesPerFigure.clear();
00587 curveNames.clear();
00588 for(QMap<QString, QWidget*>::iterator i=_figures.begin(); i!=_figures.end(); ++i)
00589 {
00590 QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
00591 if(plots.size() == 1)
00592 {
00593 QStringList names = plots[0]->curveNames();
00594 curvesPerFigure.append(names.size());
00595 curveNames.append(names);
00596 }
00597 else
00598 {
00599 UERROR("");
00600 }
00601 }
00602 }
00603 void StatsToolBox::addCurve(const QString & name, bool newFigure, bool cacheOn)
00604 {
00605 StatItem * item = _statBox->findChild<StatItem *>(name);
00606 if(!item)
00607 {
00608 this->updateStat(name, cacheOn);
00609 item = _statBox->findChild<StatItem *>(name);
00610 }
00611
00612 if(item)
00613 {
00614 if(newFigure)
00615 {
00616 this->plot(item, "");
00617 }
00618 else
00619 {
00620 this->plot(item, _plotMenu->actions().last()->text());
00621 }
00622 }
00623 else
00624 {
00625 ULOGGER_ERROR("Not supposed to be here...");
00626 }
00627 }
00628
00629 void StatsToolBox::setWorkingDirectory(const QString & workingDirectory)
00630 {
00631 if(QDir(workingDirectory).exists())
00632 {
00633 _workingDirectory = workingDirectory;
00634 for(QMap<QString, QWidget*>::iterator i=_figures.begin(); i!=_figures.end(); ++i)
00635 {
00636 QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
00637 if(plots.size() == 1)
00638 {
00639 plots[0]->setWorkingDirectory(_workingDirectory);
00640 }
00641 else
00642 {
00643 UERROR("");
00644 }
00645 }
00646 }
00647 else
00648 {
00649 UWARN("The directory \"%s\" doesn't exist, using \"%s\" instead...",
00650 workingDirectory.toStdString().c_str(),
00651 _workingDirectory.toStdString().c_str());
00652 }
00653 }
00654
00655 }