mirror of
https://github.com/introlab/rtabmap_ros.git
synced 2026-10-05 09:17:47 +08:00
Added graph::importPoses(). Can now have a ground truth published with SensorData (filled optionally by CameraImages classes). Increased database closing time performance when the database is not saved. Added ParametersToolBox widget in DatabaseViewer for core parameters (refactoring done to make easy access to all rtabmap parameters in DatabaseViewer). Added Parameters::getType(key). Added Transform::interpolate() to interpolate between two transforms (SLERP) Updated pf_filter.m and added test_pf_filter.m MATLAB scripts (making easier to compare with a ground truth) UPlot: can now save all curve data of a figure in one action (see right-click on legend area->"Copy all curve data to clipboard")
3091 lines
74 KiB
C++
3091 lines
74 KiB
C++
/*
|
|
* utilite is a cross-platform library with
|
|
* useful utilities for fast and small developing.
|
|
* Copyright (C) 2010 Mathieu Labbe
|
|
*
|
|
* utilite is free library: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* utilite is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "rtabmap/utilite/UPlot.h"
|
|
#include "rtabmap/utilite/ULogger.h"
|
|
#include "rtabmap/utilite/UMath.h"
|
|
|
|
#include <QGraphicsScene>
|
|
#include <QGraphicsView>
|
|
#include <QGraphicsItem>
|
|
#include <QGraphicsRectItem>
|
|
#include <QHBoxLayout>
|
|
#include <QFormLayout>
|
|
#include <QtGui/QResizeEvent>
|
|
#include <QtGui/QMouseEvent>
|
|
#include <QtCore/QTime>
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QFileInfo>
|
|
#include <QPushButton>
|
|
#include <QToolButton>
|
|
#include <QLabel>
|
|
#include <QMenu>
|
|
#include <QInputDialog>
|
|
#include <QMessageBox>
|
|
#include <QFileDialog>
|
|
#include <QtGui/QClipboard>
|
|
#include <QApplication>
|
|
#include <QPrinter>
|
|
#include <QColorDialog>
|
|
#include <QToolTip>
|
|
#ifdef QT_SVG_LIB
|
|
#include <QtSvg/QSvgGenerator>
|
|
#endif
|
|
#include <cmath>
|
|
|
|
#define PRINT_DEBUG 0
|
|
|
|
UPlotItem::UPlotItem(qreal dataX, qreal dataY, qreal width) :
|
|
QGraphicsEllipseItem(0, 0, width, width, 0),
|
|
_previousItem(0),
|
|
_nextItem(0),
|
|
_text(0),
|
|
_textBackground(0)
|
|
{
|
|
this->init(dataX, dataY);
|
|
}
|
|
|
|
UPlotItem::UPlotItem(const QPointF & data, qreal width) :
|
|
QGraphicsEllipseItem(0, 0, width, width, 0),
|
|
_previousItem(0),
|
|
_nextItem(0),
|
|
_text(0),
|
|
_textBackground(0)
|
|
{
|
|
this->init(data.x(), data.y());
|
|
}
|
|
|
|
void UPlotItem::init(qreal dataX, qreal dataY)
|
|
{
|
|
_data.setX(dataX);
|
|
_data.setY(dataY);
|
|
this->setAcceptHoverEvents(true);
|
|
this->setFlag(QGraphicsItem::ItemIsFocusable, true);
|
|
}
|
|
|
|
UPlotItem::~UPlotItem()
|
|
{
|
|
if(_previousItem && _nextItem)
|
|
{
|
|
_previousItem->setNextItem(_nextItem);
|
|
_nextItem->setPreviousItem(_previousItem);
|
|
}
|
|
else if(_previousItem)
|
|
{
|
|
_previousItem->setNextItem(0);
|
|
}
|
|
else if(_nextItem)
|
|
{
|
|
_nextItem->setPreviousItem(0);
|
|
}
|
|
}
|
|
|
|
void UPlotItem::setData(const QPointF & data)
|
|
{
|
|
_data = data;
|
|
}
|
|
|
|
void UPlotItem::setNextItem(UPlotItem * nextItem)
|
|
{
|
|
if(_nextItem != nextItem)
|
|
{
|
|
_nextItem = nextItem;
|
|
if(nextItem)
|
|
{
|
|
nextItem->setPreviousItem(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotItem::setPreviousItem(UPlotItem * previousItem)
|
|
{
|
|
if(_previousItem != previousItem)
|
|
{
|
|
_previousItem = previousItem;
|
|
if(previousItem)
|
|
{
|
|
previousItem->setNextItem(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotItem::showDescription(bool shown)
|
|
{
|
|
if(!_textBackground)
|
|
{
|
|
_textBackground = new QGraphicsRectItem(this);
|
|
_textBackground->setBrush(QBrush(QColor(255, 255, 255, 200)));
|
|
_textBackground->setPen(Qt::NoPen);
|
|
_textBackground->setZValue(this->zValue()+1);
|
|
_textBackground->setVisible(false);
|
|
|
|
_text = new QGraphicsTextItem(_textBackground);
|
|
}
|
|
|
|
if(this->parentItem() && this->parentItem() != _textBackground->parentItem())
|
|
{
|
|
_textBackground->setParentItem(this->parentItem());
|
|
_textBackground->setZValue(this->zValue()+1);
|
|
}
|
|
|
|
if(this->scene() && shown)
|
|
{
|
|
_textBackground->setVisible(true);
|
|
_text->setPlainText(QString("(%1,%2)").arg(_data.x()).arg(_data.y()));
|
|
|
|
this->setPen(QPen(this->pen().color(), 2));
|
|
|
|
QRectF rect = this->scene()->sceneRect();
|
|
QPointF p = this->pos();
|
|
QRectF br = _text->boundingRect();
|
|
_textBackground->setRect(QRectF(0,0,br.width(), br.height()));
|
|
|
|
// Make sure the text is always in the scene
|
|
if(p.x() - br.width() < 0)
|
|
{
|
|
p.setX(0);
|
|
}
|
|
else if(p.x() > rect.width())
|
|
{
|
|
p.setX(rect.width() - br.width());
|
|
}
|
|
else
|
|
{
|
|
p.setX(p.x() - br.width());
|
|
}
|
|
|
|
if(p.y() - br.height() < 0)
|
|
{
|
|
p.setY(0);
|
|
}
|
|
else
|
|
{
|
|
p.setY(p.y() - br.height());
|
|
}
|
|
|
|
_textBackground->setPos(p);
|
|
}
|
|
else
|
|
{
|
|
this->setPen(QPen(this->pen().color(), 1));
|
|
_textBackground->setVisible(false);
|
|
}
|
|
}
|
|
|
|
void UPlotItem::hoverEnterEvent(QGraphicsSceneHoverEvent * event)
|
|
{
|
|
this->showDescription(true);
|
|
QGraphicsEllipseItem::hoverEnterEvent(event);
|
|
}
|
|
|
|
void UPlotItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * event)
|
|
{
|
|
if(!this->hasFocus())
|
|
{
|
|
this->showDescription(false);
|
|
}
|
|
QGraphicsEllipseItem::hoverLeaveEvent(event);
|
|
}
|
|
|
|
void UPlotItem::focusInEvent(QFocusEvent * event)
|
|
{
|
|
this->showDescription(true);
|
|
QGraphicsEllipseItem::focusInEvent(event);
|
|
}
|
|
|
|
void UPlotItem::focusOutEvent(QFocusEvent * event)
|
|
{
|
|
this->showDescription(false);
|
|
QGraphicsEllipseItem::focusOutEvent(event);
|
|
}
|
|
|
|
void UPlotItem::keyReleaseEvent(QKeyEvent * keyEvent)
|
|
{
|
|
//Get the next/previous visible item
|
|
if(keyEvent->key() == Qt::Key_Right)
|
|
{
|
|
UPlotItem * next = _nextItem;
|
|
while(next && !next->isVisible())
|
|
{
|
|
next = next->nextItem();
|
|
}
|
|
if(next && next->isVisible())
|
|
{
|
|
this->clearFocus();
|
|
next->setFocus();
|
|
}
|
|
}
|
|
else if(keyEvent->key() == Qt::Key_Left)
|
|
{
|
|
UPlotItem * previous = _previousItem;
|
|
while(previous && !previous->isVisible())
|
|
{
|
|
previous = previous->previousItem();
|
|
}
|
|
if(previous && previous->isVisible())
|
|
{
|
|
this->clearFocus();
|
|
previous->setFocus();
|
|
}
|
|
}
|
|
QGraphicsEllipseItem::keyReleaseEvent(keyEvent);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UPlotCurve::UPlotCurve(const QString & name, QObject * parent) :
|
|
QObject(parent),
|
|
_plot(0),
|
|
_name(name),
|
|
_xIncrement(1),
|
|
_xStart(0),
|
|
_visible(true),
|
|
_valuesShown(false),
|
|
_itemsColor(0,0,0,150)
|
|
{
|
|
_rootItem = new QGraphicsRectItem();
|
|
}
|
|
|
|
UPlotCurve::UPlotCurve(const QString & name, QVector<UPlotItem *> data, QObject * parent) :
|
|
QObject(parent),
|
|
_plot(0),
|
|
_name(name),
|
|
_xIncrement(1),
|
|
_xStart(0),
|
|
_visible(true),
|
|
_valuesShown(false),
|
|
_itemsColor(0,0,0,150)
|
|
{
|
|
_rootItem = new QGraphicsRectItem();
|
|
this->setData(data);
|
|
}
|
|
|
|
UPlotCurve::UPlotCurve(const QString & name, const QVector<float> & x, const QVector<float> & y, QObject * parent) :
|
|
QObject(parent),
|
|
_plot(0),
|
|
_name(name),
|
|
_xIncrement(1),
|
|
_xStart(0),
|
|
_visible(true),
|
|
_valuesShown(false),
|
|
_itemsColor(0,0,0,150)
|
|
{
|
|
_rootItem = new QGraphicsRectItem();
|
|
this->setData(x, y);
|
|
}
|
|
|
|
UPlotCurve::~UPlotCurve()
|
|
{
|
|
if(_plot)
|
|
{
|
|
_plot->removeCurve(this);
|
|
}
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("%s", this->name().toStdString().c_str());
|
|
#endif
|
|
this->clear();
|
|
delete _rootItem;
|
|
}
|
|
|
|
void UPlotCurve::attach(UPlot * plot)
|
|
{
|
|
if(!plot || plot == _plot)
|
|
{
|
|
return;
|
|
}
|
|
if(_plot)
|
|
{
|
|
_plot->removeCurve(this);
|
|
}
|
|
_plot = plot;
|
|
_plot->addItem(_rootItem);
|
|
}
|
|
|
|
void UPlotCurve::detach(UPlot * plot)
|
|
{
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("curve=\"%s\" from plot=\"%s\"", this->objectName().toStdString().c_str(), plot?plot->objectName().toStdString().c_str():"");
|
|
#endif
|
|
if(plot && _plot == plot)
|
|
{
|
|
_plot = 0;
|
|
if(_rootItem->scene())
|
|
{
|
|
_rootItem->scene()->removeItem(_rootItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::updateMinMax()
|
|
{
|
|
float x,y;
|
|
const UPlotItem * item;
|
|
if(!_items.size())
|
|
{
|
|
_minMax = QVector<float>();
|
|
}
|
|
else
|
|
{
|
|
_minMax = QVector<float>(4);
|
|
}
|
|
for(int i=0; i<_items.size(); ++i)
|
|
{
|
|
item = qgraphicsitem_cast<const UPlotItem *>(_items.at(i));
|
|
if(item)
|
|
{
|
|
x = item->data().x();
|
|
y = item->data().y();
|
|
if(i==0)
|
|
{
|
|
_minMax[0] = x;
|
|
_minMax[1] = x;
|
|
_minMax[2] = y;
|
|
_minMax[3] = y;
|
|
}
|
|
else
|
|
{
|
|
if(x<_minMax[0]) _minMax[0] = x;
|
|
if(x>_minMax[1]) _minMax[1] = x;
|
|
if(y<_minMax[2]) _minMax[2] = y;
|
|
if(y>_minMax[3]) _minMax[3] = y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::_addValue(UPlotItem * data)
|
|
{
|
|
// add item
|
|
if(data)
|
|
{
|
|
float x = data->data().x();
|
|
float y = data->data().y();
|
|
|
|
if(_minMax.size() != 4)
|
|
{
|
|
_minMax = QVector<float>(4);
|
|
}
|
|
if(_items.size())
|
|
{
|
|
data->setPreviousItem((UPlotItem *)_items.last());
|
|
QGraphicsLineItem * line = new QGraphicsLineItem(_rootItem);
|
|
line->setPen(_pen);
|
|
line->setVisible(false);
|
|
_items.append(line);
|
|
//Update min/max
|
|
if(x<_minMax[0]) _minMax[0] = x;
|
|
if(x>_minMax[1]) _minMax[1] = x;
|
|
if(y<_minMax[2]) _minMax[2] = y;
|
|
if(y>_minMax[3]) _minMax[3] = y;
|
|
}
|
|
else
|
|
{
|
|
_minMax[0] = x;
|
|
_minMax[1] = x;
|
|
_minMax[2] = y;
|
|
_minMax[3] = y;
|
|
}
|
|
data->setParentItem(_rootItem);
|
|
data->setZValue(1);
|
|
_items.append(data);
|
|
data->setVisible(false);
|
|
QPen pen = data->pen();
|
|
pen.setColor(_itemsColor);
|
|
data->setPen(pen);
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("Data is null ?!?");
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::addValue(UPlotItem * data)
|
|
{
|
|
// add item
|
|
if(data)
|
|
{
|
|
this->_addValue(data);
|
|
emit dataChanged(this);
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::addValue(float x, float y)
|
|
{
|
|
if(_items.size() &&
|
|
dynamic_cast<UPlotItem*>(_items.back()) &&
|
|
x < ((UPlotItem*)_items.back())->data().x())
|
|
{
|
|
UWARN("New value (%f) added to curve \"%s\" is smaller "
|
|
"than the last added (%f). Clearing the curve.",
|
|
x, this->name().toStdString().c_str(), ((UPlotItem*)_items.back())->data().x());
|
|
this->clear();
|
|
}
|
|
|
|
float width = 2; // TODO warn : hard coded value!
|
|
this->addValue(new UPlotItem(x,y,width));
|
|
}
|
|
|
|
void UPlotCurve::addValue(float y)
|
|
{
|
|
float x = 0;
|
|
if(_items.size())
|
|
{
|
|
UPlotItem * lastItem = (UPlotItem *)_items.last();
|
|
x = lastItem->data().x() + _xIncrement;
|
|
}
|
|
else
|
|
{
|
|
x = _xStart;
|
|
}
|
|
this->addValue(x,y);
|
|
}
|
|
|
|
void UPlotCurve::addValue(const QString & value)
|
|
{
|
|
bool ok;
|
|
float v = value.toFloat(&ok);
|
|
if(ok)
|
|
{
|
|
this->addValue(v);
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("Value not valid, must be a number, received %s", value.toStdString().c_str());
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::addValues(QVector<UPlotItem *> & data)
|
|
{
|
|
for(int i=0; i<data.size(); ++i)
|
|
{
|
|
this->_addValue(data.at(i));
|
|
}
|
|
emit dataChanged(this);
|
|
}
|
|
|
|
void UPlotCurve::addValues(const QVector<float> & xs, const QVector<float> & ys)
|
|
{
|
|
float width = 2; // TODO warn : hard coded value!
|
|
for(int i=0; i<xs.size() && i<ys.size(); ++i)
|
|
{
|
|
this->_addValue(new UPlotItem(xs.at(i),ys.at(i),width));
|
|
}
|
|
emit dataChanged(this);
|
|
}
|
|
|
|
void UPlotCurve::addValues(const QVector<float> & ys)
|
|
{
|
|
float x = 0;
|
|
float width = 2; // TODO warn : hard coded value!
|
|
for(int i=0; i<ys.size(); ++i)
|
|
{
|
|
if(_items.size())
|
|
{
|
|
UPlotItem * lastItem = (UPlotItem *)_items.last();
|
|
x = lastItem->data().x() + _xIncrement;
|
|
}
|
|
else
|
|
{
|
|
x = _xStart;
|
|
}
|
|
this->_addValue(new UPlotItem(x,ys.at(i),width));
|
|
}
|
|
emit dataChanged(this);
|
|
}
|
|
|
|
void UPlotCurve::addValues(const QVector<int> & ys)
|
|
{
|
|
float x = 0;
|
|
float width = 2; // TODO warn : hard coded value!
|
|
for(int i=0; i<ys.size(); ++i)
|
|
{
|
|
if(_items.size())
|
|
{
|
|
UPlotItem * lastItem = (UPlotItem *)_items.last();
|
|
x = lastItem->data().x() + _xIncrement;
|
|
}
|
|
else
|
|
{
|
|
x = _xStart;
|
|
}
|
|
this->_addValue(new UPlotItem(x,ys.at(i),width));
|
|
}
|
|
emit dataChanged(this);
|
|
}
|
|
|
|
void UPlotCurve::addValues(const std::vector<int> & ys)
|
|
{
|
|
float x = 0;
|
|
float width = 2; // TODO warn : hard coded value!
|
|
for(unsigned int i=0; i<ys.size(); ++i)
|
|
{
|
|
if(_items.size())
|
|
{
|
|
UPlotItem * lastItem = (UPlotItem *)_items.last();
|
|
x = lastItem->data().x() + _xIncrement;
|
|
}
|
|
else
|
|
{
|
|
x = _xStart;
|
|
}
|
|
this->_addValue(new UPlotItem(x,ys.at(i),width));
|
|
}
|
|
emit dataChanged(this);
|
|
}
|
|
|
|
void UPlotCurve::addValues(const std::vector<float> & ys)
|
|
{
|
|
float x = 0;
|
|
float width = 2; // TODO warn : hard coded value!
|
|
for(unsigned int i=0; i<ys.size(); ++i)
|
|
{
|
|
if(_items.size())
|
|
{
|
|
UPlotItem * lastItem = (UPlotItem *)_items.last();
|
|
x = lastItem->data().x() + _xIncrement;
|
|
}
|
|
else
|
|
{
|
|
x = _xStart;
|
|
}
|
|
this->_addValue(new UPlotItem(x,ys.at(i),width));
|
|
}
|
|
emit dataChanged(this);
|
|
}
|
|
|
|
int UPlotCurve::removeItem(int index)
|
|
{
|
|
if(index >= 0 && index < _items.size())
|
|
{
|
|
if(index!=0)
|
|
{
|
|
index-=1;
|
|
delete _items.takeAt(index); // the line
|
|
}
|
|
else if(_items.size()>1)
|
|
{
|
|
delete _items.takeAt(index+1); // the line
|
|
}
|
|
UPlotItem * item = (UPlotItem *)_items.takeAt(index); // the plot item
|
|
//Update min/max
|
|
if(_minMax.size() == 4)
|
|
{
|
|
if(item->data().x() == _minMax[0] || item->data().x() == _minMax[1] ||
|
|
item->data().y() == _minMax[2] || item->data().y() == _minMax[3])
|
|
{
|
|
if(_items.size())
|
|
{
|
|
UPlotItem * tmp = (UPlotItem *)_items.at(0);
|
|
float x = tmp->data().x();
|
|
float y = tmp->data().y();
|
|
_minMax[0]=x;
|
|
_minMax[1]=x;
|
|
_minMax[2]=y;
|
|
_minMax[3]=y;
|
|
for(int i = 2; i<_items.size(); i+=2)
|
|
{
|
|
tmp = (UPlotItem*)_items.at(i);
|
|
x = tmp->data().x();
|
|
y = tmp->data().y();
|
|
if(x<_minMax[0]) _minMax[0] = x;
|
|
if(x>_minMax[1]) _minMax[1] = x;
|
|
if(y<_minMax[2]) _minMax[2] = y;
|
|
if(y>_minMax[3]) _minMax[3] = y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_minMax = QVector<float>();
|
|
}
|
|
}
|
|
}
|
|
delete item;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
void UPlotCurve::removeItem(UPlotItem * item) // ownership is transfered to the caller
|
|
{
|
|
for(int i=0; i<_items.size(); ++i)
|
|
{
|
|
if(_items.at(i) == item)
|
|
{
|
|
if(i!=0)
|
|
{
|
|
i-=1;
|
|
delete _items[i];
|
|
_items.removeAt(i);
|
|
}
|
|
else if(_items.size()>1)
|
|
{
|
|
delete _items[i+1];
|
|
_items.removeAt(i+1);
|
|
}
|
|
item->scene()->removeItem(item);
|
|
_items.removeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::clear()
|
|
{
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("%s", this->name().toStdString().c_str());
|
|
#endif
|
|
qDeleteAll(_rootItem->childItems());
|
|
_items.clear();
|
|
}
|
|
|
|
void UPlotCurve::setPen(const QPen & pen)
|
|
{
|
|
_pen = pen;
|
|
for(int i=1; i<_items.size(); i+=2)
|
|
{
|
|
((QGraphicsLineItem*) _items.at(i))->setPen(_pen);
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::setBrush(const QBrush & brush)
|
|
{
|
|
_brush = brush;
|
|
ULOGGER_WARN("Not used...");
|
|
}
|
|
|
|
void UPlotCurve::setItemsColor(const QColor & color)
|
|
{
|
|
if(color.isValid())
|
|
{
|
|
_itemsColor.setRgb(color.red(), color.green(), color.blue(), _itemsColor.alpha());
|
|
for(int i=0; i<_items.size(); i+=2)
|
|
{
|
|
QPen pen = ((UPlotItem*) _items.at(i))->pen();
|
|
pen.setColor(_itemsColor);
|
|
((UPlotItem*) _items.at(i))->setPen(pen);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::update(float scaleX, float scaleY, float offsetX, float offsetY, float xDir, float yDir, int maxItemsKept)
|
|
{
|
|
//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());
|
|
//make sure direction values are 1 or -1
|
|
xDir<0?xDir=-1:xDir=1;
|
|
yDir<0?yDir=-1:yDir=1;
|
|
|
|
bool hide = false;
|
|
int j=0;
|
|
for(int i=_items.size()-1; i>=0; --i)
|
|
{
|
|
if(i%2 == 0)
|
|
{
|
|
UPlotItem * item = (UPlotItem *)_items.at(i);
|
|
if(hide)
|
|
{
|
|
if(maxItemsKept == 0 || j <= maxItemsKept)
|
|
{
|
|
// if not visible, stop looping... all other items are normally already hidden
|
|
if(!item->isVisible())
|
|
{
|
|
break;
|
|
}
|
|
item->setVisible(false);
|
|
}
|
|
else
|
|
{
|
|
//remove the item with his line
|
|
i = this->removeItem(i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QPointF newPos(((xDir*item->data().x()+offsetX)*scaleX-item->rect().width()/2.0f),
|
|
((yDir*item->data().y()+offsetY)*scaleY-item->rect().width()/2.0f));
|
|
if(!item->isVisible())
|
|
{
|
|
item->setVisible(true);
|
|
}
|
|
item->setPos(newPos);
|
|
}
|
|
++j;
|
|
}
|
|
else
|
|
{
|
|
if(hide)
|
|
{
|
|
_items.at(i)->setVisible(false);
|
|
}
|
|
else
|
|
{
|
|
UPlotItem * from = (UPlotItem *)_items.at(i-1);
|
|
UPlotItem * to = (UPlotItem *)_items.at(i+1);
|
|
QGraphicsLineItem * lineItem = (QGraphicsLineItem *)_items.at(i);
|
|
lineItem->setLine((xDir*from->data().x()+offsetX)*scaleX,
|
|
(yDir*from->data().y()+offsetY)*scaleY,
|
|
(xDir*to->data().x()+offsetX)*scaleX,
|
|
(yDir*to->data().y()+offsetY)*scaleY);
|
|
if(!lineItem->isVisible())
|
|
{
|
|
lineItem->setVisible(true);
|
|
}
|
|
//Don't update not visible items
|
|
// (Detect also if the curve goes forward or backward)
|
|
QLineF line = lineItem->line();
|
|
if((line.x1() <= line.x2() && line.x2() < 0-((line.x2() - line.x1()))) ||
|
|
(line.x1() > line.x2() && line.x2() > lineItem->scene()->sceneRect().width() + ((line.x1() - line.x2()))))
|
|
{
|
|
hide = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void UPlotCurve::draw(QPainter * painter, const QRect & limits)
|
|
{
|
|
if(painter)
|
|
{
|
|
for(int i=_items.size()-1; i>=0 && _items.at(i)->isVisible(); i-=2)
|
|
{
|
|
//plotItem
|
|
const UPlotItem * item = (const UPlotItem *)_items.at(i);
|
|
int x = (int)item->x();
|
|
if(x<0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// draw line in first
|
|
if(i-1>=0)
|
|
{
|
|
//lineItem
|
|
const QGraphicsLineItem * lineItem = (const QGraphicsLineItem *)_items.at(i-1);
|
|
QLine line = lineItem->line().toLine();
|
|
if(limits.contains(line.p1()) || limits.contains(line.p2()))
|
|
{
|
|
QPointF intersection;
|
|
QLineF::IntersectType type;
|
|
type = lineItem->line().intersect(QLineF(limits.topLeft(), limits.bottomLeft()), &intersection);
|
|
if(type == QLineF::BoundedIntersection)
|
|
{
|
|
!limits.contains(line.p1())?line.setP1(intersection.toPoint()):line.setP2(intersection.toPoint());
|
|
}
|
|
else
|
|
{
|
|
type = lineItem->line().intersect(QLineF(limits.topLeft(), limits.topRight()), &intersection);
|
|
if(type == QLineF::BoundedIntersection)
|
|
{
|
|
!limits.contains(line.p1())?line.setP1(intersection.toPoint()):line.setP2(intersection.toPoint());
|
|
}
|
|
else
|
|
{
|
|
type = lineItem->line().intersect(QLineF(limits.bottomLeft(), limits.bottomRight()), &intersection);
|
|
if(type == QLineF::BoundedIntersection)
|
|
{
|
|
!limits.contains(line.p1())?line.setP1(intersection.toPoint()):line.setP2(intersection.toPoint());
|
|
}
|
|
else
|
|
{
|
|
type = lineItem->line().intersect(QLineF(limits.topRight(), limits.bottomRight()), &intersection);
|
|
if(type == QLineF::BoundedIntersection)
|
|
{
|
|
!limits.contains(line.p1())?line.setP1(intersection.toPoint()):line.setP2(intersection.toPoint());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
painter->save();
|
|
painter->setPen(this->pen());
|
|
painter->setBrush(this->brush());
|
|
painter->drawLine(line);
|
|
painter->restore();
|
|
}
|
|
}
|
|
|
|
if(limits.contains(item->pos().toPoint()) && limits.contains((item->pos() + QPointF(item->rect().width(), item->rect().height())).toPoint()))
|
|
{
|
|
painter->save();
|
|
painter->setPen(QPen(_itemsColor));
|
|
painter->drawEllipse(item->pos()+QPointF(item->rect().width()/2, item->rect().height()/2), (int)item->rect().width()/2, (int)item->rect().height()/2);
|
|
painter->restore();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int UPlotCurve::itemsSize() const
|
|
{
|
|
return _items.size();
|
|
}
|
|
|
|
QPointF UPlotCurve::getItemData(int index)
|
|
{
|
|
QPointF data;
|
|
//make sure the index point to a PlotItem {PlotItem, line, PlotItem, line...}
|
|
if(index>=0 && index < _items.size() && index % 2 == 0 )
|
|
{
|
|
data = ((UPlotItem*)_items.at(index))->data();
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("Wrong index, not pointing on a PlotItem");
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void UPlotCurve::setVisible(bool visible)
|
|
{
|
|
_visible = visible;
|
|
for(int i=0; i<_items.size(); ++i)
|
|
{
|
|
_items.at(i)->setVisible(visible);
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::setXIncrement(float increment)
|
|
{
|
|
_xIncrement = increment;
|
|
}
|
|
|
|
void UPlotCurve::setXStart(float val)
|
|
{
|
|
_xStart = val;
|
|
}
|
|
|
|
void UPlotCurve::setData(QVector<UPlotItem*> & data)
|
|
{
|
|
this->clear();
|
|
for(int i = 0; i<data.size(); ++i)
|
|
{
|
|
this->addValue(data[i]);
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::setData(const QVector<float> & x, const QVector<float> & y)
|
|
{
|
|
if(x.size() == y.size())
|
|
{
|
|
//match the size of the current data
|
|
int margin = int((_items.size()+1)/2) - x.size();
|
|
while(margin < 0)
|
|
{
|
|
UPlotItem * newItem = new UPlotItem(0, 0, 2);
|
|
this->_addValue(newItem);
|
|
++margin;
|
|
}
|
|
while(margin > 0)
|
|
{
|
|
this->removeItem(0);
|
|
--margin;
|
|
}
|
|
|
|
// update values
|
|
int index = 0;
|
|
QVector<float>::const_iterator i=x.begin();
|
|
QVector<float>::const_iterator j=y.begin();
|
|
for(; i!=x.end() && j!=y.end(); ++i, ++j, index+=2)
|
|
{
|
|
((UPlotItem*)_items[index])->setData(QPointF(*i, *j));
|
|
}
|
|
|
|
//reset minMax, this will force the plot to update the axes
|
|
this->updateMinMax();
|
|
emit dataChanged(this);
|
|
}
|
|
else if(y.size()>0 && x.size()==0)
|
|
{
|
|
this->setData(y);
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("Data vectors have not the same size.");
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::setData(const std::vector<float> & x, const std::vector<float> & y)
|
|
{
|
|
if(x.size() == y.size())
|
|
{
|
|
//match the size of the current data
|
|
int margin = int((_items.size()+1)/2) - int(x.size());
|
|
while(margin < 0)
|
|
{
|
|
UPlotItem * newItem = new UPlotItem(0, 0, 2);
|
|
this->_addValue(newItem);
|
|
++margin;
|
|
}
|
|
while(margin > 0)
|
|
{
|
|
this->removeItem(0);
|
|
--margin;
|
|
}
|
|
|
|
// update values
|
|
int index = 0;
|
|
std::vector<float>::const_iterator i=x.begin();
|
|
std::vector<float>::const_iterator j=y.begin();
|
|
for(; i!=x.end() && j!=y.end(); ++i, ++j, index+=2)
|
|
{
|
|
((UPlotItem*)_items[index])->setData(QPointF(*i, *j));
|
|
}
|
|
|
|
//reset minMax, this will force the plot to update the axes
|
|
this->updateMinMax();
|
|
emit dataChanged(this);
|
|
}
|
|
else if(y.size()>0 && x.size()==0)
|
|
{
|
|
this->setData(y);
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("Data vectors have not the same size.");
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::setData(const QVector<float> & y)
|
|
{
|
|
this->setData(y.toStdVector());
|
|
}
|
|
|
|
void UPlotCurve::setData(const std::vector<float> & y)
|
|
{
|
|
//match the size of the current data
|
|
int margin = int((_items.size()+1)/2) - int(y.size());
|
|
while(margin < 0)
|
|
{
|
|
UPlotItem * newItem = new UPlotItem(0, 0, 2);
|
|
this->_addValue(newItem);
|
|
++margin;
|
|
}
|
|
while(margin > 0)
|
|
{
|
|
this->removeItem(0);
|
|
--margin;
|
|
}
|
|
|
|
// update values
|
|
int index = 0;
|
|
float x = 0;
|
|
std::vector<float>::const_iterator j=y.begin();
|
|
for(; j!=y.end(); ++j, index+=2)
|
|
{
|
|
((UPlotItem*)_items[index])->setData(QPointF(x++, *j));
|
|
}
|
|
|
|
//reset minMax, this will force the plot to update the axes
|
|
this->updateMinMax();
|
|
emit dataChanged(this);
|
|
}
|
|
|
|
void UPlotCurve::getData(QVector<float> & x, QVector<float> & y) const
|
|
{
|
|
x.clear();
|
|
y.clear();
|
|
if(_items.size())
|
|
{
|
|
x.resize((_items.size()-1)/2+1);
|
|
y.resize(x.size());
|
|
int j=0;
|
|
for(int i=0; i<_items.size(); i+=2)
|
|
{
|
|
x[j] = ((UPlotItem*)_items.at(i))->data().x();
|
|
y[j++] = ((UPlotItem*)_items.at(i))->data().y();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotCurve::getData(QMap<float,float> & data) const
|
|
{
|
|
data.clear();
|
|
if(_items.size())
|
|
{
|
|
for(int i=0; i<_items.size(); i+=2)
|
|
{
|
|
data.insert(((UPlotItem*)_items.at(i))->data().x(), ((UPlotItem*)_items.at(i))->data().y());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UPlotCurveThreshold::UPlotCurveThreshold(const QString & name, float thesholdValue, Qt::Orientation orientation, QObject * parent) :
|
|
UPlotCurve(name, parent),
|
|
_orientation(orientation)
|
|
{
|
|
if(_orientation == Qt::Horizontal)
|
|
{
|
|
this->addValue(0, thesholdValue);
|
|
this->addValue(1, thesholdValue);
|
|
}
|
|
else
|
|
{
|
|
this->addValue(thesholdValue, 0);
|
|
this->addValue(thesholdValue, 1);
|
|
}
|
|
}
|
|
|
|
UPlotCurveThreshold::~UPlotCurveThreshold()
|
|
{
|
|
|
|
}
|
|
|
|
void UPlotCurveThreshold::setThreshold(float threshold)
|
|
{
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("%f", threshold);
|
|
#endif
|
|
if(_items.size() == 3)
|
|
{
|
|
UPlotItem * item = 0;
|
|
if(_orientation == Qt::Horizontal)
|
|
{
|
|
item = (UPlotItem*)_items.at(0);
|
|
item->setData(QPointF(item->data().x(), threshold));
|
|
item = (UPlotItem*)_items.at(2);
|
|
item->setData(QPointF(item->data().x(), threshold));
|
|
}
|
|
else
|
|
{
|
|
item = (UPlotItem*)_items.at(0);
|
|
item->setData(QPointF(threshold, item->data().y()));
|
|
item = (UPlotItem*)_items.at(2);
|
|
item->setData(QPointF(threshold, item->data().y()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("A threshold must has only 3 items (1 PlotItem + 1 QGraphicsLineItem + 1 PlotItem)");
|
|
}
|
|
}
|
|
|
|
void UPlotCurveThreshold::setOrientation(Qt::Orientation orientation)
|
|
{
|
|
if(_orientation != orientation)
|
|
{
|
|
_orientation = orientation;
|
|
if(_items.size() == 3)
|
|
{
|
|
UPlotItem * item = 0;
|
|
item = (UPlotItem*)_items.at(0);
|
|
item->setData(QPointF(item->data().y(), item->data().x()));
|
|
item = (UPlotItem*)_items.at(2);
|
|
item->setData(QPointF(item->data().y(), item->data().x()));
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("A threshold must has only 3 items (1 PlotItem + 1 QGraphicsLineItem + 1 PlotItem)");
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotCurveThreshold::update(float scaleX, float scaleY, float offsetX, float offsetY, float xDir, float yDir, int maxItemsKept)
|
|
{
|
|
if(_items.size() == 3)
|
|
{
|
|
if(_plot)
|
|
{
|
|
UPlotItem * item = 0;
|
|
if(_orientation == Qt::Horizontal)
|
|
{
|
|
//(xDir*item->data().x()+offsetX)*scaleX
|
|
item = (UPlotItem*)_items.at(0);
|
|
item->setData(QPointF(-(offsetX-item->rect().width()/scaleX)/xDir, item->data().y()));
|
|
item = (UPlotItem*)_items.at(2);
|
|
item->setData(QPointF( (_plot->sceneRect().width()/scaleX-(offsetX+item->rect().width()/scaleX))/xDir, item->data().y()));
|
|
}
|
|
else
|
|
{
|
|
item = (UPlotItem*)_items.at(0);
|
|
item->setData(QPointF(item->data().x(), -(offsetY-item->rect().height()/scaleY)/yDir));
|
|
item = (UPlotItem*)_items.at(2);
|
|
item->setData(QPointF(item->data().x(), (_plot->sceneRect().height()/scaleY-(offsetY+item->rect().height()/scaleY))/yDir));
|
|
}
|
|
this->updateMinMax();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("A threshold must has only 3 items (1 PlotItem + 1 QGraphicsLineItem + 1 PlotItem)");
|
|
}
|
|
UPlotCurve::update(scaleX, scaleY, offsetX, offsetY, xDir, yDir, maxItemsKept);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UPlotAxis::UPlotAxis(Qt::Orientation orientation, float min, float max, QWidget * parent) :
|
|
QWidget(parent),
|
|
_orientation(orientation),
|
|
_reversed(false),
|
|
_gradMaxDigits(4),
|
|
_border(0)
|
|
{
|
|
if(_orientation == Qt::Vertical)
|
|
{
|
|
_reversed = true; // default bottom->up
|
|
}
|
|
#ifdef _WIN32
|
|
this->setMinimumSize(15, 25);
|
|
#else
|
|
this->setMinimumSize(15, 25);
|
|
#endif
|
|
this->setAxis(min, max); // this initialize all attributes
|
|
}
|
|
|
|
UPlotAxis::~UPlotAxis()
|
|
{
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("");
|
|
#endif
|
|
}
|
|
|
|
// Vertical :bottom->up, horizontal :right->left
|
|
void UPlotAxis::setReversed(bool reversed)
|
|
{
|
|
if(_reversed != reversed)
|
|
{
|
|
float min = _min;
|
|
_min = _max;
|
|
_max = min;
|
|
}
|
|
_reversed = reversed;
|
|
}
|
|
|
|
void UPlotAxis::setAxis(float & min, float & max)
|
|
{
|
|
int borderMin = 0;
|
|
int borderMax = 0;
|
|
if(_orientation == Qt::Vertical)
|
|
{
|
|
borderMin = borderMax = this->fontMetrics().height()/2;
|
|
}
|
|
else
|
|
{
|
|
borderMin = this->fontMetrics().width(QString::number(_min,'g',_gradMaxDigits))/2;
|
|
borderMax = this->fontMetrics().width(QString::number(_max,'g',_gradMaxDigits))/2;
|
|
}
|
|
int border = borderMin>borderMax?borderMin:borderMax;
|
|
int borderDelta;
|
|
int length;
|
|
if(_orientation == Qt::Vertical)
|
|
{
|
|
length = (this->height()-border*2);
|
|
}
|
|
else
|
|
{
|
|
length = (this->width()-border*2);
|
|
}
|
|
|
|
if(length <= 70)
|
|
{
|
|
_count = 5;
|
|
}
|
|
else if(length <= 175)
|
|
{
|
|
_count = 10;
|
|
}
|
|
else if(length <= 350)
|
|
{
|
|
_count = 20;
|
|
}
|
|
else if(length <= 700)
|
|
{
|
|
_count = 40;
|
|
}
|
|
else if(length <= 1000)
|
|
{
|
|
_count = 60;
|
|
}
|
|
else if(length <= 1300)
|
|
{
|
|
_count = 80;
|
|
}
|
|
else
|
|
{
|
|
_count = 100;
|
|
}
|
|
|
|
// Rounding min and max
|
|
if(min != max)
|
|
{
|
|
float mul = 1;
|
|
float rangef = max - min;
|
|
int countStep = _count/5;
|
|
float val;
|
|
for(int i=0; i<6; ++i)
|
|
{
|
|
val = (rangef/float(countStep)) * mul;
|
|
if( val >= 1.0f && val < 10.0f)
|
|
{
|
|
break;
|
|
}
|
|
else if(val<1)
|
|
{
|
|
mul *= 10.0f;
|
|
}
|
|
else
|
|
{
|
|
mul /= 10.0f;
|
|
}
|
|
}
|
|
//ULOGGER_DEBUG("min=%f, max=%f", min, max);
|
|
int minR = min*mul-0.9;
|
|
int maxR = max*mul+0.9;
|
|
min = float(minR)/mul;
|
|
max = float(maxR)/mul;
|
|
//ULOGGER_DEBUG("mul=%f, minR=%d, maxR=%d,countStep=%d", mul, minR, maxR, countStep);
|
|
}
|
|
|
|
_min = min;
|
|
_max = max;
|
|
|
|
if(_reversed)
|
|
{
|
|
_min = _max;
|
|
_max = min;
|
|
}
|
|
|
|
if(_orientation == Qt::Vertical)
|
|
{
|
|
_step = length/_count;
|
|
borderDelta = length - (_step*_count);
|
|
}
|
|
else
|
|
{
|
|
_step = length/_count;
|
|
borderDelta = length - (_step*_count);
|
|
}
|
|
|
|
if(borderDelta%2 != 0)
|
|
{
|
|
borderDelta+=1;
|
|
}
|
|
|
|
_border = border + borderDelta/2;
|
|
|
|
//Resize estimation
|
|
if(_orientation == Qt::Vertical)
|
|
{
|
|
int minWidth = 0;
|
|
for (int i = 0; i <= _count; i+=5)
|
|
{
|
|
QString n(QString::number(_min + (i/5)*((_max-_min)/(_count/5)),'g',_gradMaxDigits));
|
|
if(this->fontMetrics().width(n) > minWidth)
|
|
{
|
|
minWidth = this->fontMetrics().width(n);
|
|
}
|
|
}
|
|
this->setMinimumWidth(15+minWidth);
|
|
}
|
|
}
|
|
|
|
void UPlotAxis::paintEvent(QPaintEvent * event)
|
|
{
|
|
QPainter painter(this);
|
|
if(_orientation == Qt::Vertical)
|
|
{
|
|
painter.translate(0, _border);
|
|
for (int i = 0; i <= _count; ++i)
|
|
{
|
|
if(i%5 == 0)
|
|
{
|
|
painter.drawLine(this->width(), 0, this->width()-10, 0);
|
|
QLabel n(QString::number(_min + (i/5)*((_max-_min)/(_count/5)),'g',_gradMaxDigits));
|
|
painter.drawText(this->width()-(12+n.sizeHint().width()), n.sizeHint().height()/2-2, n.text());
|
|
}
|
|
else
|
|
{
|
|
painter.drawLine(this->width(), 0, this->width()-5, 0);
|
|
}
|
|
painter.translate(0, _step);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
painter.translate(_border, 0);
|
|
for (int i = 0; i <= _count; ++i)
|
|
{
|
|
if(i%5 == 0)
|
|
{
|
|
painter.drawLine(0, 0, 0, 10);
|
|
QLabel n(QString::number(_min + (i/5)*((_max-_min)/(_count/5)),'g',_gradMaxDigits));
|
|
painter.drawText(-(n.sizeHint().width()/2)+1, 22, n.text());
|
|
}
|
|
else
|
|
{
|
|
painter.drawLine(0, 0, 0, 5);
|
|
}
|
|
painter.translate(_step, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
UPlotLegendItem::UPlotLegendItem(UPlotCurve * curve, QWidget * parent) :
|
|
QPushButton(parent),
|
|
_curve(curve)
|
|
{
|
|
QString nameSpaced = curve->name();
|
|
nameSpaced.replace('_', ' ');
|
|
this->setText(nameSpaced);
|
|
|
|
this->setIcon(QIcon(this->createSymbol(curve->pen(), curve->brush())));
|
|
this->setIconSize(QSize(25,20));
|
|
|
|
_aChangeText = new QAction(tr("Change text..."), this);
|
|
_aResetText = new QAction(tr("Reset text..."), this);
|
|
_aChangeColor = new QAction(tr("Change color..."), this);
|
|
_aCopyToClipboard = new QAction(tr("Copy curve data to clipboard"), this);
|
|
_aShowStdDev = new QAction(tr("Show std deviation"), this);
|
|
_aShowStdDev->setCheckable(true);
|
|
_aMoveUp = new QAction(tr("Move up"), this);
|
|
_aMoveDown = new QAction(tr("Move down"), this);
|
|
_aRemoveCurve = new QAction(tr("Remove this curve"), this);
|
|
_menu = new QMenu(tr("Curve"), this);
|
|
_menu->addAction(_aChangeText);
|
|
_menu->addAction(_aResetText);
|
|
_menu->addAction(_aChangeColor);
|
|
_menu->addAction(_aCopyToClipboard);
|
|
_menu->addAction(_aShowStdDev);
|
|
_menu->addSeparator();
|
|
_menu->addAction(_aMoveUp);
|
|
_menu->addAction(_aMoveDown);
|
|
_menu->addSeparator();
|
|
_menu->addAction(_aRemoveCurve);
|
|
}
|
|
|
|
UPlotLegendItem::~UPlotLegendItem()
|
|
{
|
|
|
|
}
|
|
void UPlotLegendItem::contextMenuEvent(QContextMenuEvent * event)
|
|
{
|
|
QAction * action = _menu->exec(event->globalPos());
|
|
if(action == _aChangeText)
|
|
{
|
|
bool ok;
|
|
QString text = QInputDialog::getText(this, _aChangeText->text(), tr("Name :"), QLineEdit::Normal, this->text(), &ok);
|
|
if(ok && !text.isEmpty())
|
|
{
|
|
this->setText(text);
|
|
}
|
|
}
|
|
else if(action == _aResetText)
|
|
{
|
|
if(_curve)
|
|
{
|
|
this->setText(_curve->name());
|
|
}
|
|
}
|
|
else if(action == _aChangeColor)
|
|
{
|
|
if(_curve)
|
|
{
|
|
QPen pen = _curve->pen();
|
|
QColor color = QColorDialog::getColor(pen.color(), this);
|
|
if(color.isValid())
|
|
{
|
|
pen.setColor(color);
|
|
_curve->setPen(pen);
|
|
this->setIcon(QIcon(this->createSymbol(_curve->pen(), _curve->brush())));
|
|
}
|
|
}
|
|
}
|
|
else if (action == _aCopyToClipboard)
|
|
{
|
|
if(_curve)
|
|
{
|
|
QVector<float> x;
|
|
QVector<float> y;
|
|
_curve->getData(x, y);
|
|
QString textX;
|
|
QString textY;
|
|
for(int i=0; i<x.size(); ++i)
|
|
{
|
|
textX.append(QString::number(x[i]));
|
|
textY.append(QString::number(y[i]));
|
|
if(i+1<x.size())
|
|
{
|
|
textX.append(' ');
|
|
textY.append(' ');
|
|
}
|
|
}
|
|
QClipboard * clipboard = QApplication::clipboard();
|
|
clipboard->setText((textX+"\n")+textY);
|
|
}
|
|
}
|
|
else if(action == _aShowStdDev)
|
|
{
|
|
if(_aShowStdDev->isChecked())
|
|
{
|
|
connect(_curve, SIGNAL(dataChanged(const UPlotCurve *)), this, SLOT(updateStdDev()));
|
|
}
|
|
else
|
|
{
|
|
disconnect(_curve, SIGNAL(dataChanged(const UPlotCurve *)), this, SLOT(updateStdDev()));
|
|
QString nameSpaced = _curve->name();
|
|
nameSpaced.replace('_', ' ');
|
|
this->setText(nameSpaced);
|
|
}
|
|
}
|
|
else if(action == _aRemoveCurve)
|
|
{
|
|
emit legendItemRemoved(_curve);
|
|
}
|
|
else if(action == _aMoveUp)
|
|
{
|
|
emit moveUpRequest(this);
|
|
}
|
|
else if(action == _aMoveDown)
|
|
{
|
|
emit moveDownRequest(this);
|
|
}
|
|
}
|
|
|
|
QPixmap UPlotLegendItem::createSymbol(const QPen & pen, const QBrush & brush)
|
|
{
|
|
QPixmap pixmap(50, 50);
|
|
pixmap.fill(Qt::transparent);
|
|
QPainter painter(&pixmap);
|
|
QPen p = pen;
|
|
p.setWidthF(4.0);
|
|
painter.setPen(p);
|
|
painter.drawLine(0.0, 25.0, 50.0, 25.0);
|
|
return pixmap;
|
|
}
|
|
|
|
void UPlotLegendItem::updateStdDev()
|
|
{
|
|
QVector<float> x, y;
|
|
_curve->getData(x, y);
|
|
float stdDev = std::sqrt(uVariance(y.data(), y.size()));
|
|
QString nameSpaced = _curve->name();
|
|
nameSpaced.replace('_', ' ');
|
|
nameSpaced += QString(" (%1=%2)").arg(QChar(0xc3, 0x03)).arg(stdDev);
|
|
this->setText(nameSpaced);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UPlotLegend::UPlotLegend(QWidget * parent) :
|
|
QWidget(parent),
|
|
_flat(true)
|
|
{
|
|
//menu
|
|
_aUseFlatButtons = new QAction(tr("Use flat buttons"), this);
|
|
_aUseFlatButtons->setCheckable(true);
|
|
_aUseFlatButtons->setChecked(_flat);
|
|
_aCopyAllCurveToClipboard = new QAction(tr("Copy all curve data to clipboard"), this);
|
|
_menu = new QMenu(tr("Legend"), this);
|
|
_menu->addAction(_aUseFlatButtons);
|
|
_menu->addAction(_aCopyAllCurveToClipboard);
|
|
|
|
QVBoxLayout * vLayout = new QVBoxLayout(this);
|
|
vLayout->setContentsMargins(0,0,0,0);
|
|
this->setLayout(vLayout);
|
|
vLayout->addStretch(0);
|
|
vLayout->setSpacing(0);
|
|
}
|
|
|
|
UPlotLegend::~UPlotLegend()
|
|
{
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("");
|
|
#endif
|
|
}
|
|
|
|
void UPlotLegend::setFlat(bool on)
|
|
{
|
|
if(_flat != on)
|
|
{
|
|
_flat = on;
|
|
QList<UPlotLegendItem*> items = this->findChildren<UPlotLegendItem*>();
|
|
for(int i=0; i<items.size(); ++i)
|
|
{
|
|
items.at(i)->setFlat(_flat);
|
|
items.at(i)->setChecked(!items.at(i)->isChecked());
|
|
}
|
|
_aUseFlatButtons->setChecked(_flat);
|
|
}
|
|
}
|
|
|
|
void UPlotLegend::addItem(UPlotCurve * curve)
|
|
{
|
|
if(curve)
|
|
{
|
|
UPlotLegendItem * legendItem = new UPlotLegendItem(curve, this);
|
|
legendItem->setAutoDefault(false);
|
|
legendItem->setFlat(_flat);
|
|
legendItem->setCheckable(true);
|
|
legendItem->setChecked(false);
|
|
connect(legendItem, SIGNAL(toggled(bool)), this, SLOT(redirectToggled(bool)));
|
|
connect(legendItem, SIGNAL(legendItemRemoved(const UPlotCurve *)), this, SLOT(removeLegendItem(const UPlotCurve *)));
|
|
connect(legendItem, SIGNAL(moveUpRequest(UPlotLegendItem *)), this, SLOT(moveUp(UPlotLegendItem *)));
|
|
connect(legendItem, SIGNAL(moveDownRequest(UPlotLegendItem *)), this, SLOT(moveDown(UPlotLegendItem *)));
|
|
|
|
// layout
|
|
QHBoxLayout * hLayout = new QHBoxLayout();
|
|
hLayout->addWidget(legendItem);
|
|
hLayout->addStretch(0);
|
|
hLayout->setMargin(0);
|
|
|
|
// add to the legend
|
|
((QVBoxLayout*)this->layout())->insertLayout(this->layout()->count()-1, hLayout);
|
|
}
|
|
}
|
|
|
|
bool UPlotLegend::remove(const UPlotCurve * curve)
|
|
{
|
|
QList<UPlotLegendItem *> items = this->findChildren<UPlotLegendItem*>();
|
|
for(int i=0; i<items.size(); ++i)
|
|
{
|
|
if(items.at(i)->curve() == curve)
|
|
{
|
|
delete items.at(i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UPlotLegend::removeLegendItem(const UPlotCurve * curve)
|
|
{
|
|
if(this->remove(curve))
|
|
{
|
|
emit legendItemRemoved(curve);
|
|
}
|
|
}
|
|
|
|
void UPlotLegend::moveUp(UPlotLegendItem * item)
|
|
{
|
|
int index = -1;
|
|
QLayoutItem * layoutItem = 0;
|
|
for(int i=0; i<this->layout()->count(); ++i)
|
|
{
|
|
if(this->layout()->itemAt(i)->layout() &&
|
|
this->layout()->itemAt(i)->layout()->indexOf(item) != -1)
|
|
{
|
|
layoutItem = this->layout()->itemAt(i);
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
if(index > 0 && layoutItem)
|
|
{
|
|
this->layout()->removeItem(layoutItem);
|
|
QHBoxLayout * hLayout = new QHBoxLayout();
|
|
hLayout->addWidget(layoutItem->layout()->itemAt(0)->widget());
|
|
hLayout->addStretch(0);
|
|
hLayout->setMargin(0);
|
|
((QVBoxLayout*)this->layout())->insertLayout(index-1, hLayout);
|
|
delete layoutItem;
|
|
emit legendItemMoved(item->curve(), index-1);
|
|
}
|
|
}
|
|
|
|
void UPlotLegend::moveDown(UPlotLegendItem * item)
|
|
{
|
|
int index = -1;
|
|
QLayoutItem * layoutItem = 0;
|
|
for(int i=0; i<this->layout()->count(); ++i)
|
|
{
|
|
if(this->layout()->itemAt(i)->layout() &&
|
|
this->layout()->itemAt(i)->layout()->indexOf(item) != -1)
|
|
{
|
|
layoutItem = this->layout()->itemAt(i);
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
if(index < this->layout()->count()-2 && layoutItem)
|
|
{
|
|
this->layout()->removeItem(layoutItem);
|
|
QHBoxLayout * hLayout = new QHBoxLayout();
|
|
hLayout->addWidget(layoutItem->layout()->itemAt(0)->widget());
|
|
hLayout->addStretch(0);
|
|
hLayout->setMargin(0);
|
|
((QVBoxLayout*)this->layout())->insertLayout(index+1, hLayout);
|
|
delete layoutItem;
|
|
emit legendItemMoved(item->curve(), index+1);
|
|
}
|
|
}
|
|
|
|
void UPlotLegend::contextMenuEvent(QContextMenuEvent * event)
|
|
{
|
|
QAction * action = _menu->exec(event->globalPos());
|
|
if(action == _aUseFlatButtons)
|
|
{
|
|
this->setFlat(_aUseFlatButtons->isChecked());
|
|
}
|
|
else if(action == _aCopyAllCurveToClipboard)
|
|
{
|
|
QList<UPlotLegendItem *> items = this->findChildren<UPlotLegendItem*>();
|
|
if(items.size())
|
|
{
|
|
QMap<float,float> firstData;
|
|
QVector<QVector<float> > axes;
|
|
for(int i=0; i<items.size(); ++i)
|
|
{
|
|
QMap<float, float> data;
|
|
items.at(i)->curve()->getData(data);
|
|
|
|
if(i==0)
|
|
{
|
|
firstData = data;
|
|
}
|
|
else
|
|
{
|
|
QVector<float> y(firstData.size(), 0.0f);
|
|
// ust to make usre that we have the same number of data on each curve, set 0 for unknowns
|
|
int j=0;
|
|
for(QMap<float,float>::iterator iter=firstData.begin(); iter!=firstData.end(); ++iter)
|
|
{
|
|
if(data.contains(iter.key()))
|
|
{
|
|
y[j] = data.value(iter.key());
|
|
}
|
|
++j;
|
|
}
|
|
axes.push_back(y);
|
|
}
|
|
}
|
|
if(firstData.size())
|
|
{
|
|
axes.push_front(firstData.values().toVector());
|
|
axes.push_front(firstData.keys().toVector());
|
|
QString text;
|
|
for(int i=0; i<axes.size(); ++i)
|
|
{
|
|
for(int j=0; j<axes[i].size(); ++j)
|
|
{
|
|
text.append(QString::number(axes[i][j]));
|
|
if(j+1<axes[i].size())
|
|
{
|
|
text.append(' ');
|
|
}
|
|
}
|
|
if(i+1<axes.size())
|
|
{
|
|
text.append("\n");
|
|
}
|
|
}
|
|
QClipboard * clipboard = QApplication::clipboard();
|
|
clipboard->setText(text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlotLegend::redirectToggled(bool toggled)
|
|
{
|
|
if(sender())
|
|
{
|
|
UPlotLegendItem * item = qobject_cast<UPlotLegendItem*>(sender());
|
|
if(item)
|
|
{
|
|
emit legendItemToggled(item->curve(), _flat?!toggled:toggled);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UOrientableLabel::UOrientableLabel(const QString & text, Qt::Orientation orientation, QWidget * parent) :
|
|
QLabel(text, parent),
|
|
_orientation(orientation)
|
|
{
|
|
}
|
|
|
|
UOrientableLabel::~UOrientableLabel()
|
|
{
|
|
}
|
|
|
|
QSize UOrientableLabel::sizeHint() const
|
|
{
|
|
QSize size = QLabel::sizeHint();
|
|
if (_orientation == Qt::Vertical)
|
|
size.transpose();
|
|
return size;
|
|
|
|
}
|
|
|
|
QSize UOrientableLabel::minimumSizeHint() const
|
|
{
|
|
QSize size = QLabel::minimumSizeHint();
|
|
if (_orientation == Qt::Vertical)
|
|
size.transpose();
|
|
return size;
|
|
}
|
|
|
|
void UOrientableLabel::setOrientation(Qt::Orientation orientation)
|
|
{
|
|
_orientation = orientation;
|
|
switch(orientation)
|
|
{
|
|
case Qt::Horizontal:
|
|
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
break;
|
|
|
|
case Qt::Vertical:
|
|
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void UOrientableLabel::paintEvent(QPaintEvent* event)
|
|
{
|
|
QPainter p(this);
|
|
QRect r = rect();
|
|
switch (_orientation)
|
|
{
|
|
case Qt::Horizontal:
|
|
break;
|
|
case Qt::Vertical:
|
|
p.rotate(-90);
|
|
p.translate(-height(), 0);
|
|
QSize size = r.size();
|
|
size.transpose();
|
|
r.setSize(size);
|
|
break;
|
|
}
|
|
p.drawText(r, this->alignment() | (this->wordWrap()?Qt::TextWordWrap:0), this->text());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UPlot::UPlot(QWidget *parent) :
|
|
QWidget(parent),
|
|
_maxVisibleItems(-1),
|
|
_autoScreenCaptureFormat("png"),
|
|
_bgColor(Qt::white)
|
|
{
|
|
this->setupUi();
|
|
this->createActions();
|
|
this->createMenus();
|
|
|
|
// This will update actions
|
|
this->showLegend(true);
|
|
this->setGraphicsView(false);
|
|
this->setMaxVisibleItems(0);
|
|
this->showGrid(false);
|
|
this->showRefreshRate(false);
|
|
this->keepAllData(false);
|
|
|
|
for(int i=0; i<4; ++i)
|
|
{
|
|
_axisMaximums[i] = 0;
|
|
_axisMaximumsSet[i] = false;
|
|
if(i<2)
|
|
{
|
|
_fixedAxis[i] = false;
|
|
}
|
|
}
|
|
|
|
_mouseCurrentPos = _mousePressedPos; // for zooming
|
|
|
|
_refreshIntervalTime.start();
|
|
_lowestRefreshRate = 99;
|
|
_refreshStartTime.start();
|
|
|
|
_penStyleCount = rand() % 10 + 1; // rand 1->10
|
|
_workingDirectory = QDir::homePath();
|
|
}
|
|
|
|
UPlot::~UPlot()
|
|
{
|
|
_aAutoScreenCapture->setChecked(false);
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("%s", this->title().toStdString().c_str());
|
|
#endif
|
|
this->removeCurves();
|
|
}
|
|
|
|
void UPlot::setupUi()
|
|
{
|
|
_legend = new UPlotLegend(this);
|
|
_view = new QGraphicsView(this);
|
|
_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
_view->setScene(new QGraphicsScene(0,0,0,0,this));
|
|
_view->setStyleSheet( "QGraphicsView { border-style: none; }" );
|
|
_sceneRoot = _view->scene()->addText("");
|
|
_sceneRoot->setTransform(QTransform::fromTranslate(0, 0), true);
|
|
_graphicsViewHolder = new QWidget(this);
|
|
_graphicsViewHolder->setMinimumSize(100,100);
|
|
_graphicsViewHolder->setMouseTracking(true);
|
|
_verticalAxis = new UPlotAxis(Qt::Vertical, 0, 1, this);
|
|
_horizontalAxis = new UPlotAxis(Qt::Horizontal, 0, 1, this);
|
|
_title = new QLabel("");
|
|
_xLabel = new QLabel("");
|
|
_refreshRate = new QLabel("");
|
|
_yLabel = new UOrientableLabel("");
|
|
_yLabel->setOrientation(Qt::Vertical);
|
|
_title->setAlignment(Qt::AlignCenter);
|
|
_xLabel->setAlignment(Qt::AlignCenter);
|
|
_yLabel->setAlignment(Qt::AlignCenter);
|
|
_refreshRate->setAlignment(Qt::AlignCenter);
|
|
_title->setWordWrap(true);
|
|
_xLabel->setWordWrap(true);
|
|
_yLabel->setWordWrap(true);
|
|
_title->setVisible(false);
|
|
_xLabel->setVisible(false);
|
|
_yLabel->setVisible(false);
|
|
_refreshRate->setVisible(false);
|
|
|
|
//layouts
|
|
QVBoxLayout * vLayout = new QVBoxLayout(_graphicsViewHolder);
|
|
vLayout->setContentsMargins(0,0,0,0);
|
|
vLayout->addWidget(_view);
|
|
|
|
QGridLayout * grid = new QGridLayout(this);
|
|
grid->setContentsMargins(0,0,0,0);
|
|
grid->addWidget(_title, 0, 2);
|
|
grid->addWidget(_yLabel, 1, 0);
|
|
grid->addWidget(_verticalAxis, 1, 1);
|
|
grid->addWidget(_refreshRate, 2, 1);
|
|
grid->addWidget(_graphicsViewHolder, 1, 2);
|
|
grid->setColumnStretch(2, 1);
|
|
grid->setRowStretch(1, 1);
|
|
grid->addWidget(_horizontalAxis, 2, 2);
|
|
grid->addWidget(_xLabel, 3, 2);
|
|
grid->addWidget(_legend, 1, 3);
|
|
|
|
connect(_legend, SIGNAL(legendItemToggled(const UPlotCurve *, bool)), this, SLOT(showCurve(const UPlotCurve *, bool)));
|
|
connect(_legend, SIGNAL(legendItemRemoved(const UPlotCurve *)), this, SLOT(removeCurve(const UPlotCurve *)));
|
|
connect(_legend, SIGNAL(legendItemMoved(const UPlotCurve *, int)), this, SLOT(moveCurve(const UPlotCurve *, int)));
|
|
}
|
|
|
|
void UPlot::createActions()
|
|
{
|
|
_aShowLegend = new QAction(tr("Show legend"), this);
|
|
_aShowLegend->setCheckable(true);
|
|
_aShowGrid = new QAction(tr("Show grid"), this);
|
|
_aShowGrid->setCheckable(true);
|
|
_aShowRefreshRate = new QAction(tr("Show refresh rate"), this);
|
|
_aShowRefreshRate->setCheckable(true);
|
|
_aMouseTracking = new QAction(tr("Mouse tracking"), this);
|
|
_aMouseTracking->setCheckable(true);
|
|
_aGraphicsView = new QAction(tr("Graphics view"), this);
|
|
_aGraphicsView->setCheckable(true);
|
|
_aKeepAllData = new QAction(tr("Keep all data"), this);
|
|
_aKeepAllData->setCheckable(true);
|
|
_aLimit0 = new QAction(tr("No maximum items shown"), this);
|
|
_aLimit10 = new QAction(tr("10"), this);
|
|
_aLimit50 = new QAction(tr("50"), this);
|
|
_aLimit100 = new QAction(tr("100"), this);
|
|
_aLimit500 = new QAction(tr("500"), this);
|
|
_aLimit1000 = new QAction(tr("1000"), this);
|
|
_aLimitCustom = new QAction(tr(""), this);
|
|
_aLimit0->setCheckable(true);
|
|
_aLimit10->setCheckable(true);
|
|
_aLimit50->setCheckable(true);
|
|
_aLimit100->setCheckable(true);
|
|
_aLimit500->setCheckable(true);
|
|
_aLimit1000->setCheckable(true);
|
|
_aLimitCustom->setCheckable(true);
|
|
_aLimitCustom->setVisible(false);
|
|
_aAddVerticalLine = new QAction(tr("Vertical line..."), this);
|
|
_aAddHorizontalLine = new QAction(tr("Horizontal line..."), this);
|
|
_aChangeTitle = new QAction(tr("Change title"), this);
|
|
_aChangeXLabel = new QAction(tr("Change X label..."), this);
|
|
_aChangeYLabel = new QAction(tr("Change Y label..."), this);
|
|
_aChangeBackgroundColor = new QAction(tr("Change bg color..."), this);
|
|
_aYLabelVertical = new QAction(tr("Vertical orientation"), this);
|
|
_aYLabelVertical->setCheckable(true);
|
|
_aYLabelVertical->setChecked(true);
|
|
_aSaveFigure = new QAction(tr("Save figure..."), this);
|
|
_aAutoScreenCapture = new QAction(tr("Auto screen capture..."), this);
|
|
_aAutoScreenCapture->setCheckable(true);
|
|
_aClearData = new QAction(tr("Clear data"), this);
|
|
|
|
QActionGroup * grpLimit = new QActionGroup(this);
|
|
grpLimit->addAction(_aLimit0);
|
|
grpLimit->addAction(_aLimit10);
|
|
grpLimit->addAction(_aLimit50);
|
|
grpLimit->addAction(_aLimit100);
|
|
grpLimit->addAction(_aLimit500);
|
|
grpLimit->addAction(_aLimit1000);
|
|
grpLimit->addAction(_aLimitCustom);
|
|
_aLimit0->setChecked(true);
|
|
}
|
|
|
|
void UPlot::createMenus()
|
|
{
|
|
_menu = new QMenu(tr("Plot"), this);
|
|
_menu->addAction(_aShowLegend);
|
|
_menu->addAction(_aShowGrid);
|
|
_menu->addAction(_aShowRefreshRate);
|
|
_menu->addAction(_aMouseTracking);
|
|
_menu->addAction(_aGraphicsView);
|
|
_menu->addAction(_aKeepAllData);
|
|
_menu->addSeparator()->setStatusTip(tr("Maximum items shown"));
|
|
_menu->addAction(_aLimit0);
|
|
_menu->addAction(_aLimit10);
|
|
_menu->addAction(_aLimit50);
|
|
_menu->addAction(_aLimit100);
|
|
_menu->addAction(_aLimit500);
|
|
_menu->addAction(_aLimit1000);
|
|
_menu->addAction(_aLimitCustom);
|
|
_menu->addSeparator();
|
|
QMenu * addLineMenu = _menu->addMenu(tr("Add line"));
|
|
addLineMenu->addAction(_aAddHorizontalLine);
|
|
addLineMenu->addAction(_aAddVerticalLine);
|
|
_menu->addSeparator();
|
|
_menu->addAction(_aChangeTitle);
|
|
_menu->addAction(_aChangeXLabel);
|
|
QMenu * yLabelMenu = _menu->addMenu(tr("Y label"));
|
|
yLabelMenu->addAction(_aChangeYLabel);
|
|
yLabelMenu->addAction(_aYLabelVertical);
|
|
_menu->addAction(_aChangeBackgroundColor);
|
|
_menu->addAction(_aSaveFigure);
|
|
_menu->addAction(_aAutoScreenCapture);
|
|
_menu->addSeparator();
|
|
_menu->addAction(_aClearData);
|
|
|
|
}
|
|
|
|
UPlotCurve * UPlot::addCurve(const QString & curveName, const QColor & color)
|
|
{
|
|
// add curve
|
|
UPlotCurve * curve = new UPlotCurve(curveName, this);
|
|
if(color.isValid())
|
|
{
|
|
curve->setPen(color);
|
|
}
|
|
else
|
|
{
|
|
curve->setPen(this->getRandomPenColored());
|
|
}
|
|
this->addCurve(curve);
|
|
return curve;
|
|
}
|
|
|
|
bool UPlot::addCurve(UPlotCurve * curve, bool ownershipTransferred)
|
|
{
|
|
if(curve)
|
|
{
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("Adding curve \"%s\" to plot \"%s\"...", curve->name().toStdString().c_str(), this->title().toStdString().c_str());
|
|
#endif
|
|
// only last curve can trigger an update, so disable previous connections
|
|
if(!qobject_cast<UPlotCurveThreshold*>(curve))
|
|
{
|
|
for(int i=_curves.size()-1; i>=0; --i)
|
|
{
|
|
if(!qobject_cast<UPlotCurveThreshold*>(_curves.at(i)))
|
|
{
|
|
disconnect(_curves.at(i), SIGNAL(dataChanged(const UPlotCurve *)), this, SLOT(updateAxis()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// add curve
|
|
_curves.append(curve);
|
|
curve->attach(this);
|
|
curve->setItemsColor(QColor(255-_bgColor.red(), 255-_bgColor.green(), 255-_bgColor.red(), _bgColor.alpha()));
|
|
if(ownershipTransferred)
|
|
{
|
|
curve->setParent(this);
|
|
}
|
|
this->updateAxis(curve);
|
|
curve->setXStart(_axisMaximums[1]);
|
|
|
|
connect(curve, SIGNAL(dataChanged(const UPlotCurve *)), this, SLOT(updateAxis()));
|
|
|
|
_legend->addItem(curve);
|
|
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("Curve \"%s\" added to plot \"%s\"", curve->name().toStdString().c_str(), this->title().toStdString().c_str());
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("The curve is null!");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QStringList UPlot::curveNames()
|
|
{
|
|
QStringList names;
|
|
for(QList<UPlotCurve*>::iterator iter = _curves.begin(); iter!=_curves.end(); ++iter)
|
|
{
|
|
if(*iter)
|
|
{
|
|
names.append((*iter)->name());
|
|
}
|
|
}
|
|
return names;
|
|
}
|
|
|
|
bool UPlot::contains(const QString & curveName)
|
|
{
|
|
for(QList<UPlotCurve*>::iterator iter = _curves.begin(); iter!=_curves.end(); ++iter)
|
|
{
|
|
if(*iter && (*iter)->name().compare(curveName) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QPen UPlot::getRandomPenColored()
|
|
{
|
|
return QPen((Qt::GlobalColor)(_penStyleCount++ % 12 + 7 ));
|
|
}
|
|
|
|
void UPlot::replot(QPainter * painter)
|
|
{
|
|
if(_maxVisibleItems>0)
|
|
{
|
|
UPlotCurve * c = 0;
|
|
int maxItem = 0;
|
|
// find the curve with the most items
|
|
for(QList<UPlotCurve *>::iterator i=_curves.begin(); i!=_curves.end(); ++i)
|
|
{
|
|
if((*i)->isVisible() && ((UPlotCurve *)(*i))->itemsSize() > maxItem)
|
|
{
|
|
c = *i;
|
|
maxItem = c->itemsSize();
|
|
}
|
|
}
|
|
if(c && (maxItem-1)/2+1 > _maxVisibleItems && _axisMaximums[0] < c->getItemData((c->itemsSize()-1) -_maxVisibleItems*2).x())
|
|
{
|
|
_axisMaximums[0] = c->getItemData((c->itemsSize()-1) -_maxVisibleItems*2).x();
|
|
}
|
|
}
|
|
|
|
float axis[4] = {0};
|
|
for(int i=0; i<4; ++i)
|
|
{
|
|
axis[i] = _axisMaximums[i];
|
|
}
|
|
|
|
_verticalAxis->setAxis(axis[2], axis[3]);
|
|
_horizontalAxis->setAxis(axis[0], axis[1]);
|
|
if(_aGraphicsView->isChecked() && !painter)
|
|
{
|
|
_verticalAxis->update();
|
|
_horizontalAxis->update();
|
|
}
|
|
|
|
//ULOGGER_DEBUG("x1=%f, x2=%f, y1=%f, y2=%f", _axisMaximums[0], _axisMaximums[1], _axisMaximums[2], _axisMaximums[3]);
|
|
|
|
QRectF newRect(0,0, _graphicsViewHolder->size().width(), _graphicsViewHolder->size().height());
|
|
_view->scene()->setSceneRect(newRect);
|
|
float borderHor = (float)_horizontalAxis->border();
|
|
float borderVer = (float)_verticalAxis->border();
|
|
|
|
//grid
|
|
qDeleteAll(hGridLines);
|
|
hGridLines.clear();
|
|
qDeleteAll(vGridLines);
|
|
vGridLines.clear();
|
|
if(_aShowGrid->isChecked())
|
|
{
|
|
// TODO make a PlotGrid class ?
|
|
float w = newRect.width()-(borderHor*2);
|
|
float h = newRect.height()-(borderVer*2);
|
|
float stepH = w / float(_horizontalAxis->count());
|
|
float stepV = h / float(_verticalAxis->count());
|
|
QPen dashPen(Qt::DashLine);
|
|
dashPen.setColor(QColor(255-_bgColor.red(), 255-_bgColor.green(), 255-_bgColor.blue(), 100));
|
|
QPen pen(dashPen.color());
|
|
for(float i=0.0f; i*stepV <= h+stepV; i+=5.0f)
|
|
{
|
|
//horizontal lines
|
|
if(!_aGraphicsView->isChecked())
|
|
{
|
|
if(painter)
|
|
{
|
|
painter->save();
|
|
painter->setPen(pen);
|
|
painter->drawLine(0, stepV*i+borderVer+0.5f, borderHor, stepV*i+borderVer+0.5f);
|
|
|
|
painter->setPen(dashPen);
|
|
painter->drawLine(borderHor, stepV*i+borderVer+0.5f, w+borderHor, stepV*i+borderVer+0.5f);
|
|
|
|
painter->setPen(pen);
|
|
painter->drawLine(w+borderHor, stepV*i+borderVer+0.5f, w+borderHor*2, stepV*i+borderVer+0.5f);
|
|
painter->restore();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hGridLines.append(new QGraphicsLineItem(0, stepV*i+borderVer, borderHor, stepV*i+borderVer, _sceneRoot));
|
|
hGridLines.last()->setPen(pen);
|
|
hGridLines.append(new QGraphicsLineItem(borderHor, stepV*i+borderVer, w+borderHor, stepV*i+borderVer, _sceneRoot));
|
|
hGridLines.last()->setPen(dashPen);
|
|
hGridLines.append(new QGraphicsLineItem(w+borderHor, stepV*i+borderVer, w+borderHor*2, stepV*i+borderVer, _sceneRoot));
|
|
hGridLines.last()->setPen(pen);
|
|
}
|
|
}
|
|
for(float i=0; i*stepH < w+stepH; i+=5.0f)
|
|
{
|
|
//vertical lines
|
|
if(!_aGraphicsView->isChecked())
|
|
{
|
|
if(painter)
|
|
{
|
|
painter->save();
|
|
painter->setPen(pen);
|
|
painter->drawLine(stepH*i+borderHor+0.5f, 0, stepH*i+borderHor+0.5f, borderVer);
|
|
|
|
painter->setPen(dashPen);
|
|
painter->drawLine(stepH*i+borderHor+0.5f, borderVer, stepH*i+borderHor+0.5f, h+borderVer);
|
|
|
|
painter->setPen(pen);
|
|
painter->drawLine(stepH*i+borderHor+0.5f, h+borderVer, stepH*i+borderHor+0.5f, h+borderVer*2);
|
|
painter->restore();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vGridLines.append(new QGraphicsLineItem(stepH*i+borderHor, 0, stepH*i+borderHor, borderVer, _sceneRoot));
|
|
vGridLines.last()->setPen(pen);
|
|
vGridLines.append(new QGraphicsLineItem(stepH*i+borderHor, borderVer, stepH*i+borderHor, h+borderVer, _sceneRoot));
|
|
vGridLines.last()->setPen(dashPen);
|
|
vGridLines.append(new QGraphicsLineItem(stepH*i+borderHor, h+borderVer, stepH*i+borderHor, h+borderVer*2, _sceneRoot));
|
|
vGridLines.last()->setPen(pen);
|
|
}
|
|
}
|
|
}
|
|
|
|
// curves
|
|
float scaleX = 1;
|
|
float scaleY = 1;
|
|
float den = 0;
|
|
den = axis[1] - axis[0];
|
|
if(den != 0)
|
|
{
|
|
scaleX = (newRect.width()-(borderHor*2)) / den;
|
|
}
|
|
den = axis[3] - axis[2];
|
|
if(den != 0)
|
|
{
|
|
scaleY = (newRect.height()-(borderVer*2)) / den;
|
|
}
|
|
for(QList<UPlotCurve *>::iterator i=_curves.begin(); i!=_curves.end(); ++i)
|
|
{
|
|
if((*i)->isVisible())
|
|
{
|
|
float xDir = 1.0f;
|
|
float yDir = -1.0f;
|
|
(*i)->update(scaleX,
|
|
scaleY,
|
|
xDir<0?axis[1]+borderHor/scaleX:-(axis[0]-borderHor/scaleX),
|
|
yDir<0?axis[3]+borderVer/scaleY:-(axis[2]-borderVer/scaleY),
|
|
xDir,
|
|
yDir,
|
|
_aKeepAllData->isChecked()?0:_maxVisibleItems);
|
|
if(painter)
|
|
{
|
|
(*i)->draw(painter, QRect(0,0,_graphicsViewHolder->rect().width(), _graphicsViewHolder->rect().height()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update refresh rate
|
|
if(_aShowRefreshRate->isChecked())
|
|
{
|
|
int refreshRate = qRound(1000.0f/float(_refreshIntervalTime.restart()));
|
|
if(refreshRate > 0 && refreshRate < _lowestRefreshRate)
|
|
{
|
|
_lowestRefreshRate = refreshRate;
|
|
}
|
|
// Refresh the label only after each 1000 ms
|
|
if(_refreshStartTime.elapsed() > 1000)
|
|
{
|
|
_refreshRate->setText(QString::number(_lowestRefreshRate));
|
|
_lowestRefreshRate = 99;
|
|
_refreshStartTime.start();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlot::setFixedXAxis(float x1, float x2)
|
|
{
|
|
_fixedAxis[0] = true;
|
|
_axisMaximums[0] = x1;
|
|
_axisMaximums[1] = x2;
|
|
}
|
|
|
|
void UPlot::setFixedYAxis(float y1, float y2)
|
|
{
|
|
_fixedAxis[1] = true;
|
|
_axisMaximums[2] = y1;
|
|
_axisMaximums[3] = y2;
|
|
}
|
|
|
|
void UPlot::updateAxis(const UPlotCurve * curve)
|
|
{
|
|
if(curve && curve->isVisible() && curve->itemsSize() && curve->isMinMaxValid())
|
|
{
|
|
const QVector<float> & minMax = curve->getMinMax();
|
|
//ULOGGER_DEBUG("x1=%f, x2=%f, y1=%f, y2=%f", minMax[0], minMax[1], minMax[2], minMax[3]);
|
|
if(minMax.size() != 4)
|
|
{
|
|
ULOGGER_ERROR("minMax size != 4 ?!?");
|
|
return;
|
|
}
|
|
this->updateAxis(minMax[0], minMax[1], minMax[2], minMax[3]);
|
|
_aGraphicsView->isChecked()?this->replot(0):this->update();
|
|
}
|
|
}
|
|
|
|
bool UPlot::updateAxis(float x1, float x2, float y1, float y2)
|
|
{
|
|
bool modified = false;
|
|
modified = updateAxis(x1,y1);
|
|
if(!modified)
|
|
{
|
|
modified = updateAxis(x2,y2);
|
|
}
|
|
else
|
|
{
|
|
updateAxis(x2,y2);
|
|
}
|
|
return modified;
|
|
}
|
|
|
|
bool UPlot::updateAxis(float x, float y)
|
|
{
|
|
//ULOGGER_DEBUG("x=%f, y=%f", x,y);
|
|
bool modified = false;
|
|
if(!_fixedAxis[0] && (!_axisMaximumsSet[0] || x < _axisMaximums[0]))
|
|
{
|
|
_axisMaximums[0] = x;
|
|
_axisMaximumsSet[0] = true;
|
|
modified = true;
|
|
}
|
|
|
|
if(!_fixedAxis[0] && (!_axisMaximumsSet[1] || x > _axisMaximums[1]))
|
|
{
|
|
_axisMaximums[1] = x;
|
|
_axisMaximumsSet[1] = true;
|
|
modified = true;
|
|
}
|
|
|
|
if(!_fixedAxis[1] && (!_axisMaximumsSet[2] || y < _axisMaximums[2]))
|
|
{
|
|
_axisMaximums[2] = y;
|
|
_axisMaximumsSet[2] = true;
|
|
modified = true;
|
|
}
|
|
|
|
if(!_fixedAxis[1] && (!_axisMaximumsSet[3] || y > _axisMaximums[3]))
|
|
{
|
|
_axisMaximums[3] = y;
|
|
_axisMaximumsSet[3] = true;
|
|
modified = true;
|
|
}
|
|
|
|
return modified;
|
|
}
|
|
|
|
void UPlot::updateAxis()
|
|
{
|
|
//Reset the axis
|
|
for(int i=0; i<4; ++i)
|
|
{
|
|
if((!_fixedAxis[0] && i<2) || (!_fixedAxis[1] && i>=2))
|
|
{
|
|
_axisMaximums[i] = 0;
|
|
_axisMaximumsSet[i] = false;
|
|
}
|
|
}
|
|
|
|
for(int i=0; i<_curves.size(); ++i)
|
|
{
|
|
if(_curves.at(i)->isVisible() && _curves.at(i)->isMinMaxValid())
|
|
{
|
|
const QVector<float> & minMax = _curves.at(i)->getMinMax();
|
|
this->updateAxis(minMax[0], minMax[1], minMax[2], minMax[3]);
|
|
}
|
|
}
|
|
|
|
_aGraphicsView->isChecked()?this->replot(0):this->update();
|
|
|
|
this->captureScreen();
|
|
}
|
|
|
|
void UPlot::paintEvent(QPaintEvent * event)
|
|
{
|
|
#if PRINT_DEBUG
|
|
UDEBUG("");
|
|
#endif
|
|
if(!_aGraphicsView->isChecked())
|
|
{
|
|
QPainter painter(this);
|
|
painter.translate(_graphicsViewHolder->pos());
|
|
painter.save();
|
|
painter.setBrush(_bgColor);
|
|
painter.setPen(QPen(Qt::NoPen));
|
|
painter.drawRect(_graphicsViewHolder->rect());
|
|
painter.restore();
|
|
|
|
this->replot(&painter);
|
|
|
|
if(_mouseCurrentPos != _mousePressedPos)
|
|
{
|
|
painter.save();
|
|
int left, top, right, bottom;
|
|
left = _mousePressedPos.x() < _mouseCurrentPos.x() ? _mousePressedPos.x()-_graphicsViewHolder->x():_mouseCurrentPos.x()-_graphicsViewHolder->x();
|
|
top = _mousePressedPos.y() < _mouseCurrentPos.y() ? _mousePressedPos.y()-1-_graphicsViewHolder->y():_mouseCurrentPos.y()-1-_graphicsViewHolder->y();
|
|
right = _mousePressedPos.x() > _mouseCurrentPos.x() ? _mousePressedPos.x()-_graphicsViewHolder->x():_mouseCurrentPos.x()-_graphicsViewHolder->x();
|
|
bottom = _mousePressedPos.y() > _mouseCurrentPos.y() ? _mousePressedPos.y()-_graphicsViewHolder->y():_mouseCurrentPos.y()-_graphicsViewHolder->y();
|
|
if(left <= 0)
|
|
{
|
|
left = 1;
|
|
}
|
|
if(right >= _graphicsViewHolder->width())
|
|
{
|
|
right = _graphicsViewHolder->width()-1;
|
|
}
|
|
if(top <= 0)
|
|
{
|
|
top = 1;
|
|
}
|
|
if(bottom >= _graphicsViewHolder->height())
|
|
{
|
|
bottom = _graphicsViewHolder->height()-1;
|
|
}
|
|
painter.setPen(Qt::NoPen);
|
|
painter.setBrush(QBrush(QColor(255-_bgColor.red(),255-_bgColor.green(),255-_bgColor.blue(),100)));
|
|
painter.drawRect(0, 0, _graphicsViewHolder->width(), top);
|
|
painter.drawRect(0, top, left, bottom-top);
|
|
painter.drawRect(right, top, _graphicsViewHolder->width()-right, bottom-top);
|
|
painter.drawRect(0, bottom, _graphicsViewHolder->width(), _graphicsViewHolder->height()-bottom);
|
|
painter.restore();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QWidget::paintEvent(event);
|
|
}
|
|
}
|
|
|
|
void UPlot::resizeEvent(QResizeEvent * event)
|
|
{
|
|
if(_aGraphicsView->isChecked())
|
|
{
|
|
this->replot(0);
|
|
}
|
|
QWidget::resizeEvent(event);
|
|
}
|
|
|
|
void UPlot::mousePressEvent(QMouseEvent * event)
|
|
{
|
|
_mousePressedPos = event->pos();
|
|
_mouseCurrentPos = _mousePressedPos;
|
|
QWidget::mousePressEvent(event);
|
|
}
|
|
|
|
void UPlot::mouseMoveEvent(QMouseEvent * event)
|
|
{
|
|
if(!_aGraphicsView->isChecked())
|
|
{
|
|
if(!(QApplication::mouseButtons() & Qt::LeftButton))
|
|
{
|
|
_mousePressedPos = _mouseCurrentPos;
|
|
}
|
|
|
|
float x,y;
|
|
if(mousePosToValue(event->pos(), x ,y))
|
|
{
|
|
if(QApplication::mouseButtons() & Qt::LeftButton)
|
|
{
|
|
_mouseCurrentPos = event->pos();
|
|
this->update();
|
|
}
|
|
|
|
int xPos = event->pos().x() - _graphicsViewHolder->pos().x();
|
|
int yPos = event->pos().y() - _graphicsViewHolder->pos().y();
|
|
if((QApplication::mouseButtons() & Qt::LeftButton) ||
|
|
(_aMouseTracking->isChecked() && xPos>=0 && yPos>=0 && xPos<_graphicsViewHolder->width() && yPos<_graphicsViewHolder->height()))
|
|
{
|
|
QToolTip::showText(event->globalPos(), QString("%1,%2").arg(x).arg(y));
|
|
}
|
|
else
|
|
{
|
|
QToolTip::hideText();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QToolTip::hideText();
|
|
}
|
|
}
|
|
QWidget::mouseMoveEvent(event);
|
|
}
|
|
|
|
void UPlot::mouseReleaseEvent(QMouseEvent * event)
|
|
{
|
|
if(_mousePressedPos != _mouseCurrentPos)
|
|
{
|
|
int left,top,bottom,right;
|
|
|
|
left = _mousePressedPos.x() < _mouseCurrentPos.x() ? _mousePressedPos.x():_mouseCurrentPos.x();
|
|
top = _mousePressedPos.y() < _mouseCurrentPos.y() ? _mousePressedPos.y():_mouseCurrentPos.y();
|
|
right = _mousePressedPos.x() > _mouseCurrentPos.x() ? _mousePressedPos.x():_mouseCurrentPos.x();
|
|
bottom = _mousePressedPos.y() > _mouseCurrentPos.y() ? _mousePressedPos.y():_mouseCurrentPos.y();
|
|
|
|
if(right - left > 5 || bottom - top > 5)
|
|
{
|
|
float axis[4];
|
|
if(mousePosToValue(QPoint(left, top), axis[0], axis[3]) && mousePosToValue(QPoint(right, bottom), axis[1], axis[2]))
|
|
{
|
|
#if PRINT_DEBUG
|
|
UDEBUG("resize! new axis = [%f, %f, %f, %f]", axis[0], axis[1], axis[2], axis[3]);
|
|
#endif
|
|
//update axis (only if not fixed)
|
|
for(int i=0; i<4; ++i)
|
|
{
|
|
if((!_fixedAxis[0] && i<2) || (!_fixedAxis[1] && i>=2))
|
|
{
|
|
_axisMaximums[i] = axis[i];
|
|
}
|
|
}
|
|
_aGraphicsView->isChecked()?this->replot(0):this->update();
|
|
}
|
|
}
|
|
_mousePressedPos = _mouseCurrentPos;
|
|
}
|
|
QWidget::mouseReleaseEvent(event);
|
|
}
|
|
|
|
void UPlot::mouseDoubleClickEvent(QMouseEvent * event)
|
|
{
|
|
this->updateAxis();
|
|
QWidget::mouseDoubleClickEvent(event);
|
|
}
|
|
|
|
bool UPlot::mousePosToValue(const QPoint & pos, float & x, float & y)
|
|
{
|
|
int xPos = pos.x() - _graphicsViewHolder->pos().x() - _horizontalAxis->border();
|
|
int yPos = pos.y() - _graphicsViewHolder->pos().y() - _verticalAxis->border();
|
|
int maxX = _graphicsViewHolder->width() - _horizontalAxis->border()*2;
|
|
int maxY = _graphicsViewHolder->height() - _verticalAxis->border()*2;
|
|
if(maxX == 0 || maxY == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(xPos < 0)
|
|
{
|
|
xPos = 0;
|
|
}
|
|
else if(xPos > maxX)
|
|
{
|
|
xPos = maxX;
|
|
}
|
|
|
|
if(yPos < 0)
|
|
{
|
|
yPos = 0;
|
|
}
|
|
else if(yPos > maxY)
|
|
{
|
|
yPos = maxY;
|
|
}
|
|
|
|
//UDEBUG("IN");
|
|
//UDEBUG("x1=%f, x2=%f, y1=%f, y2=%f", _axisMaximums[0], _axisMaximums[1], _axisMaximums[2], _axisMaximums[3]);
|
|
//UDEBUG("border hor=%f ver=%f", (float)_horizontalAxis->border(), (float)_verticalAxis->border());
|
|
//UDEBUG("rect = %d,%d %d,%d", _graphicsViewHolder->pos().x(), _graphicsViewHolder->pos().y(), _graphicsViewHolder->width(), _graphicsViewHolder->height());
|
|
//UDEBUG("%d,%d", event->pos().x(), event->pos().y());
|
|
//UDEBUG("x/y %d,%d", x, y);
|
|
//UDEBUG("max %d,%d", maxX, maxY);
|
|
|
|
//UDEBUG("map %f,%f", x, y);
|
|
x = _axisMaximums[0] + float(xPos)*(_axisMaximums[1] - _axisMaximums[0]) / float(maxX);
|
|
y = _axisMaximums[2] + float(maxY - yPos)*(_axisMaximums[3] - _axisMaximums[2]) / float(maxY);
|
|
return true;
|
|
}
|
|
|
|
void UPlot::contextMenuEvent(QContextMenuEvent * event)
|
|
{
|
|
QAction * action = _menu->exec(event->globalPos());
|
|
|
|
if(!action)
|
|
{
|
|
return;
|
|
}
|
|
else if(action == _aShowLegend)
|
|
{
|
|
this->showLegend(_aShowLegend->isChecked());
|
|
}
|
|
else if(action == _aShowGrid)
|
|
{
|
|
this->showGrid(_aShowGrid->isChecked());
|
|
}
|
|
else if(action == _aShowRefreshRate)
|
|
{
|
|
this->showRefreshRate(_aShowRefreshRate->isChecked());
|
|
}
|
|
else if(action == _aMouseTracking)
|
|
{
|
|
this->trackMouse(_aMouseTracking->isChecked());
|
|
}
|
|
else if(action == _aGraphicsView)
|
|
{
|
|
this->setGraphicsView(_aGraphicsView->isChecked());
|
|
}
|
|
else if(action == _aKeepAllData)
|
|
{
|
|
this->keepAllData(_aKeepAllData->isChecked());
|
|
}
|
|
else if(action == _aLimit0 ||
|
|
action == _aLimit10 ||
|
|
action == _aLimit50 ||
|
|
action == _aLimit100 ||
|
|
action == _aLimit500 ||
|
|
action == _aLimit1000 ||
|
|
action == _aLimitCustom)
|
|
{
|
|
this->setMaxVisibleItems(action->text().toInt());
|
|
}
|
|
else if(action == _aAddVerticalLine || action == _aAddHorizontalLine)
|
|
{
|
|
bool ok;
|
|
QString text = QInputDialog::getText(this, action->text(), tr("New line name :"), QLineEdit::Normal, "", &ok);
|
|
while(ok && text.isEmpty())
|
|
{
|
|
QMessageBox::warning(this, action->text(), tr("The name is not valid or it is already used in this plot."));
|
|
text = QInputDialog::getText(this, action->text(), tr("New line name :"), QLineEdit::Normal, "", &ok);
|
|
}
|
|
if(ok)
|
|
{
|
|
double min = _axisMaximums[2];
|
|
double max = _axisMaximums[3];
|
|
QString axis = "Y";
|
|
if(action == _aAddVerticalLine)
|
|
{
|
|
min = _axisMaximums[0];
|
|
max = _axisMaximums[1];
|
|
axis = "X";
|
|
}
|
|
double value = QInputDialog::getDouble(this,
|
|
action->text(),
|
|
tr("%1 value (min=%2, max=%3):").arg(axis).arg(min).arg(max),
|
|
(min+max)/2,
|
|
-2147483647,
|
|
2147483647,
|
|
4,
|
|
&ok);
|
|
if(ok)
|
|
{
|
|
if(action == _aAddHorizontalLine)
|
|
{
|
|
this->addThreshold(text, value, Qt::Horizontal);
|
|
}
|
|
else
|
|
{
|
|
this->addThreshold(text, value, Qt::Vertical);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(action == _aChangeTitle)
|
|
{
|
|
bool ok;
|
|
QString text = _title->text();
|
|
if(text.isEmpty())
|
|
{
|
|
text = this->objectName();
|
|
}
|
|
text = QInputDialog::getText(this, _aChangeTitle->text(), tr("Title :"), QLineEdit::Normal, text, &ok);
|
|
if(ok)
|
|
{
|
|
this->setTitle(text);
|
|
}
|
|
}
|
|
else if(action == _aChangeXLabel)
|
|
{
|
|
bool ok;
|
|
QString text = QInputDialog::getText(this, _aChangeXLabel->text(), tr("X axis label :"), QLineEdit::Normal, _xLabel->text(), &ok);
|
|
if(ok)
|
|
{
|
|
this->setXLabel(text);
|
|
}
|
|
}
|
|
else if(action == _aChangeYLabel)
|
|
{
|
|
bool ok;
|
|
QString text = QInputDialog::getText(this, _aChangeYLabel->text(), tr("Y axis label :"), QLineEdit::Normal, _yLabel->text(), &ok);
|
|
if(ok)
|
|
{
|
|
this->setYLabel(text, _yLabel->orientation());
|
|
}
|
|
}
|
|
else if(action == _aYLabelVertical)
|
|
{
|
|
this->setYLabel(_yLabel->text(), _aYLabelVertical->isChecked()?Qt::Vertical:Qt::Horizontal);
|
|
}
|
|
else if(action == _aChangeBackgroundColor)
|
|
{
|
|
QColor color = QColorDialog::getColor(_bgColor, this);
|
|
if(color.isValid())
|
|
{
|
|
this->setBackgroundColor(color);
|
|
}
|
|
}
|
|
else if(action == _aSaveFigure)
|
|
{
|
|
|
|
QString text;
|
|
#ifdef QT_SVG_LIB
|
|
text = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), (QDir::homePath() + "/") + this->title() + ".png", "*.png *.xpm *.jpg *.pdf *.svg");
|
|
#else
|
|
text = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), (QDir::homePath() + "/") + this->title() + ".png", "*.png *.xpm *.jpg *.pdf");
|
|
#endif
|
|
if(!text.isEmpty())
|
|
{
|
|
bool flatModified = false;
|
|
if(!_legend->isFlat())
|
|
{
|
|
_legend->setFlat(true);
|
|
flatModified = true;
|
|
}
|
|
|
|
QPalette p(palette());
|
|
// Set background color to white
|
|
QColor c = p.color(QPalette::Background);
|
|
p.setColor(QPalette::Background, Qt::white);
|
|
setPalette(p);
|
|
|
|
#ifdef QT_SVG_LIB
|
|
if(QFileInfo(text).suffix().compare("svg") == 0)
|
|
{
|
|
QSvgGenerator generator;
|
|
generator.setFileName(text);
|
|
generator.setSize(this->size());
|
|
QPainter painter;
|
|
painter.begin(&generator);
|
|
this->render(&painter);
|
|
painter.end();
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
if(QFileInfo(text).suffix().compare("pdf") == 0)
|
|
{
|
|
QPrinter printer;
|
|
printer.setOutputFormat(QPrinter::PdfFormat);
|
|
printer.setOutputFileName(text);
|
|
this->render(&printer);
|
|
}
|
|
else
|
|
{
|
|
QPixmap figure = QPixmap::grabWidget(this);
|
|
figure.save(text);
|
|
}
|
|
#ifdef QT_SVG_LIB
|
|
}
|
|
#endif
|
|
// revert background color
|
|
p.setColor(QPalette::Background, c);
|
|
setPalette(p);
|
|
|
|
if(flatModified)
|
|
{
|
|
_legend->setFlat(false);
|
|
}
|
|
}
|
|
}
|
|
else if(action == _aAutoScreenCapture)
|
|
{
|
|
if(_aAutoScreenCapture->isChecked())
|
|
{
|
|
this->selectScreenCaptureFormat();
|
|
}
|
|
}
|
|
else if(action == _aClearData)
|
|
{
|
|
this->clearData();
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_WARN("Unknown action");
|
|
}
|
|
}
|
|
|
|
void UPlot::setWorkingDirectory(const QString & workingDirectory)
|
|
{
|
|
if(QDir(_workingDirectory).exists())
|
|
{
|
|
_workingDirectory = workingDirectory;
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("The directory \"%s\" doesn't exist", workingDirectory.toStdString().c_str());
|
|
}
|
|
}
|
|
|
|
void UPlot::captureScreen()
|
|
{
|
|
if(!_aAutoScreenCapture->isChecked())
|
|
{
|
|
return;
|
|
}
|
|
QString targetDir = _workingDirectory + "/ScreensCaptured";
|
|
QDir dir;
|
|
if(!dir.exists(targetDir))
|
|
{
|
|
dir.mkdir(targetDir);
|
|
}
|
|
targetDir += "/";
|
|
targetDir += this->title().replace(" ", "_");
|
|
if(!dir.exists(targetDir))
|
|
{
|
|
dir.mkdir(targetDir);
|
|
}
|
|
targetDir += "/";
|
|
QString name = (QDateTime::currentDateTime().toString("yyMMddhhmmsszzz") + ".") + _autoScreenCaptureFormat;
|
|
QPixmap figure = QPixmap::grabWidget(this);
|
|
figure.save(targetDir + name);
|
|
}
|
|
|
|
void UPlot::selectScreenCaptureFormat()
|
|
{
|
|
QStringList items;
|
|
items << QString("png") << QString("jpg");
|
|
bool ok;
|
|
QString item = QInputDialog::getItem(this, tr("Select format"), tr("Format:"), items, 0, false, &ok);
|
|
if(ok && !item.isEmpty())
|
|
{
|
|
_autoScreenCaptureFormat = item;
|
|
}
|
|
this->captureScreen();
|
|
}
|
|
|
|
void UPlot::clearData()
|
|
{
|
|
for(int i=0; i<_curves.size(); ++i)
|
|
{
|
|
// Don't clear threshold curves
|
|
if(qobject_cast<UPlotCurveThreshold*>(_curves.at(i)) == 0)
|
|
{
|
|
_curves.at(i)->clear();
|
|
}
|
|
}
|
|
_aGraphicsView->isChecked()?this->replot(0):this->update();
|
|
}
|
|
|
|
// for convenience...
|
|
UPlotCurveThreshold * UPlot::addThreshold(const QString & name, float value, Qt::Orientation orientation)
|
|
{
|
|
UPlotCurveThreshold * curve = new UPlotCurveThreshold(name, value, orientation, this);
|
|
QPen pen = curve->pen();
|
|
pen.setStyle((Qt::PenStyle)(_penStyleCount++ % 4 + 2));
|
|
curve->setPen(pen);
|
|
if(!this->addCurve(curve))
|
|
{
|
|
if(curve)
|
|
{
|
|
delete curve;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_aGraphicsView->isChecked()?this->replot(0):this->update();
|
|
}
|
|
return curve;
|
|
}
|
|
|
|
void UPlot::setTitle(const QString & text)
|
|
{
|
|
_title->setText(text);
|
|
_title->setVisible(!text.isEmpty());
|
|
this->update();
|
|
if(_aGraphicsView->isChecked())
|
|
{
|
|
QTimer::singleShot(10, this, SLOT(updateAxis()));
|
|
}
|
|
}
|
|
|
|
void UPlot::setXLabel(const QString & text)
|
|
{
|
|
_xLabel->setText(text);
|
|
_xLabel->setVisible(!text.isEmpty());
|
|
this->update();
|
|
if(_aGraphicsView->isChecked())
|
|
{
|
|
QTimer::singleShot(10, this, SLOT(updateAxis()));
|
|
}
|
|
}
|
|
|
|
void UPlot::setYLabel(const QString & text, Qt::Orientation orientation)
|
|
{
|
|
_yLabel->setText(text);
|
|
_yLabel->setOrientation(orientation);
|
|
_yLabel->setVisible(!text.isEmpty());
|
|
_aYLabelVertical->setChecked(orientation==Qt::Vertical);
|
|
this->update();
|
|
if(_aGraphicsView->isChecked())
|
|
{
|
|
QTimer::singleShot(10, this, SLOT(updateAxis()));
|
|
}
|
|
}
|
|
|
|
void UPlot::setBackgroundColor(const QColor & color)
|
|
{
|
|
if(color.isValid())
|
|
{
|
|
_bgColor = color;
|
|
_view->scene()->setBackgroundBrush(QBrush(_bgColor));
|
|
for(QList<UPlotCurve*>::iterator iter=_curves.begin(); iter!=_curves.end(); ++iter)
|
|
{
|
|
(*iter)->setItemsColor(QColor(255-_bgColor.red(), 255-_bgColor.green(), 255-_bgColor.blue(), _bgColor.alpha()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlot::addItem(QGraphicsItem * item)
|
|
{
|
|
item->setParentItem(_sceneRoot);
|
|
item->setZValue(1.0f);
|
|
}
|
|
|
|
void UPlot::showLegend(bool shown)
|
|
{
|
|
_legend->setVisible(shown);
|
|
_aShowLegend->setChecked(shown);
|
|
this->update();
|
|
if(_aGraphicsView->isChecked())
|
|
{
|
|
QTimer::singleShot(10, this, SLOT(updateAxis()));
|
|
}
|
|
}
|
|
|
|
void UPlot::showGrid(bool shown)
|
|
{
|
|
_aShowGrid->setChecked(shown);
|
|
_aGraphicsView->isChecked()?this->replot(0):this->update();
|
|
}
|
|
|
|
void UPlot::showRefreshRate(bool shown)
|
|
{
|
|
_aShowRefreshRate->setChecked(shown);
|
|
_refreshRate->setVisible(shown);
|
|
this->update();
|
|
if(_aGraphicsView->isChecked())
|
|
{
|
|
QTimer::singleShot(10, this, SLOT(updateAxis()));
|
|
}
|
|
}
|
|
|
|
void UPlot::trackMouse(bool tracking)
|
|
{
|
|
_aMouseTracking->setChecked(tracking);
|
|
this->setMouseTracking(tracking);
|
|
}
|
|
|
|
void UPlot::setGraphicsView(bool on)
|
|
{
|
|
_aGraphicsView->setChecked(on);
|
|
_view->setVisible(on);
|
|
_aGraphicsView->isChecked()?this->replot(0):this->update();
|
|
_aMouseTracking->setEnabled(!on);
|
|
}
|
|
|
|
void UPlot::keepAllData(bool kept)
|
|
{
|
|
_aKeepAllData->setChecked(kept);
|
|
}
|
|
|
|
void UPlot::setMaxVisibleItems(int maxVisibleItems)
|
|
{
|
|
if(maxVisibleItems <= 0)
|
|
{
|
|
_aLimit0->setChecked(true);
|
|
}
|
|
else if(maxVisibleItems == 10)
|
|
{
|
|
_aLimit10->setChecked(true);
|
|
}
|
|
else if(maxVisibleItems == 50)
|
|
{
|
|
_aLimit50->setChecked(true);
|
|
}
|
|
else if(maxVisibleItems == 100)
|
|
{
|
|
_aLimit100->setChecked(true);
|
|
}
|
|
else if(maxVisibleItems == 500)
|
|
{
|
|
_aLimit500->setChecked(true);
|
|
}
|
|
else if(maxVisibleItems == 1000)
|
|
{
|
|
_aLimit1000->setChecked(true);
|
|
}
|
|
else
|
|
{
|
|
_aLimitCustom->setVisible(true);
|
|
_aLimitCustom->setChecked(true);
|
|
_aLimitCustom->setText(QString::number(maxVisibleItems));
|
|
}
|
|
_maxVisibleItems = maxVisibleItems;
|
|
updateAxis();
|
|
}
|
|
|
|
QRectF UPlot::sceneRect() const
|
|
{
|
|
return _view->sceneRect();
|
|
}
|
|
|
|
void UPlot::removeCurves()
|
|
{
|
|
QList<UPlotCurve*> tmp = _curves;
|
|
for(QList<UPlotCurve*>::iterator iter=tmp.begin(); iter!=tmp.end(); ++iter)
|
|
{
|
|
this->removeCurve(*iter);
|
|
}
|
|
_curves.clear();
|
|
}
|
|
|
|
void UPlot::removeCurve(const UPlotCurve * curve)
|
|
{
|
|
QList<UPlotCurve *>::iterator iter = qFind(_curves.begin(), _curves.end(), curve);
|
|
#if PRINT_DEBUG
|
|
ULOGGER_DEBUG("Plot=\"%s\" removing curve=\"%s\"", this->objectName().toStdString().c_str(), curve?curve->name().toStdString().c_str():"");
|
|
#endif
|
|
if(iter!=_curves.end())
|
|
{
|
|
UPlotCurve * c = *iter;
|
|
c->detach(this);
|
|
_curves.erase(iter);
|
|
_legend->remove(c);
|
|
if(!qobject_cast<UPlotCurveThreshold*>(c))
|
|
{
|
|
// transfer update connection to next curve
|
|
for(int i=_curves.size()-1; i>=0; --i)
|
|
{
|
|
if(!qobject_cast<UPlotCurveThreshold*>(_curves.at(i)))
|
|
{
|
|
connect(_curves.at(i), SIGNAL(dataChanged(const UPlotCurve *)), this, SLOT(updateAxis()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(c->parent() == this)
|
|
{
|
|
delete c;
|
|
}
|
|
// Update axis
|
|
updateAxis();
|
|
}
|
|
}
|
|
|
|
void UPlot::showCurve(const UPlotCurve * curve, bool shown)
|
|
{
|
|
QList<UPlotCurve *>::iterator iter = qFind(_curves.begin(), _curves.end(), curve);
|
|
if(iter!=_curves.end())
|
|
{
|
|
UPlotCurve * value = *iter;
|
|
if(value->isVisible() != shown)
|
|
{
|
|
value->setVisible(shown);
|
|
this->updateAxis();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UPlot::moveCurve(const UPlotCurve * curve, int index)
|
|
{
|
|
// this will change the print order
|
|
int currentIndex = -1;
|
|
UPlotCurve * c = 0;
|
|
for(int i=0; i<_curves.size(); ++i)
|
|
{
|
|
if(_curves.at(i) == curve)
|
|
{
|
|
c = _curves.at(i);
|
|
currentIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(c && currentIndex != index)
|
|
{
|
|
_curves.removeAt(currentIndex);
|
|
QList<QGraphicsItem *> children = _sceneRoot->childItems();
|
|
_curves.insert(index, c);
|
|
if(currentIndex > index)
|
|
{
|
|
children[currentIndex]->stackBefore(children[index]);
|
|
}
|
|
else
|
|
{
|
|
if(currentIndex<children.size()-2)
|
|
{
|
|
if(index < children.size()-1)
|
|
{
|
|
children[index]->stackBefore(children[currentIndex]);
|
|
}
|
|
else
|
|
{
|
|
children[currentIndex]->stackBefore(children[index]);
|
|
}
|
|
}
|
|
if(currentIndex == children.size()-2 && currentIndex < index)
|
|
{
|
|
children[index]->stackBefore(children[currentIndex]);
|
|
}
|
|
}
|
|
this->update();
|
|
}
|
|
}
|