UPlot.cpp
Go to the documentation of this file.
1 // Taken from UtiLite library r186 [www.utilite.googlecode.com]
2 
3 /*
4 * utilite is a cross-platform library with
5 * useful utilities for fast and small developing.
6 * Copyright (C) 2010 Mathieu Labbe
7 *
8 * utilite is free library: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * utilite is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "utilite/UPlot.h"
23 //#include "utilite/ULogger.h"
24 
25 #include <QGraphicsScene>
26 #include <QGraphicsView>
27 #include <QGraphicsItem>
28 #include <QHBoxLayout>
29 #include <QFormLayout>
30 #include <QResizeEvent>
31 #include <QtCore/QTime>
32 #include <QtCore/QTimer>
33 #include <QtCore/QFileInfo>
34 #include <QPushButton>
35 #include <QToolButton>
36 #include <QLabel>
37 #include <QMenu>
38 #include <QInputDialog>
39 #include <QMessageBox>
40 #include <QFileDialog>
41 #include <QClipboard>
42 #include <QApplication>
43 #include <QPrinter>
44 #ifdef QT_SVG_LIB
45 #include <QtSvg/QSvgGenerator>
46 #endif
47 #include <cmath>
48 
49 #define PRINT_DEBUG 0
50 #define ULOGGER_ERROR(A, ...)
51 #define ULOGGER_WARN(A, ...)
52 
53 UPlotItem::UPlotItem(qreal dataX, qreal dataY, qreal width) :
54  QGraphicsEllipseItem(0, 0, width, width, 0),
55  _previousItem(0),
56  _nextItem(0)
57 {
58  _data.setX(dataX);
59  _data.setY(dataY);
60  this->setZValue(1);
61  this->setAcceptHoverEvents(true);
62  _text = new QGraphicsTextItem(this);
63  _text->setPlainText(QString("(%1,%2)").arg(_data.x()).arg(_data.y()));
64  _text->setVisible(false);
65  this->setFlag(QGraphicsItem::ItemIsFocusable, true);
66 }
67 
68 UPlotItem::UPlotItem(const QPointF & data, qreal width) :
69  QGraphicsEllipseItem(0, 0, width, width, 0),
70  _data(data),
71  _previousItem(0),
72  _nextItem(0)
73 {
74  this->setZValue(1);
75  this->setAcceptHoverEvents(true);
76  _text = new QGraphicsTextItem(this);
77  _text->setPlainText(QString("(%1,%2)").arg(_data.x()).arg(_data.y()));
78  _text->setVisible(false);
79  this->setFlag(QGraphicsItem::ItemIsFocusable, true);
80 }
81 
83 {
85  {
88  }
89  else if(_previousItem)
90  {
92  }
93  else if(_nextItem)
94  {
96  }
97 }
98 
99 void UPlotItem::setData(const QPointF & data)
100 {
101  _data = data;
102 }
103 
105 {
106  if(_nextItem != nextItem)
107  {
109  if(nextItem)
110  {
111  nextItem->setPreviousItem(this);
112  }
113  }
114 }
115 
117 {
118  if(_previousItem != previousItem)
119  {
121  if(previousItem)
122  {
123  previousItem->setNextItem(this);
124  }
125  }
126 }
127 
129 {
130  _text->setPlainText(QString("(%1,%2)").arg(_data.x()).arg(_data.y()));
131  if(shown)
132  {
133  this->setPen(QPen(Qt::black, 2));
134  if(this->scene())
135  {
136  QRectF rect = this->scene()->sceneRect();
137  QPointF p = this->pos();
138  QRectF br = _text->boundingRect();
139 
140  // Make sure the text is always in the scene
141  if(p.x() - br.width() < 0)
142  {
143  p.setX(0);
144  }
145  else if(p.x() + br.width() > rect.width())
146  {
147  p.setX(rect.width() - br.width());
148  }
149  else
150  {
151  p.setX(p.x() - br.width());
152  }
153 
154  if(p.y() - br.height() < 0)
155  {
156  p.setY(0);
157  }
158  else
159  {
160  p.setY(p.y() - br.height());
161  }
162 
163  _text->setPos(this->mapFromScene(p));
164  }
165 
166  _text->setVisible(true);
167  }
168  else
169  {
170  this->setPen(QPen(Qt::black, 1));
171  _text->setVisible(false);
172  }
173 }
174 
175 void UPlotItem::hoverEnterEvent(QGraphicsSceneHoverEvent * event)
176 {
177  QGraphicsScene * scene = this->scene();
178  if(scene && scene->focusItem() == 0)
179  {
180  this->showDescription(true);
181  }
182  else
183  {
184  this->setPen(QPen(Qt::black, 2));
185  }
186  QGraphicsEllipseItem::hoverEnterEvent(event);
187 }
188 
189 void UPlotItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * event)
190 {
191  if(!this->hasFocus())
192  {
193  this->showDescription(false);
194  }
195  QGraphicsEllipseItem::hoverEnterEvent(event);
196 }
197 
198 void UPlotItem::focusInEvent(QFocusEvent * event)
199 {
200  this->showDescription(true);
201  QGraphicsEllipseItem::focusInEvent(event);
202 }
203 
204 void UPlotItem::focusOutEvent(QFocusEvent * event)
205 {
206  this->showDescription(false);
207  QGraphicsEllipseItem::focusOutEvent(event);
208 }
209 
210 void UPlotItem::keyReleaseEvent(QKeyEvent * keyEvent)
211 {
212  //Get the next/previous visible item
213  if(keyEvent->key() == Qt::Key_Right)
214  {
215  UPlotItem * next = _nextItem;
216  while(next && !next->isVisible())
217  {
218  next = next->nextItem();
219  }
220  if(next && next->isVisible())
221  {
222  this->clearFocus();
223  next->setFocus();
224  }
225  }
226  else if(keyEvent->key() == Qt::Key_Left)
227  {
228  UPlotItem * previous = _previousItem;
229  while(previous && !previous->isVisible())
230  {
231  previous = previous->previousItem();
232  }
233  if(previous && previous->isVisible())
234  {
235  this->clearFocus();
236  previous->setFocus();
237  }
238  }
239  QGraphicsEllipseItem::keyReleaseEvent(keyEvent);
240 }
241 
242 
243 
244 
245 
246 UPlotCurve::UPlotCurve(const QString & name, QObject * parent) :
247  QObject(parent),
248  _plot(0),
249  _name(name),
250  _xIncrement(1),
251  _xStart(0),
252  _visible(true),
253  _valuesShown(false)
254 {
255 }
256 
257 UPlotCurve::UPlotCurve(const QString & name, QVector<UPlotItem *> data, QObject * parent) :
258  QObject(parent),
259  _plot(0),
260  _name(name),
261  _xIncrement(1),
262  _xStart(0),
263  _visible(true),
264  _valuesShown(false)
265 {
266  this->setData(data);
267 }
268 
269 UPlotCurve::UPlotCurve(const QString & name, const QVector<float> & x, const QVector<float> & y, QObject * parent) :
270  QObject(parent),
271  _plot(0),
272  _name(name),
273  _xIncrement(1),
274  _xStart(0),
275  _visible(true),
276  _valuesShown(false)
277 {
278  this->setData(x, y);
279 }
280 
282 {
283  if(_plot)
284  {
285  _plot->removeCurve(this);
286  }
287 #if PRINT_DEBUG
288  ULOGGER_DEBUG("%s", this->name().toStdString().c_str());
289 #endif
290  this->clear();
291 }
292 
294 {
295  if(!plot || plot == _plot)
296  {
297  return;
298  }
299  if(_plot)
300  {
301  _plot->removeCurve(this);
302  }
303  _plot = plot;
304  for(int i=0; i<_items.size(); ++i)
305  {
306  _plot->addItem(_items.at(i));
307  }
308 }
309 
311 {
312 #if PRINT_DEBUG
313  ULOGGER_DEBUG("curve=\"%s\" from plot=\"%s\"", this->objectName().toStdString().c_str(), plot?plot->objectName().toStdString().c_str():"");
314 #endif
315  if(plot && _plot == plot)
316  {
317  _plot = 0;
318  for(int i=0; i<_items.size(); ++i)
319  {
320  if(_items.at(i)->scene())
321  {
322  _items.at(i)->scene()->removeItem(_items.at(i));
323  }
324  }
325  }
326 }
327 
329 {
330  float x,y;
331  const UPlotItem * item;
332  if(!_items.size())
333  {
334  _minMax = QVector<float>();
335  }
336  else
337  {
338  _minMax = QVector<float>(4);
339  }
340  for(int i=0; i<_items.size(); ++i)
341  {
342  item = qgraphicsitem_cast<const UPlotItem *>(_items.at(i));
343  if(item)
344  {
345  x = item->data().x();
346  y = item->data().y();
347  if(i==0)
348  {
349  _minMax[0] = x;
350  _minMax[1] = x;
351  _minMax[2] = y;
352  _minMax[3] = y;
353  }
354  else
355  {
356  if(x<_minMax[0]) _minMax[0] = x;
357  if(x>_minMax[1]) _minMax[1] = x;
358  if(y<_minMax[2]) _minMax[2] = y;
359  if(y>_minMax[3]) _minMax[3] = y;
360  }
361  }
362  }
363 }
364 
366 {
367  // add item
368  if(data)
369  {
370  float x = data->data().x();
371  float y = data->data().y();
372  if(_minMax.size() != 4)
373  {
374  _minMax = QVector<float>(4);
375  }
376  if(_items.size())
377  {
378  data->setPreviousItem((UPlotItem *)_items.last());
379 
380  QGraphicsLineItem * line = new QGraphicsLineItem();
381  line->setPen(_pen);
382  line->setVisible(false);
383  _items.append(line);
384  if(_plot)
385  {
386  _plot->addItem(line);
387  }
388 
389  //Update min/max
390  if(x<_minMax[0]) _minMax[0] = x;
391  if(x>_minMax[1]) _minMax[1] = x;
392  if(y<_minMax[2]) _minMax[2] = y;
393  if(y>_minMax[3]) _minMax[3] = y;
394  }
395  else
396  {
397  _minMax[0] = x;
398  _minMax[1] = x;
399  _minMax[2] = y;
400  _minMax[3] = y;
401  }
402  _items.append(data);
403  data->setVisible(false);
404  //data->showDescription(_valuesShown);
405  if(_plot)
406  {
407  _plot->addItem(_items.last());
408  }
409  }
410  else
411  {
412  ULOGGER_ERROR("Data is null ?!?");
413  }
414 }
415 
417 {
418  // add item
419  if(data)
420  {
421  this->_addValue(data);
422  Q_EMIT dataChanged(this);
423  }
424 }
425 
426 void UPlotCurve::addValue(float x, float y)
427 {
428  float width = 2; // TODO warn : hard coded value!
429  this->addValue(new UPlotItem(x,y,width));
430 }
431 
432 void UPlotCurve::addValue(float y)
433 {
434  float x = 0;
435  if(_items.size())
436  {
437  UPlotItem * lastItem = (UPlotItem *)_items.last();
438  x = lastItem->data().x() + _xIncrement;
439  }
440  else
441  {
442  x = _xStart;
443  }
444  this->addValue(x,y);
445 }
446 
447 void UPlotCurve::addValue(const QString & value)
448 {
449  bool ok;
450  float v = value.toFloat(&ok);
451  if(ok)
452  {
453  this->addValue(v);
454  }
455  else
456  {
457  ULOGGER_ERROR("Value not valid, must be a number, received %s", value.toStdString().c_str());
458  }
459 }
460 
461 void UPlotCurve::addValues(QVector<UPlotItem *> & data)
462 {
463  for(int i=0; i<data.size(); ++i)
464  {
465  this->_addValue(data.at(i));
466  }
467  Q_EMIT dataChanged(this);
468 }
469 
470 void UPlotCurve::addValues(const QVector<float> & xs, const QVector<float> & ys)
471 {
472  float width = 2; // TODO warn : hard coded value!
473  for(int i=0; i<xs.size() && i<ys.size(); ++i)
474  {
475  this->_addValue(new UPlotItem(xs.at(i),ys.at(i),width));
476  }
477  Q_EMIT dataChanged(this);
478 }
479 
480 void UPlotCurve::addValues(const QVector<float> & ys)
481 {
482  float x = 0;
483  float width = 2; // TODO warn : hard coded value!
484  for(int i=0; i<ys.size(); ++i)
485  {
486  if(_items.size())
487  {
488  UPlotItem * lastItem = (UPlotItem *)_items.last();
489  x = lastItem->data().x() + _xIncrement;
490  }
491  else
492  {
493  x = _xStart;
494  }
495  this->_addValue(new UPlotItem(x,ys.at(i),width));
496  }
497  Q_EMIT dataChanged(this);
498 }
499 
500 void UPlotCurve::addValues(const QVector<int> & ys)
501 {
502  float x = 0;
503  float width = 2; // TODO warn : hard coded value!
504  for(int i=0; i<ys.size(); ++i)
505  {
506  if(_items.size())
507  {
508  UPlotItem * lastItem = (UPlotItem *)_items.last();
509  x = lastItem->data().x() + _xIncrement;
510  }
511  else
512  {
513  x = _xStart;
514  }
515  this->_addValue(new UPlotItem(x,ys.at(i),width));
516  }
517  Q_EMIT dataChanged(this);
518 }
519 
520 void UPlotCurve::addValues(const std::vector<int> & ys)
521 {
522  float x = 0;
523  float width = 2; // TODO warn : hard coded value!
524  for(unsigned int i=0; i<ys.size(); ++i)
525  {
526  if(_items.size())
527  {
528  UPlotItem * lastItem = (UPlotItem *)_items.last();
529  x = lastItem->data().x() + _xIncrement;
530  }
531  else
532  {
533  x = _xStart;
534  }
535  this->_addValue(new UPlotItem(x,ys.at(i),width));
536  }
537  Q_EMIT dataChanged(this);
538 }
539 
540 void UPlotCurve::addValues(const std::vector<float> & ys)
541 {
542  float x = 0;
543  float width = 2; // TODO warn : hard coded value!
544  for(unsigned int i=0; i<ys.size(); ++i)
545  {
546  if(_items.size())
547  {
548  UPlotItem * lastItem = (UPlotItem *)_items.last();
549  x = lastItem->data().x() + _xIncrement;
550  }
551  else
552  {
553  x = _xStart;
554  }
555  this->_addValue(new UPlotItem(x,ys.at(i),width));
556  }
557  Q_EMIT dataChanged(this);
558 }
559 
561 {
562  if(index >= 0 && index < _items.size())
563  {
564  if(index!=0)
565  {
566  index-=1;
567  delete _items.takeAt(index); // the line
568  }
569  else if(_items.size()>1)
570  {
571  delete _items.takeAt(index+1); // the line
572  }
573  UPlotItem * item = (UPlotItem *)_items.takeAt(index); // the plot item
574  //Update min/max
575  if(_minMax.size() == 4)
576  {
577  if(item->data().x() == _minMax[0] || item->data().x() == _minMax[1] ||
578  item->data().y() == _minMax[2] || item->data().y() == _minMax[3])
579  {
580  if(_items.size())
581  {
582  UPlotItem * tmp = (UPlotItem *)_items.at(0);
583  float x = tmp->data().x();
584  float y = tmp->data().y();
585  _minMax[0]=x;
586  _minMax[1]=x;
587  _minMax[2]=y;
588  _minMax[3]=y;
589  for(int i = 2; i<_items.size(); i+=2)
590  {
591  tmp = (UPlotItem*)_items.at(i);
592  x = tmp->data().x();
593  y = tmp->data().y();
594  if(x<_minMax[0]) _minMax[0] = x;
595  if(x>_minMax[1]) _minMax[1] = x;
596  if(y<_minMax[2]) _minMax[2] = y;
597  if(y>_minMax[3]) _minMax[3] = y;
598  }
599  }
600  else
601  {
602  _minMax = QVector<float>();
603  }
604  }
605  }
606  delete item;
607  }
608 
609  return index;
610 }
611 
612 void UPlotCurve::removeItem(UPlotItem * item) // ownership is transfered to the caller
613 {
614  for(int i=0; i<_items.size(); ++i)
615  {
616  if(_items.at(i) == item)
617  {
618  if(i!=0)
619  {
620  i-=1;
621  delete _items[i];
622  _items.removeAt(i);
623  }
624  else if(_items.size()>1)
625  {
626  delete _items[i+1];
627  _items.removeAt(i+1);
628  }
629  item->scene()->removeItem(item);
630  _items.removeAt(i);
631  break;
632  }
633  }
634 }
635 
637 {
638 #if PRINT_DEBUG
639  ULOGGER_DEBUG("%s", this->name().toStdString().c_str());
640 #endif
641  qDeleteAll(_items);
642  _items.clear();
643 }
644 
645 void UPlotCurve::setPen(const QPen & pen)
646 {
647  _pen = pen;
648  for(int i=1; i<_items.size(); i+=2)
649  {
650  ((QGraphicsLineItem*) _items.at(i))->setPen(_pen);
651  }
652 }
653 
654 void UPlotCurve::setBrush(const QBrush & brush)
655 {
656  _brush = brush;
657  ULOGGER_WARN("Not used...");
658 }
659 
660 void UPlotCurve::update(float scaleX, float scaleY, float offsetX, float offsetY, float xDir, float yDir, bool allDataKept)
661 {
662  //ULOGGER_DEBUG("scaleX=%f, scaleY=%f, offsetX=%f, offsetY=%f, xDir=%d, yDir=%d, _plot->scene()->width()=%f, _plot->scene()->height=%f", scaleX, scaleY, offsetX, offsetY, xDir, yDir,_plot->scene()->width(),_plot->scene()->height());
663  //make sure direction values are 1 or -1
664  xDir<0?xDir=-1:xDir=1;
665  yDir<0?yDir=-1:yDir=1;
666 
667  bool hide = false;
668  for(int i=_items.size()-1; i>=0; --i)
669  {
670  if(i%2 == 0)
671  {
672  UPlotItem * item = (UPlotItem *)_items.at(i);
673  if(hide)
674  {
675  if(allDataKept)
676  {
677  // if not visible, stop looping... all other items are normally already hided
678  if(!item->isVisible())
679  {
680  break;
681  }
682  item->setVisible(false);
683  }
684  else
685  {
686  //remove the item with his line
687  i = this->removeItem(i);
688  }
689  }
690  else
691  {
692  QPointF newPos(((xDir*item->data().x()+offsetX)*scaleX-item->rect().width()/2.0f),
693  ((yDir*item->data().y()+offsetY)*scaleY-item->rect().width()/2.0f));
694  if(!item->isVisible())
695  {
696  item->setVisible(true);
697  }
698  item->setPos(newPos);
699  }
700  }
701  else
702  {
703  if(hide)
704  {
705  _items.at(i)->setVisible(false);
706  }
707  else
708  {
709  UPlotItem * from = (UPlotItem *)_items.at(i-1);
710  UPlotItem * to = (UPlotItem *)_items.at(i+1);
711  QGraphicsLineItem * lineItem = (QGraphicsLineItem *)_items.at(i);
712  lineItem->setLine((xDir*from->data().x()+offsetX)*scaleX,
713  (yDir*from->data().y()+offsetY)*scaleY,
714  (xDir*to->data().x()+offsetX)*scaleX,
715  (yDir*to->data().y()+offsetY)*scaleY);
716  if(!lineItem->isVisible())
717  {
718  lineItem->setVisible(true);
719  }
720  //Don't update not visible items
721  // (Detect also if the curve goes forward or backward)
722  QLineF line = lineItem->line();
723  if((line.x1() <= line.x2() && line.x2() < 0-((line.x2() - line.x1()))) ||
724  (line.x1() > line.x2() && line.x2() > lineItem->scene()->sceneRect().width() + ((line.x1() - line.x2()))))
725  {
726  hide = true;
727  }
728 
729  }
730  }
731  }
732 
733 }
734 
735 void UPlotCurve::draw(QPainter * painter)
736 {
737  if(painter)
738  {
739  for(int i=_items.size()-1; i>=0 && _items.at(i)->isVisible(); i-=2)
740  {
741  //plotItem
742  const UPlotItem * item = (const UPlotItem *)_items.at(i);
743  int x = (int)item->x();
744  if(x<0)
745  {
746  break;
747  }
748 
749  // draw line in first
750  if(i-1>=0)
751  {
752  painter->save();
753  painter->setPen(this->pen());
754  painter->setBrush(this->brush());
755  //lineItem
756  const QGraphicsLineItem * item = (const QGraphicsLineItem *)_items.at(i-1);
757  QLineF line = item->line();
758  int x = (int)line.p1().x();
759  if(x<0)
760  {
761  line.setP1(QPoint(0, line.p1().y()));
762  }
763  painter->drawLine(line);
764  painter->restore();
765  }
766 
767  painter->drawEllipse(item->pos()+QPointF(item->rect().width()/2, item->rect().height()/2), (int)item->rect().width()/2, (int)item->rect().height()/2);
768  }
769  }
770 }
771 
773 {
774  return _items.size();
775 }
776 
777 QPointF UPlotCurve::getItemData(int index)
778 {
779  QPointF data;
780  //make sure the index point to a PlotItem {PlotItem, line, PlotItem, line...}
781  if(index>=0 && index < _items.size() && index % 2 == 0 )
782  {
783  data = ((UPlotItem*)_items.at(index))->data();
784  }
785  else
786  {
787  ULOGGER_ERROR("Wrong index, not pointing on a PlotItem");
788  }
789  return data;
790 }
791 
792 void UPlotCurve::setVisible(bool visible)
793 {
794  _visible = visible;
795  for(int i=0; i<_items.size(); ++i)
796  {
797  _items.at(i)->setVisible(visible);
798  }
799 }
800 
801 void UPlotCurve::setXIncrement(float increment)
802 {
803  _xIncrement = increment;
804 }
805 
806 void UPlotCurve::setXStart(float val)
807 {
808  _xStart = val;
809 }
810 
811 void UPlotCurve::setData(QVector<UPlotItem*> & data)
812 {
813  this->clear();
814  for(int i = 0; i<data.size(); ++i)
815  {
816  this->addValue(data[i]);
817  }
818 }
819 
820 void UPlotCurve::setData(const QVector<float> & x, const QVector<float> & y)
821 {
822  if(x.size() == y.size())
823  {
824  //match the size of the current data
825  int margin = int((_items.size()+1)/2) - x.size();
826  while(margin < 0)
827  {
828  UPlotItem * newItem = new UPlotItem(0, 0, 2);
829  this->_addValue(newItem);
830  ++margin;
831  }
832  while(margin > 0)
833  {
834  this->removeItem(0);
835  --margin;
836  }
837 
838  // update values
839  int index = 0;
840  QVector<float>::const_iterator i=x.begin();
841  QVector<float>::const_iterator j=y.begin();
842  for(; i!=x.end() && j!=y.end(); ++i, ++j, index+=2)
843  {
844  ((UPlotItem*)_items[index])->setData(QPointF(*i, *j));
845  }
846 
847  //reset minMax, this will force the plot to update the axes
848  this->updateMinMax();
849  Q_EMIT dataChanged(this);
850  }
851  else
852  {
853  ULOGGER_ERROR("Data vectors have not the same size.");
854  }
855 }
856 
857 void UPlotCurve::setData(const std::vector<float> & x, const std::vector<float> & y)
858 {
859  if(x.size() == y.size())
860  {
861  //match the size of the current data
862  int margin = int((_items.size()+1)/2) - int(x.size());
863  while(margin < 0)
864  {
865  UPlotItem * newItem = new UPlotItem(0, 0, 2);
866  this->_addValue(newItem);
867  ++margin;
868  }
869  while(margin > 0)
870  {
871  this->removeItem(0);
872  --margin;
873  }
874 
875  // update values
876  int index = 0;
877  std::vector<float>::const_iterator i=x.begin();
878  std::vector<float>::const_iterator j=y.begin();
879  for(; i!=x.end() && j!=y.end(); ++i, ++j, index+=2)
880  {
881  ((UPlotItem*)_items[index])->setData(QPointF(*i, *j));
882  }
883 
884  //reset minMax, this will force the plot to update the axes
885  this->updateMinMax();
886  Q_EMIT dataChanged(this);
887  }
888  else
889  {
890  ULOGGER_ERROR("Data vectors have not the same size.");
891  }
892 }
893 
894 void UPlotCurve::setData(const QVector<float> & y)
895 {
896  this->setData(y.toStdVector());
897 }
898 
899 void UPlotCurve::setData(const std::vector<float> & y)
900 {
901  //match the size of the current data
902  int margin = int((_items.size()+1)/2) - int(y.size());
903  while(margin < 0)
904  {
905  UPlotItem * newItem = new UPlotItem(0, 0, 2);
906  this->_addValue(newItem);
907  ++margin;
908  }
909  while(margin > 0)
910  {
911  this->removeItem(0);
912  --margin;
913  }
914 
915  // update values
916  int index = 0;
917  float x = 0;
918  std::vector<float>::const_iterator j=y.begin();
919  for(; j!=y.end(); ++j, index+=2)
920  {
921  ((UPlotItem*)_items[index])->setData(QPointF(x++, *j));
922  }
923 
924  //reset minMax, this will force the plot to update the axes
925  this->updateMinMax();
926  Q_EMIT dataChanged(this);
927 }
928 
929 void UPlotCurve::getData(QVector<float> & x, QVector<float> & y) const
930 {
931  x.clear();
932  y.clear();
933  if(_items.size())
934  {
935  x.resize((_items.size()-1)/2+1);
936  y.resize(x.size());
937  int j=0;
938  for(int i=0; i<_items.size(); i+=2)
939  {
940  x[j] = ((UPlotItem*)_items.at(i))->data().x();
941  y[j++] = ((UPlotItem*)_items.at(i))->data().y();
942  }
943  }
944 }
945 
946 
947 
948 
949 
950 UPlotCurveThreshold::UPlotCurveThreshold(const QString & name, float thesholdValue, Qt::Orientation orientation, QObject * parent) :
951  UPlotCurve(name, parent),
952  _orientation(orientation)
953 {
954  if(_orientation == Qt::Horizontal)
955  {
956  this->addValue(0, thesholdValue);
957  this->addValue(1, thesholdValue);
958  }
959  else
960  {
961  this->addValue(thesholdValue, 0);
962  this->addValue(thesholdValue, 1);
963  }
964 }
965 
967 {
968 
969 }
970 
972 {
973 #if PRINT_DEBUG
974  ULOGGER_DEBUG("%f", threshold);
975 #endif
976  if(_items.size() == 3)
977  {
978  UPlotItem * item = 0;
979  if(_orientation == Qt::Horizontal)
980  {
981  item = (UPlotItem*)_items.at(0);
982  item->setData(QPointF(item->data().x(), threshold));
983  item = (UPlotItem*)_items.at(2);
984  item->setData(QPointF(item->data().x(), threshold));
985  }
986  else
987  {
988  item = (UPlotItem*)_items.at(0);
989  item->setData(QPointF(threshold, item->data().y()));
990  item = (UPlotItem*)_items.at(2);
991  item->setData(QPointF(threshold, item->data().y()));
992  }
993  }
994  else
995  {
996  ULOGGER_ERROR("A threshold must has only 3 items (1 PlotItem + 1 QGraphicsLineItem + 1 PlotItem)");
997  }
998 }
999 
1000 void UPlotCurveThreshold::setOrientation(Qt::Orientation orientation)
1001 {
1002  if(_orientation != orientation)
1003  {
1004  _orientation = orientation;
1005  if(_items.size() == 3)
1006  {
1007  UPlotItem * item = 0;
1008  item = (UPlotItem*)_items.at(0);
1009  item->setData(QPointF(item->data().y(), item->data().x()));
1010  item = (UPlotItem*)_items.at(2);
1011  item->setData(QPointF(item->data().y(), item->data().x()));
1012  }
1013  else
1014  {
1015  ULOGGER_ERROR("A threshold must has only 3 items (1 PlotItem + 1 QGraphicsLineItem + 1 PlotItem)");
1016  }
1017  }
1018 }
1019 
1020 void UPlotCurveThreshold::update(float scaleX, float scaleY, float offsetX, float offsetY, float xDir, float yDir, bool allDataKept)
1021 {
1022  if(_items.size() == 3)
1023  {
1024  if(_plot)
1025  {
1026  UPlotItem * item = 0;
1027  if(_orientation == Qt::Horizontal)
1028  {
1029  //(xDir*item->data().x()+offsetX)*scaleX
1030  item = (UPlotItem*)_items.at(0);
1031  item->setData(QPointF(-offsetX/xDir, item->data().y()));
1032  item = (UPlotItem*)_items.at(2);
1033  item->setData(QPointF( (_plot->sceneRect().width()/scaleX-offsetX)/xDir, item->data().y()));
1034  }
1035  else
1036  {
1037  item = (UPlotItem*)_items.at(0);
1038  item->setData(QPointF(item->data().x(), -offsetY/yDir));
1039  item = (UPlotItem*)_items.at(2);
1040  item->setData(QPointF(item->data().x(), (_plot->sceneRect().height()/scaleY-offsetY)/yDir));
1041  }
1042  this->updateMinMax();
1043  }
1044  }
1045  else
1046  {
1047  ULOGGER_ERROR("A threshold must has only 3 items (1 PlotItem + 1 QGraphicsLineItem + 1 PlotItem)");
1048  }
1049  UPlotCurve::update(scaleX, scaleY, offsetX, offsetY, xDir, yDir, allDataKept);
1050 }
1051 
1052 
1053 
1054 
1055 
1056 
1057 
1058 UPlotAxis::UPlotAxis(Qt::Orientation orientation, float min, float max, QWidget * parent) :
1059  QWidget(parent),
1060  _orientation(orientation),
1061  _reversed(false),
1062  _gradMaxDigits(4),
1063  _border(0)
1064 {
1065  if(_orientation == Qt::Vertical)
1066  {
1067  _reversed = true; // default bottom->up
1068  }
1069 #ifdef WIN32
1070  this->setMinimumSize(15, 25);
1071 #else
1072  this->setMinimumSize(15, 25);
1073 #endif
1074  this->setAxis(min, max); // this initialize all attributes
1075 }
1076 
1078 {
1079 #if PRINT_DEBUG
1080  ULOGGER_DEBUG("");
1081 #endif
1082 }
1083 
1084 // Vertical :bottom->up, horizontal :right->left
1085 void UPlotAxis::setReversed(bool reversed)
1086 {
1087  if(_reversed != reversed)
1088  {
1089  float min = _min;
1090  _min = _max;
1091  _max = min;
1092  }
1093  _reversed = reversed;
1094 }
1095 
1096 void UPlotAxis::setAxis(float & min, float & max)
1097 {
1098  int borderMin = 0;
1099  int borderMax = 0;
1100  if(_orientation == Qt::Vertical)
1101  {
1102  borderMin = borderMax = this->fontMetrics().height()/2;
1103  }
1104  else
1105  {
1106  borderMin = this->fontMetrics().width(QString::number(_min,'g',_gradMaxDigits))/2;
1107  borderMax = this->fontMetrics().width(QString::number(_max,'g',_gradMaxDigits))/2;
1108  }
1109  int border = borderMin>borderMax?borderMin:borderMax;
1110  int borderDelta;
1111  int length;
1112  if(_orientation == Qt::Vertical)
1113  {
1114  length = (this->height()-border*2);
1115  }
1116  else
1117  {
1118  length = (this->width()-border*2);
1119  }
1120 
1121  if(length <= 70)
1122  {
1123  _count = 5;
1124  }
1125  else if(length <= 175)
1126  {
1127  _count = 10;
1128  }
1129  else if(length <= 350)
1130  {
1131  _count = 20;
1132  }
1133  else if(length <= 700)
1134  {
1135  _count = 40;
1136  }
1137  else if(length <= 1000)
1138  {
1139  _count = 60;
1140  }
1141  else if(length <= 1300)
1142  {
1143  _count = 80;
1144  }
1145  else
1146  {
1147  _count = 100;
1148  }
1149 
1150  // Rounding min and max
1151  if(min != max)
1152  {
1153  float mul = 1;
1154  float rangef = max - min;
1155  int countStep = _count/5;
1156  float val;
1157  for(int i=0; i<6; ++i)
1158  {
1159  val = (rangef/float(countStep)) * mul;
1160  if( val >= 1.0f && val < 10.0f)
1161  {
1162  break;
1163  }
1164  else if(val<1)
1165  {
1166  mul *= 10.0f;
1167  }
1168  else
1169  {
1170  mul /= 10.0f;
1171  }
1172  }
1173  //ULOGGER_DEBUG("min=%f, max=%f", min, max);
1174  int minR = min*mul-0.9;
1175  int maxR = max*mul+0.9;
1176  min = float(minR)/mul;
1177  max = float(maxR)/mul;
1178  //ULOGGER_DEBUG("mul=%f, minR=%d, maxR=%d,countStep=%d", mul, minR, maxR, countStep);
1179  }
1180 
1181  _min = min;
1182  _max = max;
1183 
1184  if(_reversed)
1185  {
1186  _min = _max;
1187  _max = min;
1188  }
1189 
1190  if(_orientation == Qt::Vertical)
1191  {
1192  _step = length/_count;
1193  borderDelta = length - (_step*_count);
1194  }
1195  else
1196  {
1197  _step = length/_count;
1198  borderDelta = length - (_step*_count);
1199  }
1200 
1201  if(borderDelta%2 != 0)
1202  {
1203  borderDelta+=1;
1204  }
1205 
1206  _border = border + borderDelta/2;
1207 
1208  //Resize estimation
1209  if(_orientation == Qt::Vertical)
1210  {
1211  int minWidth = 0;
1212  for (int i = 0; i <= _count; i+=5)
1213  {
1214  QString n(QString::number(_min + (i/5)*((_max-_min)/(_count/5)),'g',_gradMaxDigits));
1215  if(this->fontMetrics().width(n) > minWidth)
1216  {
1217  minWidth = this->fontMetrics().width(n);
1218  }
1219  }
1220  this->setMinimumWidth(15+minWidth);
1221  }
1222 }
1223 
1224 void UPlotAxis::paintEvent(QPaintEvent * event)
1225 {
1226  QPainter painter(this);
1227  if(_orientation == Qt::Vertical)
1228  {
1229  painter.translate(0, _border);
1230  for (int i = 0; i <= _count; ++i)
1231  {
1232  if(i%5 == 0)
1233  {
1234  painter.drawLine(this->width(), 0, this->width()-10, 0);
1235  QLabel n(QString::number(_min + (i/5)*((_max-_min)/(_count/5)),'g',_gradMaxDigits));
1236  painter.drawText(this->width()-(12+n.sizeHint().width()), n.sizeHint().height()/2-2, n.text());
1237  }
1238  else
1239  {
1240  painter.drawLine(this->width(), 0, this->width()-5, 0);
1241  }
1242  painter.translate(0, _step);
1243  }
1244  }
1245  else
1246  {
1247  painter.translate(_border, 0);
1248  for (int i = 0; i <= _count; ++i)
1249  {
1250  if(i%5 == 0)
1251  {
1252  painter.drawLine(0, 0, 0, 10);
1253  QLabel n(QString::number(_min + (i/5)*((_max-_min)/(_count/5)),'g',_gradMaxDigits));
1254  painter.drawText(-(n.sizeHint().width()/2)+1, 22, n.text());
1255  }
1256  else
1257  {
1258  painter.drawLine(0, 0, 0, 5);
1259  }
1260  painter.translate(_step, 0);
1261  }
1262  }
1263 }
1264 
1265 
1266 
1267 
1268 UPlotLegendItem::UPlotLegendItem(const UPlotCurve * curve, QWidget * parent) :
1269  QPushButton(parent),
1270  _curve(curve)
1271 {
1272  QString nameSpaced = curve->name();
1273  nameSpaced.replace('_', ' ');
1274  this->setText(nameSpaced);
1275 
1276  _aChangeText = new QAction(tr("Change text..."), this);
1277  _aResetText = new QAction(tr("Reset text..."), this);
1278  _aRemoveCurve = new QAction(tr("Remove this curve"), this);
1279  _aCopyToClipboard = new QAction(tr("Copy curve data to the clipboard"), this);
1280  _menu = new QMenu(tr("Curve"), this);
1281  _menu->addAction(_aChangeText);
1282  _menu->addAction(_aResetText);
1283  _menu->addAction(_aRemoveCurve);
1284  _menu->addAction(_aCopyToClipboard);
1285 }
1286 
1288 {
1289 
1290 }
1291 void UPlotLegendItem::contextMenuEvent(QContextMenuEvent * event)
1292 {
1293  QAction * action = _menu->exec(event->globalPos());
1294  if(action == _aChangeText)
1295  {
1296  bool ok;
1297  QString text = QInputDialog::getText(this, _aChangeText->text(), tr("Name :"), QLineEdit::Normal, this->text(), &ok);
1298  if(ok && !text.isEmpty())
1299  {
1300  this->setText(text);
1301  }
1302  }
1303  else if(action == _aResetText)
1304  {
1305  if(_curve)
1306  {
1307  this->setText(_curve->name());
1308  }
1309  }
1310  else if(action == _aRemoveCurve)
1311  {
1312  Q_EMIT legendItemRemoved(_curve);
1313  }
1314  else if (action == _aCopyToClipboard)
1315  {
1316  if(_curve)
1317  {
1318  QVector<float> x;
1319  QVector<float> y;
1320  _curve->getData(x, y);
1321  QString textX;
1322  QString textY;
1323  for(int i=0; i<x.size(); ++i)
1324  {
1325  textX.append(QString::number(x[i]));
1326  textY.append(QString::number(y[i]));
1327  if(i+1<x.size())
1328  {
1329  textX.append(' ');
1330  textY.append(' ');
1331  }
1332  }
1333  QClipboard * clipboard = QApplication::clipboard();
1334  clipboard->setText((textX+"\n")+textY);
1335  }
1336  }
1337 }
1338 
1339 
1340 
1341 
1342 
1343 
1344 UPlotLegend::UPlotLegend(QWidget * parent) :
1345  QWidget(parent),
1346  _flat(true)
1347 {
1348  //menu
1349  _aUseFlatButtons = new QAction(tr("Use flat buttons"), this);
1350  _aUseFlatButtons->setCheckable(true);
1351  _aUseFlatButtons->setChecked(_flat);
1352  _menu = new QMenu(tr("Legend"), this);
1353  _menu->addAction(_aUseFlatButtons);
1354 
1355  QVBoxLayout * vLayout = new QVBoxLayout(this);
1356  vLayout->setContentsMargins(0,0,0,0);
1357  this->setLayout(vLayout);
1358  vLayout->addStretch(0);
1359  vLayout->setSpacing(0);
1360 }
1361 
1363 {
1364 #if PRINT_DEBUG
1365  ULOGGER_DEBUG("");
1366 #endif
1367 }
1368 
1370 {
1371  if(_flat != on)
1372  {
1373  _flat = on;
1374  QList<UPlotLegendItem*> items = this->findChildren<UPlotLegendItem*>();
1375  for(int i=0; i<items.size(); ++i)
1376  {
1377  items.at(i)->setFlat(_flat);
1378  items.at(i)->setChecked(!items.at(i)->isChecked());
1379  }
1380  _aUseFlatButtons->setChecked(_flat);
1381  }
1382 }
1383 
1385 {
1386  if(curve)
1387  {
1388  UPlotLegendItem * legendItem = new UPlotLegendItem(curve, this);
1389  legendItem->setAutoDefault(false);
1390  legendItem->setFlat(_flat);
1391  legendItem->setCheckable(true);
1392  legendItem->setChecked(false);
1393  legendItem->setIcon(QIcon(this->createSymbol(curve->pen(), curve->brush())));
1394  legendItem->setIconSize(QSize(25,20));
1395  connect(legendItem, SIGNAL(toggled(bool)), this, SLOT(redirectToggled(bool)));
1396  connect(legendItem, SIGNAL(legendItemRemoved(const UPlotCurve *)), this, SLOT(removeLegendItem(const UPlotCurve *)));
1397 
1398  // layout
1399  QHBoxLayout * hLayout = new QHBoxLayout();
1400  hLayout->addWidget(legendItem);
1401  hLayout->addStretch(0);
1402  hLayout->setMargin(0);
1403 
1404  // add to the legend
1405  ((QVBoxLayout*)this->layout())->insertLayout(this->layout()->count()-1, hLayout);
1406  }
1407 }
1408 
1409 QPixmap UPlotLegend::createSymbol(const QPen & pen, const QBrush & brush)
1410 {
1411  QPixmap pixmap(50, 50);
1412  pixmap.fill(Qt::transparent);
1413  QPainter painter(&pixmap);
1414  QPen p = pen;
1415  p.setWidthF(4.0);
1416  painter.setPen(p);
1417  painter.drawLine(0.0, 25.0, 50.0, 25.0);
1418  return pixmap;
1419 }
1420 
1421 bool UPlotLegend::remove(const UPlotCurve * curve)
1422 {
1423  QList<UPlotLegendItem *> items = this->findChildren<UPlotLegendItem*>();
1424  for(int i=0; i<items.size(); ++i)
1425  {
1426  if(items.at(i)->curve() == curve)
1427  {
1428  delete items.at(i);
1429  return true;
1430  }
1431  }
1432  return false;
1433 }
1434 
1436 {
1437  if(this->remove(curve))
1438  {
1439  Q_EMIT legendItemRemoved(curve);
1440  }
1441 }
1442 
1443 void UPlotLegend::contextMenuEvent(QContextMenuEvent * event)
1444 {
1445  QAction * action = _menu->exec(event->globalPos());
1446  if(action == _aUseFlatButtons)
1447  {
1448  this->setFlat(_aUseFlatButtons->isChecked());
1449  }
1450 }
1451 
1453 {
1454  if(sender())
1455  {
1456  UPlotLegendItem * item = qobject_cast<UPlotLegendItem*>(sender());
1457  if(item)
1458  {
1459  Q_EMIT legendItemToggled(item->curve(), _flat?!toggled:toggled);
1460  }
1461  }
1462 }
1463 
1464 
1465 
1466 
1467 
1468 
1469 
1470 UOrientableLabel::UOrientableLabel(const QString & text, Qt::Orientation orientation, QWidget * parent) :
1471  QLabel(text, parent),
1472  _orientation(orientation)
1473 {
1474 }
1475 
1477 {
1478 }
1479 
1481 {
1482  QSize size = QLabel::sizeHint();
1483  if (_orientation == Qt::Vertical)
1484  size.transpose();
1485  return size;
1486 
1487 }
1488 
1490 {
1491  QSize size = QLabel::minimumSizeHint();
1492  if (_orientation == Qt::Vertical)
1493  size.transpose();
1494  return size;
1495 }
1496 
1498 {
1500  switch(orientation)
1501  {
1502  case Qt::Horizontal:
1503  setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
1504  break;
1505 
1506  case Qt::Vertical:
1507  setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1508  break;
1509  }
1510 }
1511 
1512 void UOrientableLabel::paintEvent(QPaintEvent* event)
1513 {
1514  QPainter p(this);
1515  QRect r = rect();
1516  switch (_orientation)
1517  {
1518  case Qt::Horizontal:
1519  break;
1520  case Qt::Vertical:
1521  p.rotate(-90);
1522  p.translate(-height(), 0);
1523  QSize size = r.size();
1524  size.transpose();
1525  r.setSize(size);
1526  break;
1527  }
1528  p.drawText(r, this->alignment() | (this->wordWrap()?Qt::TextWordWrap:0), this->text());
1529 }
1530 
1531 
1532 
1533 
1534 
1535 
1536 
1537 
1538 
1539 
1540 
1541 
1542 
1543 UPlot::UPlot(QWidget *parent) :
1544  QWidget(parent),
1545  _maxVisibleItems(-1),
1546  _autoScreenCaptureFormat("png")
1547 {
1548  this->setupUi();
1549  this->createActions();
1550  this->createMenus();
1551 
1552  // This will update actions
1553  this->showLegend(true);
1554  this->setGraphicsView(false);
1555  this->setMaxVisibleItems(0);
1556  this->showGrid(false);
1557  this->showRefreshRate(false);
1558  this->keepAllData(false);
1559 
1560  for(int i=0; i<4; ++i)
1561  {
1562  _axisMaximums[i] = 0;
1563  _axisMaximumsSet[i] = false;
1564  if(i<2)
1565  {
1566  _fixedAxis[i] = false;
1567  }
1568  }
1569 
1570  _refreshIntervalTime.start();
1571  _lowestRefreshRate = 99;
1572  _refreshStartTime.start();
1573 
1574  _penStyleCount = rand() % 10 + 1; // rand 1->10
1575  _workingDirectory = QDir::homePath();
1576 }
1577 
1579 {
1580  _aAutoScreenCapture->setChecked(false);
1581 #if PRINT_DEBUG
1582  ULOGGER_DEBUG("%s", this->title().toStdString().c_str());
1583 #endif
1584  this->removeCurves();
1585 }
1586 
1588 {
1589  _legend = new UPlotLegend(this);
1590  _view = new QGraphicsView(this);
1591  _view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1592  _view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1593  _view->setScene(new QGraphicsScene(0,0,0,0,this));
1594  _view->setStyleSheet( "QGraphicsView { border-style: none; }" );
1595  _sceneRoot = _view->scene()->addText("");
1596  _sceneRoot->setPos(0,0);
1597  _graphicsViewHolder = new QWidget(this);
1598  _graphicsViewHolder->setMinimumSize(100,100);
1599  _verticalAxis = new UPlotAxis(Qt::Vertical, 0, 1, this);
1600  _horizontalAxis = new UPlotAxis(Qt::Horizontal, 0, 1, this);
1601  _title = new QLabel("");
1602  _xLabel = new QLabel("");
1603  _refreshRate = new QLabel("");
1604  _yLabel = new UOrientableLabel("");
1605  _yLabel->setOrientation(Qt::Vertical);
1606  _title->setAlignment(Qt::AlignCenter);
1607  _xLabel->setAlignment(Qt::AlignCenter);
1608  _yLabel->setAlignment(Qt::AlignCenter);
1609  _refreshRate->setAlignment(Qt::AlignCenter);
1610  _title->setWordWrap(true);
1611  _xLabel->setWordWrap(true);
1612  _yLabel->setWordWrap(true);
1613  _title->setVisible(false);
1614  _xLabel->setVisible(false);
1615  _yLabel->setVisible(false);
1616  _refreshRate->setVisible(false);
1617 
1618  //layouts
1619  QVBoxLayout * vLayout = new QVBoxLayout(_graphicsViewHolder);
1620  vLayout->setContentsMargins(0,0,0,0);
1621  vLayout->addWidget(_view);
1622 
1623  QGridLayout * grid = new QGridLayout(this);
1624  grid->setContentsMargins(0,0,0,0);
1625  grid->addWidget(_title, 0, 2);
1626  grid->addWidget(_yLabel, 1, 0);
1627  grid->addWidget(_verticalAxis, 1, 1);
1628  grid->addWidget(_refreshRate, 2, 1);
1629  grid->addWidget(_graphicsViewHolder, 1, 2);
1630  grid->setColumnStretch(2, 1);
1631  grid->setRowStretch(1, 1);
1632  grid->addWidget(_horizontalAxis, 2, 2);
1633  grid->addWidget(_xLabel, 3, 2);
1634  grid->addWidget(_legend, 1, 3);
1635 
1636  connect(_legend, SIGNAL(legendItemToggled(const UPlotCurve *, bool)), this, SLOT(showCurve(const UPlotCurve *, bool)));
1637  connect(_legend, SIGNAL(legendItemRemoved(const UPlotCurve *)), this, SLOT(removeCurve(const UPlotCurve *)));
1638 }
1639 
1641 {
1642  _aShowLegend = new QAction(tr("Show legend"), this);
1643  _aShowLegend->setCheckable(true);
1644  _aShowGrid = new QAction(tr("Show grid"), this);
1645  _aShowGrid->setCheckable(true);
1646  _aShowRefreshRate = new QAction(tr("Show refresh rate"), this);
1647  _aShowRefreshRate->setCheckable(true);
1648  _aGraphicsView = new QAction(tr("Graphics view"), this);
1649  _aGraphicsView->setCheckable(true);
1650  _aKeepAllData = new QAction(tr("Keep all data"), this);
1651  _aKeepAllData->setCheckable(true);
1652  _aLimit0 = new QAction(tr("No maximum items shown"), this);
1653  _aLimit10 = new QAction(tr("10"), this);
1654  _aLimit50 = new QAction(tr("50"), this);
1655  _aLimit100 = new QAction(tr("100"), this);
1656  _aLimit500 = new QAction(tr("500"), this);
1657  _aLimit1000 = new QAction(tr("1000"), this);
1658  _aLimitCustom = new QAction(tr(""), this);
1659  _aLimit0->setCheckable(true);
1660  _aLimit10->setCheckable(true);
1661  _aLimit50->setCheckable(true);
1662  _aLimit100->setCheckable(true);
1663  _aLimit500->setCheckable(true);
1664  _aLimit1000->setCheckable(true);
1665  _aLimitCustom->setCheckable(true);
1666  _aLimitCustom->setVisible(false);
1667  _aAddVerticalLine = new QAction(tr("Vertical line..."), this);
1668  _aAddHorizontalLine = new QAction(tr("Horizontal line..."), this);
1669  _aChangeTitle = new QAction(tr("Change title"), this);
1670  _aChangeXLabel = new QAction(tr("Change X label..."), this);
1671  _aChangeYLabel = new QAction(tr("Change Y label..."), this);
1672  _aYLabelVertical = new QAction(tr("Vertical orientation"), this);
1673  _aYLabelVertical->setCheckable(true);
1674  _aYLabelVertical->setChecked(true);
1675  _aSaveFigure = new QAction(tr("Save figure..."), this);
1676  _aAutoScreenCapture = new QAction(tr("Auto screen capture..."), this);
1677  _aAutoScreenCapture->setCheckable(true);
1678  _aClearData = new QAction(tr("Clear data"), this);
1679 
1680  QActionGroup * grpLimit = new QActionGroup(this);
1681  grpLimit->addAction(_aLimit0);
1682  grpLimit->addAction(_aLimit10);
1683  grpLimit->addAction(_aLimit50);
1684  grpLimit->addAction(_aLimit100);
1685  grpLimit->addAction(_aLimit500);
1686  grpLimit->addAction(_aLimit1000);
1687  grpLimit->addAction(_aLimitCustom);
1688  _aLimit0->setChecked(true);
1689 }
1690 
1692 {
1693  _menu = new QMenu(tr("Plot"), this);
1694  _menu->addAction(_aShowLegend);
1695  _menu->addAction(_aShowGrid);
1696  _menu->addAction(_aShowRefreshRate);
1697  _menu->addAction(_aGraphicsView);
1698  _menu->addAction(_aKeepAllData);
1699  _menu->addSeparator()->setStatusTip(tr("Maximum items shown"));
1700  _menu->addAction(_aLimit0);
1701  _menu->addAction(_aLimit10);
1702  _menu->addAction(_aLimit50);
1703  _menu->addAction(_aLimit100);
1704  _menu->addAction(_aLimit500);
1705  _menu->addAction(_aLimit1000);
1706  _menu->addAction(_aLimitCustom);
1707  _menu->addSeparator();
1708  QMenu * addLineMenu = _menu->addMenu(tr("Add line"));
1709  addLineMenu->addAction(_aAddHorizontalLine);
1710  addLineMenu->addAction(_aAddVerticalLine);
1711  _menu->addSeparator();
1712  _menu->addAction(_aChangeTitle);
1713  _menu->addAction(_aChangeXLabel);
1714  QMenu * yLabelMenu = _menu->addMenu(tr("Y label"));
1715  yLabelMenu->addAction(_aChangeYLabel);
1716  yLabelMenu->addAction(_aYLabelVertical);
1717  _menu->addAction(_aSaveFigure);
1718  _menu->addAction(_aAutoScreenCapture);
1719  _menu->addSeparator();
1720  _menu->addAction(_aClearData);
1721 
1722 }
1723 
1724 UPlotCurve * UPlot::addCurve(const QString & curveName, const QColor & color)
1725 {
1726  // add curve
1727  UPlotCurve * curve = new UPlotCurve(curveName, this);
1728  if(color.isValid())
1729  {
1730  curve->setPen(color);
1731  }
1732  else
1733  {
1734  curve->setPen(this->getRandomPenColored());
1735  }
1736  this->addCurve(curve);
1737  return curve;
1738 }
1739 
1740 bool UPlot::addCurve(UPlotCurve * curve, bool ownershipTransferred)
1741 {
1742  if(curve)
1743  {
1744  // only last curve can trigger an update, so disable previous connections
1745  if(!qobject_cast<UPlotCurveThreshold*>(curve))
1746  {
1747  for(int i=_curves.size()-1; i>=0; --i)
1748  {
1749  if(!qobject_cast<UPlotCurveThreshold*>(_curves.at(i)))
1750  {
1751  disconnect(_curves.at(i), SIGNAL(dataChanged(const UPlotCurve *)), this, SLOT(updateAxis()));
1752  break;
1753  }
1754  }
1755  }
1756 
1757  // add curve
1758  _curves.append(curve);
1759  curve->attach(this);
1760  if(ownershipTransferred)
1761  {
1762  curve->setParent(this);
1763  }
1764  this->updateAxis(curve);
1765  curve->setXStart(_axisMaximums[1]);
1766 
1767  connect(curve, SIGNAL(dataChanged(const UPlotCurve *)), this, SLOT(updateAxis()));
1768 
1769  _legend->addItem(curve);
1770 
1771 #if PRINT_DEBUG
1772  ULOGGER_DEBUG("Curve \"%s\" added to plot \"%s\"", curve->name().toStdString().c_str(), this->title().toStdString().c_str());
1773 #endif
1774 
1775  return true;
1776  }
1777  else
1778  {
1779  ULOGGER_ERROR("The curve is null!");
1780  }
1781  return false;
1782 }
1783 
1784 QStringList UPlot::curveNames()
1785 {
1786  QStringList names;
1787  for(QList<UPlotCurve*>::iterator iter = _curves.begin(); iter!=_curves.end(); ++iter)
1788  {
1789  if(*iter)
1790  {
1791  names.append((*iter)->name());
1792  }
1793  }
1794  return names;
1795 }
1796 
1797 bool UPlot::contains(const QString & curveName)
1798 {
1799  for(QList<UPlotCurve*>::iterator iter = _curves.begin(); iter!=_curves.end(); ++iter)
1800  {
1801  if(*iter && (*iter)->name().compare(curveName) == 0)
1802  {
1803  return true;
1804  }
1805  }
1806  return false;
1807 }
1808 
1810 {
1811  return QPen((Qt::GlobalColor)(_penStyleCount++ % 12 + 7 ));
1812 }
1813 
1814 void UPlot::replot(QPainter * painter)
1815 {
1816  if(_maxVisibleItems>0)
1817  {
1818  UPlotCurve * c = 0;
1819  int maxItem = 0;
1820  // find the curve with the most items
1821  for(QList<UPlotCurve *>::iterator i=_curves.begin(); i!=_curves.end(); ++i)
1822  {
1823  if((*i)->isVisible() && ((UPlotCurve *)(*i))->itemsSize() > maxItem)
1824  {
1825  c = *i;
1826  maxItem = c->itemsSize();
1827  }
1828  }
1829  if(c && (maxItem-1)/2+1 > _maxVisibleItems)
1830  {
1831  _axisMaximums[0] = c->getItemData((c->itemsSize()-1) -_maxVisibleItems*2).x();
1832  }
1833  }
1834 
1835  float axis[4] = {0};
1836  for(int i=0; i<4; ++i)
1837  {
1838  axis[i] = _axisMaximums[i];
1839  }
1840 
1841  _verticalAxis->setAxis(axis[2], axis[3]);
1842  _horizontalAxis->setAxis(axis[0], axis[1]);
1843  if(_aGraphicsView->isChecked() && !painter)
1844  {
1845  _verticalAxis->update();
1846  _horizontalAxis->update();
1847  }
1848 
1849  //ULOGGER_DEBUG("x1=%f, x2=%f, y1=%f, y2=%f", _axisMaximums[0], _axisMaximums[1], _axisMaximums[2], _axisMaximums[3]);
1850 
1851  QRectF newRect(0,0, _graphicsViewHolder->size().width(), _graphicsViewHolder->size().height());
1852  _view->scene()->setSceneRect(newRect);
1853  float borderHor = (float)_horizontalAxis->border();
1854  float borderVer = (float)_verticalAxis->border();
1855 
1856  //grid
1857  qDeleteAll(hGridLines);
1858  hGridLines.clear();
1859  qDeleteAll(vGridLines);
1860  vGridLines.clear();
1861  if(_aShowGrid->isChecked())
1862  {
1863  // TODO make a PlotGrid class ?
1864  float w = newRect.width()-(borderHor*2);
1865  float h = newRect.height()-(borderVer*2);
1866  float stepH = w / float(_horizontalAxis->count());
1867  float stepV = h / float(_verticalAxis->count());
1868  QPen pen(Qt::DashLine);
1869  for(float i=0.0f; i*stepV <= h+stepV; i+=5.0f)
1870  {
1871  //horizontal lines
1872  if(!_aGraphicsView->isChecked())
1873  {
1874  if(painter)
1875  {
1876  painter->drawLine(0, stepV*i+borderVer+0.5f, borderHor, stepV*i+borderVer+0.5f);
1877  painter->save();
1878  painter->setPen(pen);
1879  painter->drawLine(borderHor, stepV*i+borderVer+0.5f, w+borderHor, stepV*i+borderVer+0.5f);
1880  painter->restore();
1881  painter->drawLine(w+borderHor, stepV*i+borderVer+0.5f, w+borderHor*2, stepV*i+borderVer+0.5f);
1882  }
1883  }
1884  else
1885  {
1886  hGridLines.append(new QGraphicsLineItem(0, stepV*i+borderVer, borderHor, stepV*i+borderVer, _sceneRoot));
1887  hGridLines.append(new QGraphicsLineItem(borderHor, stepV*i+borderVer, w+borderHor, stepV*i+borderVer, _sceneRoot));
1888  hGridLines.last()->setPen(pen);
1889  hGridLines.append(new QGraphicsLineItem(w+borderHor, stepV*i+borderVer, w+borderHor*2, stepV*i+borderVer, _sceneRoot));
1890  }
1891  }
1892  for(float i=0; i*stepH < w+stepH; i+=5.0f)
1893  {
1894  //vertical lines
1895  if(!_aGraphicsView->isChecked())
1896  {
1897  if(painter)
1898  {
1899  painter->drawLine(stepH*i+borderHor+0.5f, 0, stepH*i+borderHor+0.5f, borderVer);
1900  painter->save();
1901  painter->setPen(pen);
1902  painter->drawLine(stepH*i+borderHor+0.5f, borderVer, stepH*i+borderHor+0.5f, h+borderVer);
1903  painter->restore();
1904  painter->drawLine(stepH*i+borderHor+0.5f, h+borderVer, stepH*i+borderHor+0.5f, h+borderVer*2);
1905  }
1906  }
1907  else
1908  {
1909  vGridLines.append(new QGraphicsLineItem(stepH*i+borderHor, 0, stepH*i+borderHor, borderVer, _sceneRoot));
1910  vGridLines.append(new QGraphicsLineItem(stepH*i+borderHor, borderVer, stepH*i+borderHor, h+borderVer, _sceneRoot));
1911  vGridLines.last()->setPen(pen);
1912  vGridLines.append(new QGraphicsLineItem(stepH*i+borderHor, h+borderVer, stepH*i+borderHor, h+borderVer*2, _sceneRoot));
1913  }
1914  }
1915  }
1916 
1917  // curves
1918  float scaleX = 1;
1919  float scaleY = 1;
1920  float den = 0;
1921  den = axis[1] - axis[0];
1922  if(den != 0)
1923  {
1924  scaleX = (newRect.width()-(borderHor*2)) / den;
1925  }
1926  den = axis[3] - axis[2];
1927  if(den != 0)
1928  {
1929  scaleY = (newRect.height()-(borderVer*2)) / den;
1930  }
1931  for(QList<UPlotCurve *>::iterator i=_curves.begin(); i!=_curves.end(); ++i)
1932  {
1933  if((*i)->isVisible())
1934  {
1935  float xDir = 1.0f;
1936  float yDir = -1.0f;
1937  (*i)->update(scaleX,
1938  scaleY,
1939  xDir<0?axis[1]+borderHor/scaleX:-(axis[0]-borderHor/scaleX),
1940  yDir<0?axis[3]+borderVer/scaleY:-(axis[2]-borderVer/scaleY),
1941  xDir,
1942  yDir,
1943  _aKeepAllData->isChecked());
1944  if(painter)
1945  {
1946  (*i)->draw(painter);
1947  }
1948  }
1949  }
1950 
1951  // Update refresh rate
1952  if(_aShowRefreshRate->isChecked())
1953  {
1954  int refreshRate = qRound(1000.0f/float(_refreshIntervalTime.restart()));
1955  if(refreshRate > 0 && refreshRate < _lowestRefreshRate)
1956  {
1957  _lowestRefreshRate = refreshRate;
1958  }
1959  // Refresh the label only after each 1000 ms
1960  if(_refreshStartTime.elapsed() > 1000)
1961  {
1962  _refreshRate->setText(QString::number(_lowestRefreshRate));
1963  _lowestRefreshRate = 99;
1964  _refreshStartTime.start();
1965  }
1966  }
1967 }
1968 
1969 void UPlot::setFixedXAxis(float x1, float x2)
1970 {
1971  _fixedAxis[0] = true;
1972  _axisMaximums[0] = x1;
1973  _axisMaximums[1] = x2;
1974 }
1975 
1976 void UPlot::setFixedYAxis(float y1, float y2)
1977 {
1978  _fixedAxis[1] = true;
1979  _axisMaximums[2] = y1;
1980  _axisMaximums[3] = y2;
1981 }
1982 
1983 void UPlot::updateAxis(const UPlotCurve * curve)
1984 {
1985  if(curve && curve->isVisible() && curve->itemsSize() && curve->isMinMaxValid())
1986  {
1987  const QVector<float> & minMax = curve->getMinMax();
1988  //ULOGGER_DEBUG("x1=%f, x2=%f, y1=%f, y2=%f", minMax[0], minMax[1], minMax[2], minMax[3]);
1989  if(minMax.size() != 4)
1990  {
1991  ULOGGER_ERROR("minMax size != 4 ?!?");
1992  return;
1993  }
1994  this->updateAxis(minMax[0], minMax[1], minMax[2], minMax[3]);
1995  _aGraphicsView->isChecked()?this->replot(0):this->update();
1996  }
1997 }
1998 
1999 bool UPlot::updateAxis(float x1, float x2, float y1, float y2)
2000 {
2001  bool modified = false;
2002  modified = updateAxis(x1,y1);
2003  if(!modified)
2004  {
2005  modified = updateAxis(x2,y2);
2006  }
2007  else
2008  {
2009  updateAxis(x2,y2);
2010  }
2011  return modified;
2012 }
2013 
2014 bool UPlot::updateAxis(float x, float y)
2015 {
2016  //ULOGGER_DEBUG("x=%f, y=%f", x,y);
2017  bool modified = false;
2018  if(!_fixedAxis[0] && (!_axisMaximumsSet[0] || x < _axisMaximums[0]))
2019  {
2020  _axisMaximums[0] = x;
2021  _axisMaximumsSet[0] = true;
2022  modified = true;
2023  }
2024 
2025  if(!_fixedAxis[0] && (!_axisMaximumsSet[1] || x > _axisMaximums[1]))
2026  {
2027  _axisMaximums[1] = x;
2028  _axisMaximumsSet[1] = true;
2029  modified = true;
2030  }
2031 
2032  if(!_fixedAxis[1] && (!_axisMaximumsSet[2] || y < _axisMaximums[2]))
2033  {
2034  _axisMaximums[2] = y;
2035  _axisMaximumsSet[2] = true;
2036  modified = true;
2037  }
2038 
2039  if(!_fixedAxis[1] && (!_axisMaximumsSet[3] || y > _axisMaximums[3]))
2040  {
2041  _axisMaximums[3] = y;
2042  _axisMaximumsSet[3] = true;
2043  modified = true;
2044  }
2045 
2046  return modified;
2047 }
2048 
2050 {
2051  //Reset the axis
2052  for(int i=0; i<4; ++i)
2053  {
2054  if((!_fixedAxis[0] && i<2) || (!_fixedAxis[1] && i>=2))
2055  {
2056  _axisMaximums[i] = 0;
2057  _axisMaximumsSet[i] = false;
2058  }
2059  }
2060 
2061  for(int i=0; i<_curves.size(); ++i)
2062  {
2063  if(_curves.at(i)->isVisible() && _curves.at(i)->isMinMaxValid())
2064  {
2065  const QVector<float> & minMax = _curves.at(i)->getMinMax();
2066  this->updateAxis(minMax[0], minMax[1], minMax[2], minMax[3]);
2067  }
2068  }
2069 
2070  _aGraphicsView->isChecked()?this->replot(0):this->update();
2071 
2072  this->captureScreen();
2073 }
2074 
2075 void UPlot::paintEvent(QPaintEvent * event)
2076 {
2077 #if PRINT_DEBUG
2078  UDEBUG("");
2079 #endif
2080  if(!_aGraphicsView->isChecked())
2081  {
2082  QPainter painter(this);
2083  painter.translate(_graphicsViewHolder->pos());
2084  painter.save();
2085  painter.setBrush(Qt::white);
2086  painter.setPen(QPen(Qt::NoPen));
2087  painter.drawRect(_graphicsViewHolder->rect());
2088  painter.restore();
2089 
2090  this->replot(&painter);
2091  }
2092  else
2093  {
2094  QWidget::paintEvent(event);
2095  }
2096 }
2097 
2098 void UPlot::resizeEvent(QResizeEvent * event)
2099 {
2100  if(_aGraphicsView->isChecked())
2101  {
2102  this->replot(0);
2103  }
2104  QWidget::resizeEvent(event);
2105 }
2106 
2107 void UPlot::contextMenuEvent(QContextMenuEvent * event)
2108 {
2109  QAction * action = _menu->exec(event->globalPos());
2110 
2111  if(!action)
2112  {
2113  return;
2114  }
2115  else if(action == _aShowLegend)
2116  {
2117  this->showLegend(_aShowLegend->isChecked());
2118  }
2119  else if(action == _aShowGrid)
2120  {
2121  this->showGrid(_aShowGrid->isChecked());
2122  }
2123  else if(action == _aShowRefreshRate)
2124  {
2125  this->showRefreshRate(_aShowRefreshRate->isChecked());
2126  }
2127  else if(action == _aGraphicsView)
2128  {
2129  this->setGraphicsView(_aGraphicsView->isChecked());
2130  }
2131  else if(action == _aKeepAllData)
2132  {
2133  this->keepAllData(_aKeepAllData->isChecked());
2134  }
2135  else if(action == _aLimit0 ||
2136  action == _aLimit10 ||
2137  action == _aLimit50 ||
2138  action == _aLimit100 ||
2139  action == _aLimit500 ||
2140  action == _aLimit1000 ||
2141  action == _aLimitCustom)
2142  {
2143  this->setMaxVisibleItems(action->text().toInt());
2144  }
2145  else if(action == _aAddVerticalLine || action == _aAddHorizontalLine)
2146  {
2147  bool ok;
2148  QString text = QInputDialog::getText(this, action->text(), tr("New line name :"), QLineEdit::Normal, "", &ok);
2149  while(ok && text.isEmpty())
2150  {
2151  QMessageBox::warning(this, action->text(), tr("The name is not valid or it is already used in this plot."));
2152  text = QInputDialog::getText(this, action->text(), tr("New line name :"), QLineEdit::Normal, "", &ok);
2153  }
2154  if(ok)
2155  {
2156  double min = _axisMaximums[2];
2157  double max = _axisMaximums[3];
2158  QString axis = "Y";
2159  if(action == _aAddVerticalLine)
2160  {
2161  min = _axisMaximums[0];
2162  max = _axisMaximums[1];
2163  axis = "X";
2164  }
2165  double value = QInputDialog::getDouble(this,
2166  action->text(),
2167  tr("%1 value (min=%2, max=%3):").arg(axis).arg(min).arg(max),
2168  (min+max)/2,
2169  -2147483647,
2170  2147483647,
2171  4,
2172  &ok);
2173  if(ok)
2174  {
2175  if(action == _aAddHorizontalLine)
2176  {
2177  this->addThreshold(text, value, Qt::Horizontal);
2178  }
2179  else
2180  {
2181  this->addThreshold(text, value, Qt::Vertical);
2182  }
2183  }
2184  }
2185  }
2186  else if(action == _aChangeTitle)
2187  {
2188  bool ok;
2189  QString text = _title->text();
2190  if(text.isEmpty())
2191  {
2192  text = this->objectName();
2193  }
2194  text = QInputDialog::getText(this, _aChangeTitle->text(), tr("Title :"), QLineEdit::Normal, text, &ok);
2195  if(ok)
2196  {
2197  this->setTitle(text);
2198  }
2199  }
2200  else if(action == _aChangeXLabel)
2201  {
2202  bool ok;
2203  QString text = QInputDialog::getText(this, _aChangeXLabel->text(), tr("X axis label :"), QLineEdit::Normal, _xLabel->text(), &ok);
2204  if(ok)
2205  {
2206  this->setXLabel(text);
2207  }
2208  }
2209  else if(action == _aChangeYLabel)
2210  {
2211  bool ok;
2212  QString text = QInputDialog::getText(this, _aChangeYLabel->text(), tr("Y axis label :"), QLineEdit::Normal, _yLabel->text(), &ok);
2213  if(ok)
2214  {
2215  this->setYLabel(text, _yLabel->orientation());
2216  }
2217  }
2218  else if(action == _aYLabelVertical)
2219  {
2220  this->setYLabel(_yLabel->text(), _aYLabelVertical->isChecked()?Qt::Vertical:Qt::Horizontal);
2221  }
2222  else if(action == _aSaveFigure)
2223  {
2224 
2225  QString text;
2226 #ifdef QT_SVG_LIB
2227  text = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), (QDir::homePath() + "/") + this->title() + ".png", "*.png *.xpm *.jpg *.pdf *.svg");
2228 #else
2229  text = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), (QDir::homePath() + "/") + this->title() + ".png", "*.png *.xpm *.jpg *.pdf");
2230 #endif
2231  if(!text.isEmpty())
2232  {
2233  bool flatModified = false;
2234  if(!_legend->isFlat())
2235  {
2236  _legend->setFlat(true);
2237  flatModified = true;
2238  }
2239 
2240  QPalette p(palette());
2241  // Set background color to white
2242  QColor c = p.color(QPalette::Background);
2243  p.setColor(QPalette::Background, Qt::white);
2244  setPalette(p);
2245 
2246 #ifdef QT_SVG_LIB
2247  if(QFileInfo(text).suffix().compare("svg") == 0)
2248  {
2249  QSvgGenerator generator;
2250  generator.setFileName(text);
2251  generator.setSize(this->size());
2252  QPainter painter;
2253  painter.begin(&generator);
2254  this->render(&painter);
2255  painter.end();
2256  }
2257  else
2258  {
2259 #endif
2260  if(QFileInfo(text).suffix().compare("pdf") == 0)
2261  {
2262  QPrinter printer;
2263  printer.setOutputFormat(QPrinter::PdfFormat);
2264  printer.setOutputFileName(text);
2265  this->render(&printer);
2266  }
2267  else
2268  {
2269  QPixmap figure = QPixmap::grabWidget(this);
2270  figure.save(text);
2271  }
2272 #ifdef QT_SVG_LIB
2273  }
2274 #endif
2275  // revert background color
2276  p.setColor(QPalette::Background, c);
2277  setPalette(p);
2278 
2279  if(flatModified)
2280  {
2281  _legend->setFlat(false);
2282  }
2283  }
2284  }
2285  else if(action == _aAutoScreenCapture)
2286  {
2287  if(_aAutoScreenCapture->isChecked())
2288  {
2289  this->selectScreenCaptureFormat();
2290  }
2291  }
2292  else if(action == _aClearData)
2293  {
2294  this->clearData();
2295  }
2296  else
2297  {
2298  ULOGGER_WARN("Unknown action");
2299  }
2300 }
2301 
2302 void UPlot::setWorkingDirectory(const QString & workingDirectory)
2303 {
2304  if(QDir(_workingDirectory).exists())
2305  {
2306  _workingDirectory = workingDirectory;
2307  }
2308  else
2309  {
2310  ULOGGER_ERROR("The directory \"%s\" doesn't exist", workingDirectory.toStdString().c_str());
2311  }
2312 }
2313 
2315 {
2316  if(!_aAutoScreenCapture->isChecked())
2317  {
2318  return;
2319  }
2320  QString targetDir = _workingDirectory + "/ScreensCaptured";
2321  QDir dir;
2322  if(!dir.exists(targetDir))
2323  {
2324  dir.mkdir(targetDir);
2325  }
2326  targetDir += "/";
2327  targetDir += this->title().replace(" ", "_");
2328  if(!dir.exists(targetDir))
2329  {
2330  dir.mkdir(targetDir);
2331  }
2332  targetDir += "/";
2333  QString name = (QDateTime::currentDateTime().toString("yyMMddhhmmsszzz") + ".") + _autoScreenCaptureFormat;
2334  QPixmap figure = QPixmap::grabWidget(this);
2335  figure.save(targetDir + name);
2336 }
2337 
2339 {
2340  QStringList items;
2341  items << QString("png") << QString("jpg");
2342  bool ok;
2343  QString item = QInputDialog::getItem(this, tr("Select format"), tr("Format:"), items, 0, false, &ok);
2344  if(ok && !item.isEmpty())
2345  {
2346  _autoScreenCaptureFormat = item;
2347  }
2348  this->captureScreen();
2349 }
2350 
2352 {
2353  for(int i=0; i<_curves.size(); ++i)
2354  {
2355  // Don't clear threshold curves
2356  if(qobject_cast<UPlotCurveThreshold*>(_curves.at(i)) == 0)
2357  {
2358  _curves.at(i)->clear();
2359  }
2360  }
2361  _aGraphicsView->isChecked()?this->replot(0):this->update();
2362 }
2363 
2364 // for convenience...
2365 UPlotCurveThreshold * UPlot::addThreshold(const QString & name, float value, Qt::Orientation orientation)
2366 {
2367  UPlotCurveThreshold * curve = new UPlotCurveThreshold(name, value, orientation, this);
2368  QPen pen = curve->pen();
2369  pen.setStyle((Qt::PenStyle)(_penStyleCount++ % 4 + 2));
2370  curve->setPen(pen);
2371  if(!this->addCurve(curve))
2372  {
2373  if(curve)
2374  {
2375  delete curve;
2376  }
2377  }
2378  else
2379  {
2380  _aGraphicsView->isChecked()?this->replot(0):this->update();
2381  }
2382  return curve;
2383 }
2384 
2385 void UPlot::setTitle(const QString & text)
2386 {
2387  _title->setText(text);
2388  _title->setVisible(!text.isEmpty());
2389  this->update();
2390  if(_aGraphicsView->isChecked())
2391  {
2392  QTimer::singleShot(10, this, SLOT(updateAxis()));
2393  }
2394 }
2395 
2396 void UPlot::setXLabel(const QString & text)
2397 {
2398  _xLabel->setText(text);
2399  _xLabel->setVisible(!text.isEmpty());
2400  this->update();
2401  if(_aGraphicsView->isChecked())
2402  {
2403  QTimer::singleShot(10, this, SLOT(updateAxis()));
2404  }
2405 }
2406 
2407 void UPlot::setYLabel(const QString & text, Qt::Orientation orientation)
2408 {
2409  _yLabel->setText(text);
2410  _yLabel->setOrientation(orientation);
2411  _yLabel->setVisible(!text.isEmpty());
2412  _aYLabelVertical->setChecked(orientation==Qt::Vertical);
2413  this->update();
2414  if(_aGraphicsView->isChecked())
2415  {
2416  QTimer::singleShot(10, this, SLOT(updateAxis()));
2417  }
2418 }
2419 
2420 void UPlot::addItem(QGraphicsItem * item)
2421 {
2422  item->setParentItem(_sceneRoot);
2423 }
2424 
2425 void UPlot::showLegend(bool shown)
2426 {
2427  _legend->setVisible(shown);
2428  _aShowLegend->setChecked(shown);
2429  this->update();
2430  if(_aGraphicsView->isChecked())
2431  {
2432  QTimer::singleShot(10, this, SLOT(updateAxis()));
2433  }
2434 }
2435 
2436 void UPlot::showGrid(bool shown)
2437 {
2438  _aShowGrid->setChecked(shown);
2439  _aGraphicsView->isChecked()?this->replot(0):this->update();
2440 }
2441 
2442 void UPlot::showRefreshRate(bool shown)
2443 {
2444  _aShowRefreshRate->setChecked(shown);
2445  _refreshRate->setVisible(shown);
2446  this->update();
2447  if(_aGraphicsView->isChecked())
2448  {
2449  QTimer::singleShot(10, this, SLOT(updateAxis()));
2450  }
2451 }
2452 
2454 {
2455  _aGraphicsView->setChecked(on);
2456  _view->setVisible(on);
2457  _aGraphicsView->isChecked()?this->replot(0):this->update();
2458 }
2459 
2460 void UPlot::keepAllData(bool kept)
2461 {
2462  _aKeepAllData->setChecked(kept);
2463 }
2464 
2465 void UPlot::setMaxVisibleItems(int maxVisibleItems)
2466 {
2467  if(maxVisibleItems <= 0)
2468  {
2469  _aLimit0->setChecked(true);
2470  }
2471  else if(maxVisibleItems == 10)
2472  {
2473  _aLimit10->setChecked(true);
2474  }
2475  else if(maxVisibleItems == 50)
2476  {
2477  _aLimit50->setChecked(true);
2478  }
2479  else if(maxVisibleItems == 100)
2480  {
2481  _aLimit100->setChecked(true);
2482  }
2483  else if(maxVisibleItems == 500)
2484  {
2485  _aLimit500->setChecked(true);
2486  }
2487  else if(maxVisibleItems == 1000)
2488  {
2489  _aLimit1000->setChecked(true);
2490  }
2491  else
2492  {
2493  _aLimitCustom->setVisible(true);
2494  _aLimitCustom->setChecked(true);
2495  _aLimitCustom->setText(QString::number(maxVisibleItems));
2496  }
2497  _maxVisibleItems = maxVisibleItems;
2498  updateAxis();
2499 }
2500 
2501 QRectF UPlot::sceneRect() const
2502 {
2503  return _view->sceneRect();
2504 }
2505 
2507 {
2508  QList<UPlotCurve*> tmp = _curves;
2509  for(QList<UPlotCurve*>::iterator iter=tmp.begin(); iter!=tmp.end(); ++iter)
2510  {
2511  this->removeCurve(*iter);
2512  }
2513  _curves.clear();
2514 }
2515 
2516 void UPlot::removeCurve(const UPlotCurve * curve)
2517 {
2518  QList<UPlotCurve *>::iterator iter = qFind(_curves.begin(), _curves.end(), curve);
2519 #if PRINT_DEBUG
2520  ULOGGER_DEBUG("Plot=\"%s\" removing curve=\"%s\"", this->objectName().toStdString().c_str(), curve?curve->name().toStdString().c_str():"");
2521 #endif
2522  if(iter!=_curves.end())
2523  {
2524  UPlotCurve * c = *iter;
2525  c->detach(this);
2526  _curves.erase(iter);
2527  _legend->remove(c);
2528  if(!qobject_cast<UPlotCurveThreshold*>(c))
2529  {
2530  // transfer update connection to next curve
2531  for(int i=_curves.size()-1; i>=0; --i)
2532  {
2533  if(!qobject_cast<UPlotCurveThreshold*>(_curves.at(i)))
2534  {
2535  connect(_curves.at(i), SIGNAL(dataChanged(const UPlotCurve *)), this, SLOT(updateAxis()));
2536  break;
2537  }
2538  }
2539  }
2540 
2541  if(c->parent() == this)
2542  {
2543  delete c;
2544  }
2545  // Update axis
2546  updateAxis();
2547  }
2548 }
2549 
2550 void UPlot::showCurve(const UPlotCurve * curve, bool shown)
2551 {
2552  QList<UPlotCurve *>::iterator iter = qFind(_curves.begin(), _curves.end(), curve);
2553  if(iter!=_curves.end())
2554  {
2555  UPlotCurve * value = *iter;
2556  if(value->isVisible() != shown)
2557  {
2558  value->setVisible(shown);
2559  this->updateAxis();
2560  }
2561  }
2562 }
QString _autoScreenCaptureFormat
Definition: UPlot.h:570
virtual ~UPlotCurve()
Definition: UPlot.cpp:281
QPointF getItemData(int index)
Definition: UPlot.cpp:777
UPlotCurveThreshold(const QString &name, float thesholdValue, Qt::Orientation orientation=Qt::Horizontal, QObject *parent=0)
Definition: UPlot.cpp:950
QGraphicsTextItem * _text
Definition: UPlot.h:80
virtual void contextMenuEvent(QContextMenuEvent *event)
Definition: UPlot.cpp:1291
UPlot * _plot
Definition: UPlot.h:233
void detach(UPlot *plot)
Definition: UPlot.cpp:310
void setOrientation(Qt::Orientation orientation)
Definition: UPlot.cpp:1000
void redirectToggled(bool)
Definition: UPlot.cpp:1452
QAction * _aChangeYLabel
Definition: UPlot.h:587
QGraphicsItem * _sceneRoot
Definition: UPlot.h:550
float _axisMaximums[4]
Definition: UPlot.h:552
UPlotAxis * _verticalAxis
Definition: UPlot.h:555
QList< UPlotCurve * > _curves
Definition: UPlot.h:561
QRectF sceneRect() const
Definition: UPlot.cpp:2501
virtual ~UPlotCurveThreshold()
Definition: UPlot.cpp:966
void showGrid(bool shown)
Definition: UPlot.cpp:2436
QPen _pen
Definition: UPlot.h:240
float _max
Definition: UPlot.h:324
void setXIncrement(float increment)
Definition: UPlot.cpp:801
virtual ~UPlotLegend()
Definition: UPlot.cpp:1362
const QVector< float > & getMinMax() const
Definition: UPlot.h:227
virtual void contextMenuEvent(QContextMenuEvent *event)
Definition: UPlot.cpp:2107
QAction * _aKeepAllData
Definition: UPlot.h:575
f
QAction * _aLimit500
Definition: UPlot.h:580
const QBrush & brush() const
Definition: UPlot.h:116
QLabel * _refreshRate
Definition: UPlot.h:565
int removeItem(int index)
Definition: UPlot.cpp:560
bool _flat
Definition: UPlot.h:397
void setFixedYAxis(float y1, float y2)
Definition: UPlot.cpp:1976
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event)
Definition: UPlot.cpp:175
void setBrush(const QBrush &brush)
Definition: UPlot.cpp:654
QAction * _aLimit100
Definition: UPlot.h:579
QAction * _aGraphicsView
Definition: UPlot.h:593
QLabel * _xLabel
Definition: UPlot.h:563
UPlotAxis(Qt::Orientation orientation=Qt::Horizontal, float min=0, float max=1, QWidget *parent=0)
Definition: UPlot.cpp:1058
int itemsSize() const
Definition: UPlot.cpp:772
const UPlotCurve * _curve
Definition: UPlot.h:355
void setFixedXAxis(float x1, float x2)
Definition: UPlot.cpp:1969
void updateMinMax()
Definition: UPlot.cpp:328
QAction * _aAddVerticalLine
Definition: UPlot.h:583
int border() const
Definition: UPlot.h:304
UPlotItem * _nextItem
Definition: UPlot.h:82
bool _fixedAxis[2]
Definition: UPlot.h:554
UPlotItem * _previousItem
Definition: UPlot.h:81
QAction * _aClearData
Definition: UPlot.h:592
const UPlotCurve * curve() const
Definition: UPlot.h:346
virtual ~UOrientableLabel()
Definition: UPlot.cpp:1476
virtual void contextMenuEvent(QContextMenuEvent *event)
Definition: UPlot.cpp:1443
virtual bool isMinMaxValid() const
Definition: UPlot.h:230
bool isFlat() const
Definition: UPlot.h:378
void showRefreshRate(bool shown)
Definition: UPlot.cpp:2442
UOrientableLabel * _yLabel
Definition: UPlot.h:564
void addValue(UPlotItem *data)
Definition: UPlot.cpp:416
QWidget * _graphicsViewHolder
Definition: UPlot.h:551
QStringList curveNames()
Definition: UPlot.cpp:1784
Qt::Orientation _orientation
Definition: UPlot.h:322
QTime _refreshIntervalTime
Definition: UPlot.h:567
void getData(QVector< float > &x, QVector< float > &y) const
Definition: UPlot.cpp:929
void createMenus()
Definition: UPlot.cpp:1691
QAction * _aShowGrid
Definition: UPlot.h:574
void setPen(const QPen &pen)
Definition: UPlot.cpp:645
void setupUi()
Definition: UPlot.cpp:1587
void setData(QVector< UPlotItem *> &data)
Definition: UPlot.cpp:811
Qt::Orientation orientation() const
Definition: UPlot.h:419
void removeCurves()
Definition: UPlot.cpp:2506
UPlotLegendItem(const UPlotCurve *curve, QWidget *parent=0)
Definition: UPlot.cpp:1268
QString _workingDirectory
Definition: UPlot.h:566
QAction * _aLimitCustom
Definition: UPlot.h:582
Definition: UPlot.h:462
bool _visible
Definition: UPlot.h:244
void update(const std::string &key, const XmlRpc::XmlRpcValue &v)
QAction * _aLimit10
Definition: UPlot.h:577
QPen getRandomPenColored()
Definition: UPlot.cpp:1809
UPlotAxis * _horizontalAxis
Definition: UPlot.h:556
int _count
Definition: UPlot.h:325
virtual void showDescription(bool shown)
Definition: UPlot.cpp:128
UPlot(QWidget *parent=0)
Definition: UPlot.cpp:1543
bool remove(const UPlotCurve *curve)
Definition: UPlot.cpp:1421
void removeLegendItem(const UPlotCurve *curve)
Definition: UPlot.cpp:1435
virtual void paintEvent(QPaintEvent *event)
Definition: UPlot.cpp:1224
friend class UPlotCurve
Definition: UPlot.h:535
QAction * _aRemoveCurve
Definition: UPlot.h:359
QSize sizeHint() const
Definition: UPlot.cpp:1480
void setVisible(bool visible)
Definition: UPlot.cpp:792
#define ULOGGER_DEBUG(...)
Definition: ULogger.h:54
Qt::Orientation _orientation
Definition: UPlot.h:429
QMenu * _menu
Definition: UPlot.h:356
QAction * _aChangeText
Definition: UPlot.h:357
void setOrientation(Qt::Orientation orientation)
Definition: UPlot.cpp:1497
UPlotCurve * addCurve(const QString &curveName, const QColor &color=QColor())
Definition: UPlot.cpp:1724
QString name() const
Definition: UPlot.h:130
QAction * _aShowRefreshRate
Definition: UPlot.h:589
QAction * _aLimit1000
Definition: UPlot.h:581
void clearData()
Definition: UPlot.cpp:2351
void removeCurve(const UPlotCurve *curve)
Definition: UPlot.cpp:2516
UPlotItem(qreal dataX, qreal dataY, qreal width=2)
Definition: UPlot.cpp:53
bool _valuesShown
Definition: UPlot.h:245
void setGraphicsView(bool on)
Definition: UPlot.cpp:2453
QAction * _aAddHorizontalLine
Definition: UPlot.h:584
const QPen & pen() const
Definition: UPlot.h:112
ROSCPP_DECL bool ok()
QMenu * _menu
Definition: UPlot.h:398
QGraphicsView * _view
Definition: UPlot.h:549
void _addValue(UPlotItem *data)
Definition: UPlot.cpp:365
void attach(UPlot *plot)
Definition: UPlot.cpp:293
void setThreshold(float threshold)
Definition: UPlot.cpp:971
void legendItemRemoved(const UPlotCurve *)
int _maxVisibleItems
Definition: UPlot.h:558
virtual ~UPlotLegendItem()
Definition: UPlot.cpp:1287
UPlotLegend(QWidget *parent=0)
Definition: UPlot.cpp:1344
QSize minimumSizeHint() const
Definition: UPlot.cpp:1489
virtual void keyReleaseEvent(QKeyEvent *keyEvent)
Definition: UPlot.cpp:210
int count() const
Definition: UPlot.h:312
virtual void update(float scaleX, float scaleY, float offsetX, float offsetY, float xDir, float yDir, bool allDataKept)
Definition: UPlot.cpp:660
QList< QGraphicsItem * > _items
Definition: UPlot.h:232
virtual void resizeEvent(QResizeEvent *event)
Definition: UPlot.cpp:2098
QMenu * _menu
Definition: UPlot.h:572
action
float _xIncrement
Definition: UPlot.h:242
QAction * _aYLabelVertical
Definition: UPlot.h:588
QPixmap createSymbol(const QPen &pen, const QBrush &brush)
Definition: UPlot.cpp:1409
virtual ~UPlotItem()
Definition: UPlot.cpp:82
virtual ~UPlot()
Definition: UPlot.cpp:1578
void setFlat(bool on)
Definition: UPlot.cpp:1369
virtual ~UPlotAxis()
Definition: UPlot.cpp:1077
bool contains(const QString &curveName)
Definition: UPlot.cpp:1797
UOrientableLabel(const QString &text, Qt::Orientation orientation=Qt::Horizontal, QWidget *parent=0)
Definition: UPlot.cpp:1470
void keepAllData(bool kept)
Definition: UPlot.cpp:2460
void setXLabel(const QString &text)
Definition: UPlot.cpp:2396
QList< QGraphicsLineItem * > vGridLines
Definition: UPlot.h:560
QAction * _aCopyToClipboard
Definition: UPlot.h:360
int _step
Definition: UPlot.h:326
QAction * _aAutoScreenCapture
Definition: UPlot.h:591
void updateAxis()
Definition: UPlot.cpp:2049
QAction * _aChangeXLabel
Definition: UPlot.h:586
UPlotCurveThreshold * addThreshold(const QString &name, float value, Qt::Orientation orientation=Qt::Horizontal)
Definition: UPlot.cpp:2365
#define UDEBUG(...)
void selectScreenCaptureFormat()
Definition: UPlot.cpp:2338
int _penStyleCount
Definition: UPlot.h:557
void setTitle(const QString &text)
Definition: UPlot.cpp:2385
int _gradMaxDigits
Definition: UPlot.h:328
int min(int a, int b)
bool isVisible() const
Definition: UPlot.h:136
virtual void update(float scaleX, float scaleY, float offsetX, float offsetY, float xDir, float yDir, bool allDataKept)
Definition: UPlot.cpp:1020
virtual void paintEvent(QPaintEvent *event)
Definition: UPlot.cpp:2075
#define ULOGGER_WARN(A,...)
Definition: UPlot.cpp:51
UPlotLegend * _legend
Definition: UPlot.h:548
QAction * _aLimit0
Definition: UPlot.h:576
virtual void clear()
Definition: UPlot.cpp:636
QLabel * _title
Definition: UPlot.h:562
void createActions()
Definition: UPlot.cpp:1640
QAction * _aSaveFigure
Definition: UPlot.h:590
virtual void focusOutEvent(QFocusEvent *event)
Definition: UPlot.cpp:204
QString _name
Definition: UPlot.h:239
void legendItemRemoved(const UPlotCurve *curve)
UPlotItem * nextItem() const
Definition: UPlot.h:65
UPlotItem * previousItem() const
Definition: UPlot.h:66
void setData(const QPointF &data)
Definition: UPlot.cpp:99
void setMaxVisibleItems(int maxVisibleItems)
Definition: UPlot.cpp:2465
QAction * _aChangeTitle
Definition: UPlot.h:585
const QPointF & data() const
Definition: UPlot.h:67
TFSIMD_FORCE_INLINE tfScalar length(const Quaternion &q)
QTime _refreshStartTime
Definition: UPlot.h:569
ROSCPP_DECL bool exists(const std::string &service_name, bool print_failure_reason)
#define ULOGGER_ERROR(A,...)
Definition: UPlot.cpp:50
virtual void focusInEvent(QFocusEvent *event)
Definition: UPlot.cpp:198
bool _axisMaximumsSet[4]
Definition: UPlot.h:553
float _xStart
Definition: UPlot.h:243
QBrush _brush
Definition: UPlot.h:241
void addItem(QGraphicsItem *item)
Definition: UPlot.cpp:2420
QAction * _aUseFlatButtons
Definition: UPlot.h:399
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
Definition: UPlot.cpp:189
void replot(QPainter *painter)
Definition: UPlot.cpp:1814
virtual void paintEvent(QPaintEvent *event)
Definition: UPlot.cpp:1512
int _border
Definition: UPlot.h:329
QVector< float > _minMax
Definition: UPlot.h:246
void showLegend(bool shown)
Definition: UPlot.cpp:2425
void dataChanged(const UPlotCurve *)
bool _reversed
Definition: UPlot.h:327
UPlotCurve(const QString &name, QObject *parent=0)
Definition: UPlot.cpp:246
int _lowestRefreshRate
Definition: UPlot.h:568
QAction * _aShowLegend
Definition: UPlot.h:573
QAction * _aResetText
Definition: UPlot.h:358
void setYLabel(const QString &text, Qt::Orientation orientation=Qt::Vertical)
Definition: UPlot.cpp:2407
void showCurve(const UPlotCurve *curve, bool shown)
Definition: UPlot.cpp:2550
void setReversed(bool reversed)
Definition: UPlot.cpp:1085
QPointF _data
Definition: UPlot.h:79
void setAxis(float &min, float &max)
Definition: UPlot.cpp:1096
float _min
Definition: UPlot.h:323
void setNextItem(UPlotItem *nextItem)
Definition: UPlot.cpp:104
QList< QGraphicsLineItem * > hGridLines
Definition: UPlot.h:559
void draw(QPainter *painter)
Definition: UPlot.cpp:735
void addValues(QVector< UPlotItem *> &data)
Definition: UPlot.cpp:461
void addItem(const UPlotCurve *curve)
Definition: UPlot.cpp:1384
Qt::Orientation _orientation
Definition: UPlot.h:280
QAction * _aLimit50
Definition: UPlot.h:578
void captureScreen()
Definition: UPlot.cpp:2314
QString title() const
Definition: UPlot.h:491
void setWorkingDirectory(const QString &workingDirectory)
Definition: UPlot.cpp:2302
void legendItemToggled(const UPlotCurve *curve, bool toggled)
void setPreviousItem(UPlotItem *previousItem)
Definition: UPlot.cpp:116
void setXStart(float val)
Definition: UPlot.cpp:806


find_object_2d
Author(s): Mathieu Labbe
autogenerated on Mon Dec 12 2022 03:20:10