StatsToolBox.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2010-2014, 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 "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                 //This fixes an issue where the
00109                 //button (used on col 0) of the first line in the
00110                 //toolbox couldn't be clicked
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         //Statistics in the GUI (for plotting)
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         // round float to max 2 numbers after the dot
00198         //x = (float(int(100*x)))/100;
00199         //y = (float(int(100*y)))/100;
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                 // statFullName format : "Grp/Name/unit"
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                 //layout->insertWidget(layout->count()-1, item);
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                 // if not already in the plot
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                 //Create a new plot
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                 //Dock
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                 //Plot
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                 //Add a new curve linked to the statBox
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 }


rtabmap
Author(s): Mathieu Labbe
autogenerated on Fri Aug 28 2015 12:51:42