StatsToolBox.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the Universite de Sherbrooke nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
29 
30 #include <QHBoxLayout>
31 #include <QVBoxLayout>
32 #include <QGridLayout>
33 #include <QMenu>
34 #include <QLabel>
35 #include <QToolButton>
36 #include <QtCore/QChildEvent>
37 #include <QtCore/QDir>
38 #include <QtGui/QContextMenuEvent>
39 #include <QToolBox>
40 #include <QDialog>
41 
42 #include "rtabmap/utilite/UPlot.h"
44 
45 namespace rtabmap {
46 
47 StatItem::StatItem(const QString & name, bool cacheOn, const std::vector<qreal> & x, const std::vector<qreal> & y, const QString & unit, const QMenu * menu, QGridLayout * grid, QWidget * parent) :
48  QWidget(parent),
49  _button(0),
50  _name(0),
51  _value(0),
52  _unit(0),
53  _menu(0),
54  _cacheOn(cacheOn)
55 {
56  this->setupUi(grid);
57  _name->setText(name);
58  if(y.size() == 1 || (y.size() > 1 && _cacheOn))
59  {
60  _value->setNum(y[y.size()-1]);
61  }
62  else if(y.size() > 1)
63  {
64  _value->setText("*");
65  }
66  if(cacheOn)
67  {
68  _x = x;
69  _y = y;
70  }
71  _unit->setText(unit);
72  this->updateMenu(menu);
73 }
74 
76 {
77 
78 }
79 
81 {
82  _x.clear();
83  _y.clear();
84  _value->clear();
85 }
86 
87 void StatItem::addValue(qreal y)
88 {
89  if(_cacheOn)
90  {
91  _y.push_back(y);
92  }
93  _value->setText(QString::number(y, 'g', 3));
94  Q_EMIT valueAdded(y);
95 }
96 
97 void StatItem::addValue(qreal x, qreal y)
98 {
99  if(_cacheOn)
100  {
101  if (_x.size() && x <_x.back())
102  {
103  clearCache();
104  }
105 
106  _y.push_back(y);
107  _x.push_back(x);
108  }
109 
110  _value->setText(QString::number(y, 'g', 3));
111  Q_EMIT valueAdded(x,y);
112 }
113 
114 void StatItem::setValues(const std::vector<qreal> & x, const std::vector<qreal> & y)
115 {
116  if(_cacheOn)
117  {
118  _x = x;
119  _y = y;
120  if(y.size())
121  {
122  _value->setNum(y[y.size()-1]);
123  }
124  }
125  else
126  {
127  _value->setText("*");
128  }
129  Q_EMIT valuesChanged(x,y);
130 }
131 
132 QString StatItem::value() const
133 {
134  return _value->text();
135 }
136 
137 void StatItem::setupUi(QGridLayout * grid)
138 {
139  _menu = new QMenu(this);
140  _menu->addMenu("Add to figure...");
141  _button = new QToolButton(this);
142  _button->setIcon(QIcon(":/images/Plot16.png"));
143  _button->setPopupMode(QToolButton::InstantPopup);
144  _button->setMenu(_menu);
145  _name = new QLabel(this);
146  _name->setTextInteractionFlags(Qt::TextSelectableByMouse);
147  _name->setWordWrap(true);
148  _value = new QLabel(this);
149  _value->setTextInteractionFlags(Qt::TextSelectableByMouse);
150  _unit = new QLabel(this);
151 
152  if(grid)
153  {
154  int row = grid->rowCount();
155 
156  //This fixes an issue where the
157  //button (used on col 0) of the first line in the
158  //toolbox couldn't be clicked
159  grid->addWidget(_button, row, 3);
160  grid->addWidget(_name, row, 0);
161  grid->addWidget(_value, row, 1);
162  grid->addWidget(_unit, row, 2);
163  }
164  else
165  {
166  QHBoxLayout * layout = new QHBoxLayout(this);
167  this->setLayout(layout);
168  layout->addWidget(_button);
169  layout->addWidget(_name);
170  layout->addWidget(_value);
171  layout->addWidget(_unit);
172  layout->addStretch();
173  layout->setMargin(0);
174  }
175 }
176 
177 void StatItem::setCacheOn(bool on)
178 {
179  _cacheOn = on;
180  if(!on)
181  {
182  _x.clear();
183  _y.clear();
184  }
185 }
186 
187 void StatItem::updateMenu(const QMenu * menu)
188 {
189  _menu->clear();
190  QAction * action;
191  QList<QAction *> actions = menu->actions();
192  QMenu * plotMenu = _menu->addMenu("Add to figure...");
193  for(int i=0; i<actions.size(); ++i)
194  {
195  action = plotMenu->addAction(actions.at(i)->text());
196  connect(action, SIGNAL(triggered()), this, SLOT(preparePlotRequest()));
197  }
198 }
199 
201 {
202  QAction * action = qobject_cast<QAction*>(sender());
203  if(action)
204  {
205  Q_EMIT plotRequested(this, action->text());
206  }
207 }
208 
209 
210 
211 
212 
213 
214 
215 
216 StatsToolBox::StatsToolBox(QWidget * parent) :
217  QWidget(parent)
218 {
219  ULOGGER_DEBUG("");
220  //Statistics in the GUI (for plotting)
221  _statBox = new QToolBox(this);
222  this->setLayout(new QVBoxLayout());
223  this->layout()->setMargin(0);
224  this->layout()->addWidget(_statBox);
225  _statBox->layout()->setSpacing(0);
226  _plotMenu = new QMenu(this);
227  _plotMenu->addAction(tr("<New figure>"));
228  _workingDirectory = QDir::homePath();
229  _newFigureMaxItems = 0;
230 }
231 
233 {
234  closeFigures();
235 }
236 
238 {
239  QMap<QString, QWidget*> figuresTmp = _figures;
240  for(QMap<QString, QWidget*>::iterator iter = figuresTmp.begin(); iter!=figuresTmp.end(); ++iter)
241  {
242  iter.value()->close();
243  }
244 }
245 
247 {
248  QList<StatItem *> items = _statBox->findChildren<StatItem *>();
249  for(int i=0; i<items.size(); ++i)
250  {
251  items[i]->setCacheOn(on);
252  }
253 }
254 
255 void StatsToolBox::updateStat(const QString & statFullName, bool cacheOn)
256 {
257  std::vector<qreal> vx,vy;
258  updateStat(statFullName, vx, vy, cacheOn);
259 }
260 
261 void StatsToolBox::updateStat(const QString & statFullName, qreal y, bool cacheOn)
262 {
263  std::vector<qreal> vx,vy(1);
264  vy[0] = y;
265  updateStat(statFullName, vx, vy, cacheOn);
266 }
267 
268 void StatsToolBox::updateStat(const QString & statFullName, qreal x, qreal y, bool cacheOn)
269 {
270  std::vector<qreal> vx(1),vy(1);
271  vx[0] = x;
272  vy[0] = y;
273  updateStat(statFullName, vx, vy, cacheOn);
274 }
275 
276 void StatsToolBox::updateStat(const QString & statFullName, const std::vector<qreal> & x, const std::vector<qreal> & y, bool cacheOn)
277 {
278  // round qreal to max 2 numbers after the dot
279  //x = (qreal(int(100*x)))/100;
280  //y = (qreal(int(100*y)))/100;
281 
282  StatItem * item = _statBox->findChild<StatItem *>(statFullName);
283  if(item)
284  {
285  item->setCacheOn(cacheOn);
286  if(y.size() == 1 && x.size() == 1)
287  {
288  item->addValue(x[0], y[0]);
289  }
290  else if(y.size() == 1 && x.size() == 0)
291  {
292  item->addValue(y[0]);
293  }
294  else
295  {
296  item->setValues(x, y);
297  }
298  }
299  else
300  {
301  // statFullName format : "Grp/Name/unit"
302  QStringList list = statFullName.split('/');
303  QString grp;
304  QString name;
305  QString unit;
306  if(list.size() >= 3)
307  {
308  grp = list.at(0);
309  name = list.at(1);
310  unit = list.at(2);
311  for(int i=3; i<list.size(); ++i)
312  {
313  unit += "/" + list.at(i);
314  }
315  }
316  else if(list.size() == 2)
317  {
318  grp = list.at(0);
319  name = list.at(1);
320  }
321  else if(list.size() == 1)
322  {
323  name = list.at(0);
324  }
325  else
326  {
327  ULOGGER_WARN("A statistic has no name");
328  return;
329  }
330 
331  if(grp.isEmpty())
332  {
333  grp = tr("Global");
334  }
335 
336  int index = -1;
337  for(int i=0; i<_statBox->count(); ++i)
338  {
339  if(_statBox->itemText(i).compare(grp) == 0)
340  {
341  index = i;
342  break;
343  }
344  }
345 
346  if(index<0)
347  {
348  QWidget * newWidget = new QWidget(_statBox);
349  index = _statBox->addItem(newWidget, grp);
350  QVBoxLayout * layout = new QVBoxLayout(newWidget);
351  newWidget->setLayout(layout);
352  QGridLayout * grid = new QGridLayout();
353  grid->setVerticalSpacing(2);
354  grid->setColumnStretch(0, 1);
355  layout->addLayout(grid);
356  layout->addStretch();
357  }
358 
359  QVBoxLayout * layout = qobject_cast<QVBoxLayout *>(_statBox->widget(index)->layout());
360  if(!layout)
361  {
362  ULOGGER_ERROR("Layout is null ?!?");
363  return;
364  }
365  QGridLayout * grid = qobject_cast<QGridLayout *>(layout->itemAt(0)->layout());
366  if(!grid)
367  {
368  ULOGGER_ERROR("Layout is null ?!?");
369  return;
370  }
371 
372  item = new StatItem(name, cacheOn, x, y, unit, _plotMenu, grid, _statBox->widget(index));
373  item->setObjectName(statFullName);
374 
375  //layout->insertWidget(layout->count()-1, item);
376  connect(item, SIGNAL(plotRequested(const StatItem *, const QString &)), this, SLOT(plot(const StatItem *, const QString &)));
377  connect(this, SIGNAL(menuChanged(const QMenu *)), item, SLOT(updateMenu(const QMenu *)));
378  }
379 }
380 
381 void StatsToolBox::plot(const StatItem * stat, const QString & plotName)
382 {
383  QWidget * fig = _figures.value(plotName, (QWidget*)0);
384  UPlot * plot = 0;
385  if(fig)
386  {
387  plot = fig->findChild<UPlot *>(plotName);
388  }
389  if(plot)
390  {
391  // if not already in the plot
392  if(!plot->contains(stat->objectName()))
393  {
394  UPlotCurve * curve = new UPlotCurve(stat->objectName(), plot);
395  curve->setPen(plot->getRandomPenColored());
396  connect(stat, SIGNAL(valueAdded(qreal)), curve, SLOT(addValue(qreal)));
397  connect(stat, SIGNAL(valueAdded(qreal, qreal)), curve, SLOT(addValue(qreal, qreal)));
398  connect(stat, SIGNAL(valuesChanged(const std::vector<qreal> &, const std::vector<qreal> &)), curve, SLOT(setData(const std::vector<qreal> &, const std::vector<qreal> &)));
399  if(stat->value().compare("*") == 0)
400  {
401  plot->setMaxVisibleItems(0);
402  }
403  if(!stat->yValues().empty())
404  {
405  if(stat->xValues().size() == stat->yValues().size())
406  {
407  curve->setData(stat->xValues(),stat->yValues());
408  }
409  else
410  {
411  curve->setData(stat->yValues());
412  }
413  }
414  if(!plot->addCurve(curve))
415  {
416  ULOGGER_WARN("Already added to the figure");
417  }
418  Q_EMIT figuresSetupChanged();
419  }
420  else
421  {
422  ULOGGER_WARN("Already added to the figure");
423  }
424  plot->activateWindow();
425  }
426  else
427  {
428  //Create a new plot
429  QString id = tr("Figure 0");
430  if(_plotMenu->actions().size())
431  {
432  id = _plotMenu->actions().last()->text();
433  }
434  id.replace(tr("Figure "), "");
435  QString newPlotName = QString(tr("Figure %1")).arg(id.toInt()+1);
436  //Dock
437  QDialog * figure = new QDialog(0, Qt::Window);
438  _figures.insert(newPlotName, figure);
439  QHBoxLayout * hLayout = new QHBoxLayout(figure);
440  hLayout->setContentsMargins(0,0,0,0);
441  figure->setWindowTitle(newPlotName);
442  figure->setAttribute(Qt::WA_DeleteOnClose, true);
443  connect(figure, SIGNAL(destroyed(QObject*)), this, SLOT(figureDeleted(QObject*)));
444  //Plot
445  UPlot * newPlot = new UPlot(figure);
448  newPlot->setObjectName(newPlotName);
449  hLayout->addWidget(newPlot);
450  _plotMenu->addAction(newPlotName);
451  figure->setSizeGripEnabled(true);
452 
453  //Add a new curve linked to the statBox
454  UPlotCurve * curve = new UPlotCurve(stat->objectName(), newPlot);
455  curve->setPen(newPlot->getRandomPenColored());
456  connect(stat, SIGNAL(valueAdded(qreal)), curve, SLOT(addValue(qreal)));
457  connect(stat, SIGNAL(valueAdded(qreal, qreal)), curve, SLOT(addValue(qreal, qreal)));
458  connect(stat, SIGNAL(valuesChanged(const std::vector<qreal> &, const std::vector<qreal> &)), curve, SLOT(setData(const std::vector<qreal> &, const std::vector<qreal> &)));
459  if(stat->value().compare("*") == 0)
460  {
461  newPlot->setMaxVisibleItems(0);
462  }
463 
464  if(!stat->yValues().empty())
465  {
466  if(stat->xValues().size() == stat->yValues().size())
467  {
468  curve->setData(stat->xValues(),stat->yValues());
469  }
470  else
471  {
472  curve->setData(stat->yValues());
473  }
474  }
475 
476  if(!newPlot->addCurve(curve))
477  {
478  ULOGGER_ERROR("Not supposed to be here !?!");
479  delete curve;
480  }
481  figure->show();
482  Q_EMIT figuresSetupChanged();
483 
484  Q_EMIT menuChanged(_plotMenu);
485  }
486 }
487 
488 void StatsToolBox::figureDeleted(QObject * obj)
489 {
490  if(obj)
491  {
492  QWidget * plot = qobject_cast<QWidget*>(obj);
493  if(plot)
494  {
495  _figures.remove(plot->windowTitle());
496  QList<QAction*> actions = _plotMenu->actions();
497  for(int i=0; i<actions.size(); ++i)
498  {
499  if(actions.at(i)->text().compare(plot->windowTitle()) == 0)
500  {
501  _plotMenu->removeAction(actions.at(i));
502  delete actions[i];
503  Q_EMIT menuChanged(_plotMenu);
504  break;
505  }
506  }
507  Q_EMIT figuresSetupChanged();
508  }
509  else
510  {
511  UERROR("");
512  }
513  }
514  else
515  {
516  UERROR("");
517  }
518 }
519 
521 {
522  for (QMap<QString, QWidget*>::iterator i = _figures.begin(); i != _figures.end(); ++i)
523  {
524  QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
525  if (plots.size() == 1)
526  {
527  QStringList names = plots[0]->curveNames();
528  plots[0]->clearData();
529  }
530  else
531  {
532  UERROR("");
533  }
534  }
535  QList<StatItem*> items = _statBox->currentWidget()->findChildren<StatItem*>();
536  for (int i = 0; i<items.size(); ++i)
537  {
538  items[i]->clearCache();
539  }
540 }
541 
542 void StatsToolBox::contextMenuEvent(QContextMenuEvent * event)
543 {
544  QMenu topMenu(this);
545  QMenu * menu = topMenu.addMenu(tr("Add all statistics from tab \"%1\" to...").arg(_statBox->itemText(_statBox->currentIndex())));
546  QList<QAction* > actions = _plotMenu->actions();
547  menu->addActions(actions);
548  QAction * aClearFigures = topMenu.addAction(tr("Clear all figures"));
549  QAction * action = topMenu.exec(event->globalPos());
550  QString plotName;
551  if(action)
552  {
553  if(action == aClearFigures)
554  {
555  this->clear();
556  }
557  else
558  {
559  for(int i=0; i<actions.size(); ++i)
560  {
561  if(actions.at(i) == action)
562  {
563  plotName = actions.at(i)->text();
564  break;
565  }
566  }
567  }
568  }
569 
570  if(!plotName.isEmpty())
571  {
572  QList<StatItem*> items = _statBox->currentWidget()->findChildren<StatItem*>();
573  for(int i=0; i<items.size(); ++i)
574  {
575  this->plot(items.at(i), plotName);
576  if(plotName.compare(tr("<New figure>")) == 0)
577  {
578  plotName = _plotMenu->actions().last()->text();
579  }
580  }
581  }
582 }
583 
584 void StatsToolBox::getFiguresSetup(QList<int> & curvesPerFigure, QStringList & curveNames)
585 {
586  curvesPerFigure.clear();
587  curveNames.clear();
588  for(QMap<QString, QWidget*>::iterator i=_figures.begin(); i!=_figures.end(); ++i)
589  {
590  QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
591  if(plots.size() == 1)
592  {
593  QStringList names = plots[0]->curveNames();
594  curvesPerFigure.append(names.size());
595  curveNames.append(names);
596  }
597  else
598  {
599  UERROR("");
600  }
601  }
602 }
603 void StatsToolBox::addCurve(const QString & name, bool newFigure, bool cacheOn)
604 {
605  StatItem * item = _statBox->findChild<StatItem *>(name);
606  if(!item)
607  {
608  this->updateStat(name, cacheOn);
609  item = _statBox->findChild<StatItem *>(name);
610  }
611 
612  if(item)
613  {
614  if(newFigure)
615  {
616  this->plot(item, "");
617  }
618  else
619  {
620  this->plot(item, _plotMenu->actions().last()->text());
621  }
622  }
623  else
624  {
625  ULOGGER_ERROR("Not supposed to be here...");
626  }
627 }
628 
629 void StatsToolBox::setWorkingDirectory(const QString & workingDirectory)
630 {
631  if(QDir(workingDirectory).exists())
632  {
633  _workingDirectory = workingDirectory;
634  for(QMap<QString, QWidget*>::iterator i=_figures.begin(); i!=_figures.end(); ++i)
635  {
636  QList<UPlot *> plots = i.value()->findChildren<UPlot *>();
637  if(plots.size() == 1)
638  {
640  }
641  else
642  {
643  UERROR("");
644  }
645  }
646  }
647  else
648  {
649  UWARN("The directory \"%s\" doesn't exist, using \"%s\" instead...",
650  workingDirectory.toStdString().c_str(),
651  _workingDirectory.toStdString().c_str());
652  }
653 }
654 
655 }
void updateStat(const QString &statFullName, bool cacheOn)
void getFiguresSetup(QList< int > &curvesPerFigure, QStringList &curveNames)
void valuesChanged(const std::vector< qreal > &, const std::vector< qreal > &)
void updateMenu(const QMenu *menu)
StatsToolBox(QWidget *parent)
void plotRequested(const StatItem *, const QString &)
QMap< QString, QWidget * > _figures
Definition: StatsToolBox.h:128
void setWorkingDirectory(const QString &workingDirectory)
GLM_FUNC_DECL genType::row_type row(genType const &m, length_t const &index)
QStringList curveNames()
Definition: UPlot.cpp:2166
void addValue(qreal y)
void setCacheOn(bool on)
void setPen(const QPen &pen)
Definition: UPlot.cpp:661
void setData(QVector< UPlotItem *> &data)
Definition: UPlot.cpp:878
void setupUi(QGridLayout *grid)
std::vector< qreal > _x
Definition: StatsToolBox.h:83
Definition: UPlot.h:489
virtual void contextMenuEvent(QContextMenuEvent *event)
void plot(const StatItem *stat, const QString &plotName=QString())
QPen getRandomPenColored()
Definition: UPlot.cpp:2191
#define ULOGGER_DEBUG(...)
Definition: ULogger.h:53
void setValues(const std::vector< qreal > &x, const std::vector< qreal > &y)
void menuChanged(const QMenu *)
UPlotCurve * addCurve(const QString &curveName, const QColor &color=QColor())
Definition: UPlot.cpp:2102
string name
void addCurve(const QString &name, bool newFigure=true, bool cacheOn=false)
QString value() const
void addValue(int pos, float val, float *vals, T *point, T *points, int n)
bool contains(const QString &curveName)
Definition: UPlot.cpp:2179
std::vector< qreal > yValues() const
Definition: StatsToolBox.h:56
StatItem(const QString &name, bool cacheOn, const std::vector< qreal > &x, const std::vector< qreal > &y, const QString &unit=QString(), const QMenu *menu=0, QGridLayout *grid=0, QWidget *parent=0)
#define UERROR(...)
void setCacheOn(bool on)
QToolButton * _button
Definition: StatsToolBox.h:76
#define ULOGGER_WARN(...)
Definition: ULogger.h:55
ULogger class and convenient macros.
#define UWARN(...)
void setMaxVisibleItems(int maxVisibleItems)
Definition: UPlot.cpp:3131
std::vector< qreal > xValues() const
Definition: StatsToolBox.h:55
void figureDeleted(QObject *obj)
std::vector< qreal > _y
Definition: StatsToolBox.h:84
#define ULOGGER_ERROR(...)
Definition: ULogger.h:56
void valueAdded(qreal)
void setWorkingDirectory(const QString &workingDirectory)
Definition: UPlot.cpp:2894


rtabmap
Author(s): Mathieu Labbe
autogenerated on Mon Jan 23 2023 03:38:57