StatsToolBox.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007     * Redistributions of source code must retain the above copyright
00008       notice, this list of conditions and the following disclaimer.
00009     * Redistributions in binary form must reproduce the above copyright
00010       notice, this list of conditions and the following disclaimer in the
00011       documentation and/or other materials provided with the distribution.
00012     * Neither the name of the Universite de Sherbrooke nor the
00013       names of its contributors may be used to endorse or promote products
00014       derived from this software without specific prior written permission.
00015 
00016 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00017 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
00020 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00022 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00023 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00025 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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                 //This fixes an issue where the
00157                 //button (used on col 0) of the first line in the
00158                 //toolbox couldn't be clicked
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         //Statistics in the GUI (for plotting)
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         // round float to max 2 numbers after the dot
00279         //x = (float(int(100*x)))/100;
00280         //y = (float(int(100*y)))/100;
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                 // statFullName format : "Grp/Name/unit"
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                 //layout->insertWidget(layout->count()-1, item);
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                 // if not already in the plot
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                 //Create a new plot
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                 //Dock
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                 //Plot
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                 //Add a new curve linked to the statBox
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 }


rtabmap
Author(s): Mathieu Labbe
autogenerated on Thu Jun 6 2019 21:59:31