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, 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                 //This fixes an issue where the
00115                 //button (used on col 0) of the first line in the
00116                 //toolbox couldn't be clicked
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         //Statistics in the GUI (for plotting)
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         // round float to max 2 numbers after the dot
00211         //x = (float(int(100*x)))/100;
00212         //y = (float(int(100*y)))/100;
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                 // statFullName format : "Grp/Name/unit"
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                 //layout->insertWidget(layout->count()-1, item);
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                 // if not already in the plot
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                 //Create a new plot
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                 //Dock
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                 //Plot
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                 //Add a new curve linked to the statBox
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 }


rtabmap
Author(s): Mathieu Labbe
autogenerated on Sat Jul 23 2016 11:44:27