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