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, 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 {
00055 this->setupUi(grid);
00056 _name->setText(name);
00057 if(y.size() == 1)
00058 {
00059 _value->setNum(y[0]);
00060 }
00061 else if(y.size() > 1)
00062 {
00063 _value->setText("*");
00064 }
00065 _unit->setText(unit);
00066 this->updateMenu(menu);
00067 }
00068
00069 StatItem::~StatItem()
00070 {
00071
00072 }
00073
00074 void StatItem::addValue(float y)
00075 {
00076 _value->setText(QString::number(y, 'g', 3));
00077 emit valueAdded(y);
00078 }
00079
00080 void StatItem::addValue(float x, float y)
00081 {
00082 _value->setText(QString::number(y, 'g', 3));
00083 emit valueAdded(x,y);
00084 }
00085
00086 void StatItem::setValues(const std::vector<float> & x, const std::vector<float> & y)
00087 {
00088 _value->setText("*");
00089 emit valuesChanged(x,y);
00090 }
00091
00092 QString StatItem::value() const
00093 {
00094 return _value->text();
00095 }
00096
00097 void StatItem::setupUi(QGridLayout * grid)
00098 {
00099 _menu = new QMenu(this);
00100 _menu->addMenu("Add to figure...");
00101 _button = new QToolButton(this);
00102 _button->setIcon(QIcon(":/images/Plot16.png"));
00103 _button->setPopupMode(QToolButton::InstantPopup);
00104 _button->setMenu(_menu);
00105 _name = new QLabel(this);
00106 _name->setWordWrap(true);
00107 _value = new QLabel(this);
00108 _unit = new QLabel(this);
00109
00110 if(grid)
00111 {
00112 int row = grid->rowCount();
00113
00114
00115
00116
00117 grid->addWidget(_button, row, 3);
00118 grid->addWidget(_name, row, 0);
00119 grid->addWidget(_value, row, 1);
00120 grid->addWidget(_unit, row, 2);
00121 }
00122 else
00123 {
00124 QHBoxLayout * layout = new QHBoxLayout(this);
00125 this->setLayout(layout);
00126 layout->addWidget(_button);
00127 layout->addWidget(_name);
00128 layout->addWidget(_value);
00129 layout->addWidget(_unit);
00130 layout->addStretch();
00131 layout->setMargin(0);
00132 }
00133 }
00134
00135 void StatItem::updateMenu(const QMenu * menu)
00136 {
00137 _menu->clear();
00138 QAction * action;
00139 QList<QAction *> actions = menu->actions();
00140 QMenu * plotMenu = _menu->addMenu("Add to figure...");
00141 for(int i=0; i<actions.size(); ++i)
00142 {
00143 action = plotMenu->addAction(actions.at(i)->text());
00144 connect(action, SIGNAL(triggered()), this, SLOT(preparePlotRequest()));
00145 }
00146 }
00147
00148 void StatItem::preparePlotRequest()
00149 {
00150 QAction * action = qobject_cast<QAction*>(sender());
00151 if(action)
00152 {
00153 emit plotRequested(this, action->text());
00154 }
00155 }
00156
00157
00158
00159
00160
00161
00162
00163
00164 StatsToolBox::StatsToolBox(QWidget * parent) :
00165 QWidget(parent)
00166 {
00167 ULOGGER_DEBUG("");
00168
00169 _statBox = new QToolBox(this);
00170 this->setLayout(new QVBoxLayout());
00171 this->layout()->setMargin(0);
00172 this->layout()->addWidget(_statBox);
00173 _statBox->layout()->setSpacing(0);
00174 _plotMenu = new QMenu(this);
00175 _plotMenu->addAction(tr("<New figure>"));
00176 _workingDirectory = QDir::homePath();
00177 }
00178
00179 StatsToolBox::~StatsToolBox()
00180 {
00181 closeFigures();
00182 }
00183
00184 void StatsToolBox::closeFigures()
00185 {
00186 QMap<QString, QWidget*> figuresTmp = _figures;
00187 for(QMap<QString, QWidget*>::iterator iter = figuresTmp.begin(); iter!=figuresTmp.end(); ++iter)
00188 {
00189 iter.value()->close();
00190 }
00191 }
00192
00193 void StatsToolBox::updateStat(const QString & statFullName, float y)
00194 {
00195 std::vector<float> vx,vy(1);
00196 vy[0] = y;
00197 updateStat(statFullName, vx, vy);
00198 }
00199
00200 void StatsToolBox::updateStat(const QString & statFullName, float x, float y)
00201 {
00202 std::vector<float> vx(1),vy(1);
00203 vx[0] = x;
00204 vy[0] = y;
00205 updateStat(statFullName, vx, vy);
00206 }
00207
00208 void StatsToolBox::updateStat(const QString & statFullName, const std::vector<float> & x, const std::vector<float> & y)
00209 {
00210
00211
00212
00213
00214 StatItem * item = _statBox->findChild<StatItem *>(statFullName);
00215 if(item)
00216 {
00217 if(y.size() == 1 && x.size() == 1)
00218 {
00219 item->addValue(x[0], y[0]);
00220 }
00221 else if(y.size() == 1 && x.size() == 0)
00222 {
00223 item->addValue(y[0]);
00224 }
00225 else
00226 {
00227 item->setValues(x, y);
00228 }
00229 }
00230 else
00231 {
00232
00233 QStringList list = statFullName.split('/');
00234 QString grp;
00235 QString name;
00236 QString unit;
00237 if(list.size() >= 3)
00238 {
00239 grp = list.at(0);
00240 name = list.at(1);
00241 unit = list.at(2);
00242 }
00243 else if(list.size() == 2)
00244 {
00245 grp = list.at(0);
00246 name = list.at(1);
00247 }
00248 else if(list.size() == 1)
00249 {
00250 name = list.at(0);
00251 }
00252 else
00253 {
00254 ULOGGER_WARN("A statistic has no name");
00255 return;
00256 }
00257
00258 if(grp.isEmpty())
00259 {
00260 grp = tr("Global");
00261 }
00262
00263 int index = -1;
00264 for(int i=0; i<_statBox->count(); ++i)
00265 {
00266 if(_statBox->itemText(i).compare(grp) == 0)
00267 {
00268 index = i;
00269 break;
00270 }
00271 }
00272
00273 if(index<0)
00274 {
00275 QWidget * newWidget = new QWidget(_statBox);
00276 index = _statBox->addItem(newWidget, grp);
00277 QVBoxLayout * layout = new QVBoxLayout(newWidget);
00278 newWidget->setLayout(layout);
00279 QGridLayout * grid = new QGridLayout();
00280 grid->setVerticalSpacing(2);
00281 grid->setColumnStretch(0, 1);
00282 layout->addLayout(grid);
00283 layout->addStretch();
00284 }
00285
00286 QVBoxLayout * layout = qobject_cast<QVBoxLayout *>(_statBox->widget(index)->layout());
00287 if(!layout)
00288 {
00289 ULOGGER_ERROR("Layout is null ?!?");
00290 return;
00291 }
00292 QGridLayout * grid = qobject_cast<QGridLayout *>(layout->itemAt(0)->layout());
00293 if(!grid)
00294 {
00295 ULOGGER_ERROR("Layout is null ?!?");
00296 return;
00297 }
00298
00299 item = new StatItem(name, x, y, unit, _plotMenu, grid, _statBox->widget(index));
00300 item->setObjectName(statFullName);
00301
00302
00303 connect(item, SIGNAL(plotRequested(const StatItem *, const QString &)), this, SLOT(plot(const StatItem *, const QString &)));
00304 connect(this, SIGNAL(menuChanged(const QMenu *)), item, SLOT(updateMenu(const QMenu *)));
00305 }
00306 }
00307
00308 void StatsToolBox::plot(const StatItem * stat, const QString & plotName)
00309 {
00310 QWidget * fig = _figures.value(plotName, (QWidget*)0);
00311 UPlot * plot = 0;
00312 if(fig)
00313 {
00314 plot = fig->findChild<UPlot *>(plotName);
00315 }
00316 if(plot)
00317 {
00318
00319 if(!plot->contains(stat->objectName()))
00320 {
00321 UPlotCurve * curve = new UPlotCurve(stat->objectName(), plot);
00322 curve->setPen(plot->getRandomPenColored());
00323 connect(stat, SIGNAL(valueAdded(float)), curve, SLOT(addValue(float)));
00324 connect(stat, SIGNAL(valueAdded(float, float)), curve, SLOT(addValue(float, float)));
00325 connect(stat, SIGNAL(valuesChanged(const std::vector<float> &, const std::vector<float> &)), curve, SLOT(setData(const std::vector<float> &, const std::vector<float> &)));
00326 if(stat->value().compare("*") == 0)
00327 {
00328 plot->setMaxVisibleItems(0);
00329 }
00330 if(!plot->addCurve(curve))
00331 {
00332 ULOGGER_WARN("Already added to the figure");
00333 }
00334 emit figuresSetupChanged();
00335 }
00336 else
00337 {
00338 ULOGGER_WARN("Already added to the figure");
00339 }
00340 plot->activateWindow();
00341 }
00342 else
00343 {
00344
00345 QString id = tr("Figure 0");
00346 if(_plotMenu->actions().size())
00347 {
00348 id = _plotMenu->actions().last()->text();
00349 }
00350 id.replace(tr("Figure "), "");
00351 QString newPlotName = QString(tr("Figure %1")).arg(id.toInt()+1);
00352
00353 QDialog * figure = new QDialog(0, Qt::Window);
00354 _figures.insert(newPlotName, figure);
00355 QHBoxLayout * hLayout = new QHBoxLayout(figure);
00356 hLayout->setContentsMargins(0,0,0,0);
00357 figure->setWindowTitle(newPlotName);
00358 figure->setAttribute(Qt::WA_DeleteOnClose, true);
00359 connect(figure, SIGNAL(destroyed(QObject*)), this, SLOT(figureDeleted(QObject*)));
00360
00361 UPlot * newPlot = new UPlot(figure);
00362 newPlot->setWorkingDirectory(_workingDirectory);
00363 newPlot->setMaxVisibleItems(50);
00364 newPlot->setObjectName(newPlotName);
00365 hLayout->addWidget(newPlot);
00366 _plotMenu->addAction(newPlotName);
00367 figure->setSizeGripEnabled(true);
00368
00369
00370 UPlotCurve * curve = new UPlotCurve(stat->objectName(), newPlot);
00371 curve->setPen(newPlot->getRandomPenColored());
00372 connect(stat, SIGNAL(valueAdded(float)), curve, SLOT(addValue(float)));
00373 connect(stat, SIGNAL(valueAdded(float, float)), curve, SLOT(addValue(float, float)));
00374 connect(stat, SIGNAL(valuesChanged(const std::vector<float> &, const std::vector<float> &)), curve, SLOT(setData(const std::vector<float> &, const std::vector<float> &)));
00375 if(stat->value().compare("*") == 0)
00376 {
00377 newPlot->setMaxVisibleItems(0);
00378 }
00379 if(!newPlot->addCurve(curve))
00380 {
00381 ULOGGER_ERROR("Not supposed to be here !?!");
00382 delete curve;
00383 }
00384 figure->show();
00385 emit figuresSetupChanged();
00386
00387 emit menuChanged(_plotMenu);
00388 }
00389 }
00390
00391 void StatsToolBox::figureDeleted(QObject * obj)
00392 {
00393 if(obj)
00394 {
00395 QWidget * plot = qobject_cast<QWidget*>(obj);
00396 if(plot)
00397 {
00398 _figures.remove(plot->windowTitle());
00399 QList<QAction*> actions = _plotMenu->actions();
00400 for(int i=0; i<actions.size(); ++i)
00401 {
00402 if(actions.at(i)->text().compare(plot->windowTitle()) == 0)
00403 {
00404 _plotMenu->removeAction(actions.at(i));
00405 delete actions[i];
00406 emit menuChanged(_plotMenu);
00407 break;
00408 }
00409 }
00410 emit figuresSetupChanged();
00411 }
00412 else
00413 {
00414 UERROR("");
00415 }
00416 }
00417 else
00418 {
00419 UERROR("");
00420 }
00421 }
00422
00423 void StatsToolBox::contextMenuEvent(QContextMenuEvent * event)
00424 {
00425 QMenu topMenu(this);
00426 QMenu * menu = topMenu.addMenu(tr("Add all statistics from tab \"%1\" to...").arg(_statBox->itemText(_statBox->currentIndex())));
00427 QList<QAction* > actions = _plotMenu->actions();
00428 menu->addActions(actions);
00429 QAction * aClearFigures = topMenu.addAction(tr("Clear all figures"));
00430 QAction * action = topMenu.exec(event->globalPos());
00431 QString plotName;
00432 if(action)
00433 {
00434 if(action == aClearFigures)
00435 {
00436 for(QMap<QString, QWidget*>::iterator i=_figures.begin(); i!=_figures.end(); ++i)
00437 {
00438 QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
00439 if(plots.size() == 1)
00440 {
00441 QStringList names = plots[0]->curveNames();
00442 plots[0]->clearData();
00443 }
00444 else
00445 {
00446 UERROR("");
00447 }
00448 }
00449 }
00450 else
00451 {
00452 for(int i=0; i<actions.size(); ++i)
00453 {
00454 if(actions.at(i) == action)
00455 {
00456 plotName = actions.at(i)->text();
00457 break;
00458 }
00459 }
00460 }
00461 }
00462
00463 if(!plotName.isEmpty())
00464 {
00465 QList<StatItem*> items = _statBox->currentWidget()->findChildren<StatItem*>();
00466 for(int i=0; i<items.size(); ++i)
00467 {
00468 this->plot(items.at(i), plotName);
00469 if(plotName.compare(tr("<New figure>")) == 0)
00470 {
00471 plotName = _plotMenu->actions().last()->text();
00472 }
00473 }
00474 }
00475 }
00476
00477 void StatsToolBox::getFiguresSetup(QList<int> & curvesPerFigure, QStringList & curveNames)
00478 {
00479 curvesPerFigure.clear();
00480 curveNames.clear();
00481 for(QMap<QString, QWidget*>::iterator i=_figures.begin(); i!=_figures.end(); ++i)
00482 {
00483 QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
00484 if(plots.size() == 1)
00485 {
00486 QStringList names = plots[0]->curveNames();
00487 curvesPerFigure.append(names.size());
00488 curveNames.append(names);
00489 }
00490 else
00491 {
00492 UERROR("");
00493 }
00494 }
00495 }
00496 void StatsToolBox::addCurve(const QString & name, bool newFigure)
00497 {
00498 StatItem * item = _statBox->findChild<StatItem *>(name);
00499 if(!item)
00500 {
00501 this->updateStat(name, 0, 0);
00502 item = _statBox->findChild<StatItem *>(name);
00503 }
00504
00505 if(item)
00506 {
00507 if(newFigure)
00508 {
00509 this->plot(item, "");
00510 }
00511 else
00512 {
00513 this->plot(item, _plotMenu->actions().last()->text());
00514 }
00515 }
00516 else
00517 {
00518 ULOGGER_ERROR("Not supposed to be here...");
00519 }
00520 }
00521
00522 void StatsToolBox::setWorkingDirectory(const QString & workingDirectory)
00523 {
00524 if(QDir(workingDirectory).exists())
00525 {
00526 _workingDirectory = workingDirectory;
00527 for(QMap<QString, QWidget*>::iterator i=_figures.begin(); i!=_figures.end(); ++i)
00528 {
00529 QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
00530 if(plots.size() == 1)
00531 {
00532 plots[0]->setWorkingDirectory(_workingDirectory);
00533 }
00534 else
00535 {
00536 UERROR("");
00537 }
00538 }
00539 }
00540 else
00541 {
00542 UWARN("The directory \"%s\" doesn't exist, using \"%s\" instead...",
00543 workingDirectory.toStdString().c_str(),
00544 _workingDirectory.toStdString().c_str());
00545 }
00546 }
00547
00548 }