qxyseries.cpp
519 lines
| 14.3 KiB
| text/x-c
|
CppLexer
Michal Klocek
|
r466 | #include "qxyseries.h" | ||
QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||||
/*! | ||||
\class QXYSeries | ||||
Michal Klocek
|
r470 | \brief The QXYSeries class is a base class for line, spline and scatter series. | ||
Michal Klocek
|
r466 | */ | ||
/*! | ||||
\fn QPen QXYSeries::pen() const | ||||
Michal Klocek
|
r480 | \brief Returns pen used to draw points for series. | ||
Michal Klocek
|
r466 | \sa setPen() | ||
*/ | ||||
Michal Klocek
|
r480 | /*! | ||
\fn QBrush QXYSeries::brush() const | ||||
\brief Returns brush used to draw points for series. | ||||
\sa setBrush() | ||||
*/ | ||||
Michal Klocek
|
r574 | /*! | ||
\fn void QXYSeries::clicked(const QPointF& point) | ||||
\brief Signal is emitted when user clicks the \a point on chart. | ||||
*/ | ||||
Michal Klocek
|
r466 | /*! | ||
\fn void QXYSeries::pointReplaced(int index) | ||||
\brief \internal \a index | ||||
*/ | ||||
/*! | ||||
\fn void QXYSeries::pointAdded(int index) | ||||
\brief \internal \a index | ||||
*/ | ||||
/*! | ||||
\fn void QXYSeries::pointRemoved(int index) | ||||
\brief \internal \a index | ||||
*/ | ||||
/*! | ||||
\fn void QXYSeries::updated() | ||||
\brief \internal | ||||
*/ | ||||
/*! | ||||
Constructs empty series object which is a child of \a parent. | ||||
When series object is added to QChartView or QChart instance ownerships is transfered. | ||||
*/ | ||||
Michal Klocek
|
r470 | QXYSeries::QXYSeries(QObject* parent):QSeries(parent) | ||
Michal Klocek
|
r466 | { | ||
Marek Rosa
|
r527 | m_mapX = -1; | ||
m_mapY = -1; | ||||
Marek Rosa
|
r734 | m_mapFirst = 0; | ||
m_mapCount = 0; | ||||
m_mapLimited = false; | ||||
Marek Rosa
|
r527 | m_mapOrientation = Qt::Vertical; | ||
Marek Rosa
|
r630 | // m_mapYOrientation = Qt::Vertical; | ||
Michal Klocek
|
r466 | } | ||
/*! | ||||
Destroys the object. Series added to QChartView or QChart instances are owned by those, | ||||
and are deleted when mentioned object are destroyed. | ||||
*/ | ||||
QXYSeries::~QXYSeries() | ||||
{ | ||||
} | ||||
/*! | ||||
Adds data point \a x \a y to the series. Points are connected with lines on the chart. | ||||
*/ | ||||
void QXYSeries::add(qreal x,qreal y) | ||||
{ | ||||
Q_ASSERT(m_x.size() == m_y.size()); | ||||
m_x<<x; | ||||
m_y<<y; | ||||
emit pointAdded(m_x.size()-1); | ||||
} | ||||
/*! | ||||
This is an overloaded function. | ||||
Adds data \a point to the series. Points are connected with lines on the chart. | ||||
*/ | ||||
void QXYSeries::add(const QPointF& point) | ||||
{ | ||||
add(point.x(),point.y()); | ||||
} | ||||
Michal Klocek
|
r481 | /*! | ||
This is an overloaded function. | ||||
Adds list of data \a points to the series. Points are connected with lines on the chart. | ||||
*/ | ||||
void QXYSeries::add(const QList<QPointF> points) | ||||
{ | ||||
foreach(const QPointF& point , points) { | ||||
add(point.x(),point.y()); | ||||
} | ||||
} | ||||
Michal Klocek
|
r466 | /*! | ||
Modifies \a y value for given \a x a value. | ||||
*/ | ||||
void QXYSeries::replace(qreal x,qreal y) | ||||
{ | ||||
int index = m_x.indexOf(x); | ||||
m_x[index]=x; | ||||
m_y[index]=y; | ||||
emit pointReplaced(index); | ||||
} | ||||
/*! | ||||
This is an overloaded function. | ||||
Replaces current y value of for given \a point x value with \a point y value. | ||||
*/ | ||||
void QXYSeries::replace(const QPointF& point) | ||||
{ | ||||
Marek Rosa
|
r519 | replace(point.x(),point.y()); | ||
Michal Klocek
|
r466 | } | ||
Michal Klocek
|
r622 | /*! | ||
Removes first \a x value and related y value. | ||||
*/ | ||||
void QXYSeries::remove(qreal x) | ||||
{ | ||||
int index = m_x.indexOf(x); | ||||
if(index==-1) return; | ||||
m_x.remove(index); | ||||
m_y.remove(index); | ||||
emit pointRemoved(index); | ||||
} | ||||
Michal Klocek
|
r466 | /*! | ||
Michal Klocek
|
r541 | Removes current \a x and \a y value. | ||
Michal Klocek
|
r466 | */ | ||
Michal Klocek
|
r541 | void QXYSeries::remove(qreal x,qreal y) | ||
Michal Klocek
|
r466 | { | ||
Michal Klocek
|
r541 | int index =-1; | ||
do{ | ||||
index = m_x.indexOf(x,index+1); | ||||
}while(index !=-1 && m_y.at(index)!=y); | ||||
if(index==-1) return; | ||||
Marek Rosa
|
r545 | |||
Michal Klocek
|
r466 | m_x.remove(index); | ||
m_y.remove(index); | ||||
Marek Rosa
|
r545 | emit pointRemoved(index); | ||
Michal Klocek
|
r466 | } | ||
/*! | ||||
Removes current \a point x value. Note \a point y value is ignored. | ||||
*/ | ||||
void QXYSeries::remove(const QPointF& point) | ||||
{ | ||||
Michal Klocek
|
r541 | remove(point.x(),point.y()); | ||
Michal Klocek
|
r466 | } | ||
/*! | ||||
Michal Klocek
|
r481 | Removes all data points from the series. | ||
Michal Klocek
|
r466 | */ | ||
Michal Klocek
|
r481 | void QXYSeries::removeAll() | ||
Michal Klocek
|
r466 | { | ||
m_x.clear(); | ||||
m_y.clear(); | ||||
} | ||||
/*! | ||||
\internal \a pos | ||||
*/ | ||||
qreal QXYSeries::x(int pos) const | ||||
{ | ||||
Marek Rosa
|
r519 | if (m_model) | ||
Marek Rosa
|
r527 | if (m_mapOrientation == Qt::Vertical) | ||
// consecutive data is read from model's column | ||||
Marek Rosa
|
r734 | return m_model->data(m_model->index(pos + m_mapFirst, m_mapX), Qt::DisplayRole).toDouble(); | ||
Marek Rosa
|
r527 | else | ||
// consecutive data is read from model's row | ||||
Marek Rosa
|
r734 | return m_model->data(m_model->index(m_mapX, pos + m_mapFirst), Qt::DisplayRole).toDouble(); | ||
Marek Rosa
|
r519 | else | ||
Marek Rosa
|
r527 | // model is not specified, return the data from series' internal data store | ||
Marek Rosa
|
r519 | return m_x.at(pos); | ||
Michal Klocek
|
r466 | } | ||
/*! | ||||
\internal \a pos | ||||
*/ | ||||
qreal QXYSeries::y(int pos) const | ||||
{ | ||||
Marek Rosa
|
r519 | if (m_model) | ||
Marek Rosa
|
r527 | if (m_mapOrientation == Qt::Vertical) | ||
// consecutive data is read from model's column | ||||
Marek Rosa
|
r734 | return m_model->data(m_model->index(pos + m_mapFirst, m_mapY), Qt::DisplayRole).toDouble(); | ||
Marek Rosa
|
r527 | else | ||
// consecutive data is read from model's row | ||||
Marek Rosa
|
r734 | return m_model->data(m_model->index(m_mapY, pos + m_mapFirst), Qt::DisplayRole).toDouble(); | ||
Marek Rosa
|
r519 | else | ||
Marek Rosa
|
r527 | // model is not specified, return the data from series' internal data store | ||
Marek Rosa
|
r519 | return m_y.at(pos); | ||
Michal Klocek
|
r466 | } | ||
/*! | ||||
Returns number of data points within series. | ||||
*/ | ||||
int QXYSeries::count() const | ||||
{ | ||||
Marek Rosa
|
r519 | Q_ASSERT(m_x.size() == m_y.size()); | ||
Michal Klocek
|
r466 | |||
Jani Honkonen
|
r609 | if (m_model) { | ||
Marek Rosa
|
r527 | if (m_mapOrientation == Qt::Vertical) | ||
Marek Rosa
|
r734 | { | ||
// data is in a column. Return the number of mapped items if the model's column have enough items | ||||
// or the number of items that can be mapped | ||||
if (m_mapLimited) | ||||
return qMin(m_mapCount, qMax(m_model->rowCount() - m_mapFirst, 0)); | ||||
else | ||||
return qMax(m_model->rowCount() - m_mapFirst, 0); | ||||
} | ||||
Marek Rosa
|
r527 | else | ||
Marek Rosa
|
r734 | { | ||
// data is in a row. Return the number of mapped items if the model's row have enough items | ||||
// or the number of items that can be mapped | ||||
if (m_mapLimited) | ||||
return qMin(m_mapCount, qMax(m_model->columnCount() - m_mapFirst, 0)); | ||||
else | ||||
return qMax(m_model->columnCount() - m_mapFirst, 0); | ||||
} | ||||
Jani Honkonen
|
r609 | } | ||
// model is not specified, return the number of points in the series internal data store | ||||
return m_x.size(); | ||||
Michal Klocek
|
r466 | } | ||
Tero Ahola
|
r491 | /*! | ||
Returns the data points of the series. | ||||
*/ | ||||
QList<QPointF> QXYSeries::data() | ||||
{ | ||||
QList<QPointF> data; | ||||
for (int i(0); i < m_x.count() && i < m_y.count(); i++) | ||||
data.append(QPointF(m_x.at(i), m_y.at(i))); | ||||
return data; | ||||
} | ||||
Michal Klocek
|
r467 | /*! | ||
Michal Klocek
|
r481 | Sets \a pen used for drawing points on the chart. If the pen is not defined, the | ||
pen from chart theme is used. | ||||
\sa QChart::setChartTheme() | ||||
Michal Klocek
|
r467 | */ | ||
void QXYSeries::setPen(const QPen& pen) | ||||
{ | ||||
if(pen!=m_pen){ | ||||
Michal Klocek
|
r470 | m_pen=pen; | ||
emit updated(); | ||||
Michal Klocek
|
r467 | } | ||
} | ||||
/*! | ||||
Michal Klocek
|
r481 | Sets \a brush used for drawing points on the chart. If the brush is not defined, brush | ||
from chart theme setting is used. | ||||
\sa QChart::setChartTheme() | ||||
Michal Klocek
|
r467 | */ | ||
Michal Klocek
|
r470 | |||
void QXYSeries::setBrush(const QBrush& brush) | ||||
Michal Klocek
|
r467 | { | ||
Michal Klocek
|
r470 | if(brush!=m_brush){ | ||
m_brush=brush; | ||||
emit updated(); | ||||
Michal Klocek
|
r467 | } | ||
} | ||||
Michal Klocek
|
r466 | /*! | ||
Stream operator for adding a data \a point to the series. | ||||
\sa add() | ||||
*/ | ||||
QXYSeries& QXYSeries::operator<< (const QPointF &point) | ||||
{ | ||||
add(point); | ||||
return *this; | ||||
} | ||||
Michal Klocek
|
r481 | /*! | ||
Stream operator for adding a list of \a points to the series. | ||||
\sa add() | ||||
*/ | ||||
QXYSeries& QXYSeries::operator<< (const QList<QPointF> points) | ||||
{ | ||||
add(points); | ||||
return *this; | ||||
} | ||||
Marek Rosa
|
r519 | void QXYSeries::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight) | ||
{ | ||||
Tero Ahola
|
r611 | Q_UNUSED(bottomRight) | ||
Marek Rosa
|
r597 | if (m_mapOrientation == Qt::Vertical) | ||
Marek Rosa
|
r734 | { | ||
if (topLeft.row() >= m_mapFirst && (!m_mapLimited || topLeft.row() < m_mapFirst + m_mapCount)) | ||||
emit pointReplaced(topLeft.row() - m_mapFirst); | ||||
} | ||||
else | ||||
{ | ||||
if (topLeft.column() >= m_mapFirst && (!m_mapLimited || topLeft.column() < m_mapFirst + m_mapCount)) | ||||
emit pointReplaced(topLeft.column() - m_mapFirst); | ||||
} | ||||
} | ||||
void QXYSeries::modelDataAboutToBeAdded(QModelIndex parent, int start, int end) | ||||
{ | ||||
Q_UNUSED(parent) | ||||
// Q_UNUSED(end) | ||||
if (m_mapLimited) | ||||
{ | ||||
if (start >= m_mapFirst + m_mapCount) | ||||
// the added data is below mapped area | ||||
// therefore it has no relevance | ||||
return; | ||||
else | ||||
{ | ||||
// the added data is in the mapped area or before it and update is needed | ||||
// check how many mapped items there is currently (before new items are added) | ||||
// if the number of items currently is equal the m_mapCount then some needs to be removed from xychartitem | ||||
// internal storage before new ones can be added | ||||
Marek Rosa
|
r735 | |||
int itemsToRemove = qMin(count() - (start - m_mapFirst), end - start + 1); | ||||
Marek Rosa
|
r734 | if (m_mapCount == count()) | ||
Marek Rosa
|
r735 | for (int i = 0; i < itemsToRemove; i++) | ||
Marek Rosa
|
r734 | emit pointRemoved(count() - 1 - i); | ||
} | ||||
} | ||||
Marek Rosa
|
r597 | else | ||
Marek Rosa
|
r734 | { | ||
// map is not limited (it includes all the items starting from m_mapFirst till the end of model) | ||||
// nothing to do | ||||
// emit pointAdded(qMax(start - m_mapFirst, 0)); | ||||
} | ||||
Marek Rosa
|
r519 | } | ||
Marek Rosa
|
r545 | void QXYSeries::modelDataAdded(QModelIndex parent, int start, int end) | ||
{ | ||||
Tero Ahola
|
r611 | Q_UNUSED(parent) | ||
Marek Rosa
|
r734 | // Q_UNUSED(end) | ||
if (m_mapLimited) | ||||
{ | ||||
if (start >= m_mapFirst + m_mapCount) | ||||
// the added data is below mapped area | ||||
// therefore it has no relevance | ||||
return; | ||||
else | ||||
{ | ||||
// the added data is in the mapped area or before it | ||||
// update needed | ||||
if (count() > 0) | ||||
Marek Rosa
|
r735 | for (int i = 0; i < qMin(m_mapCount - (start - m_mapFirst), end - start + 1); i++) | ||
Marek Rosa
|
r734 | emit pointAdded(qMax(start + i - m_mapFirst, 0)); | ||
} | ||||
} | ||||
else | ||||
{ | ||||
// map is not limited (it included all the items starting from m_mapFirst till the end of model) | ||||
for (int i = 0; i < end - start + 1; i++) | ||||
emit pointAdded(qMax(start + i - m_mapFirst, 0)); | ||||
} | ||||
} | ||||
void QXYSeries::modelDataAboutToBeRemoved(QModelIndex parent, int start, int end) | ||||
{ | ||||
Q_UNUSED(parent) | ||||
// Q_UNUSED(end) | ||||
if (m_mapLimited) | ||||
{ | ||||
if (start >= m_mapFirst + m_mapCount) | ||||
// the removed data is below mapped area | ||||
// therefore it has no relevance | ||||
return; | ||||
else | ||||
{ | ||||
// the removed data is in the mapped area or before it | ||||
// update needed | ||||
Marek Rosa
|
r735 | |||
// check how many items need to be removed from the xychartitem storage | ||||
// the number equals the number of items that are removed and that lay before | ||||
// or in the mapped area. Items that lay beyond the map do not count | ||||
// the max is the current number of items in storage (count()) | ||||
int itemsToRemove = qMin(count(), qMin(end, m_mapFirst + m_mapCount - 1) - start + 1); | ||||
Marek Rosa
|
r734 | for (int i = 0; i < itemsToRemove; i++) | ||
emit pointRemoved(qMax(start - m_mapFirst, 0)); | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
// map is not limited (it included all the items starting from m_mapFirst till the end of model) | ||||
for (int i = 0; i < end - start + 1; i++) | ||||
emit pointRemoved(qMax(start - m_mapFirst, 0)); | ||||
} | ||||
Marek Rosa
|
r545 | } | ||
void QXYSeries::modelDataRemoved(QModelIndex parent, int start, int end) | ||||
{ | ||||
Tero Ahola
|
r611 | Q_UNUSED(parent) | ||
Q_UNUSED(end) | ||||
Marek Rosa
|
r734 | |||
// how many items there were before data was removed | ||||
// int oldCount = count() - 1; | ||||
if (m_mapLimited) | ||||
{ | ||||
if (start >= m_mapFirst + m_mapCount) | ||||
// the removed data is below mapped area | ||||
// therefore it has no relevance | ||||
return; | ||||
else | ||||
Marek Rosa
|
r735 | { | ||
// if the current items count in the whole model is bigger than the index of the last item | ||||
// that was removed than it means there are some extra items available | ||||
Marek Rosa
|
r734 | int extraItemsAvailable = 0; | ||
if (m_mapOrientation == Qt::Vertical) | ||||
{ | ||||
Marek Rosa
|
r735 | extraItemsAvailable = qMax(m_model->rowCount() - end, 0); | ||
Marek Rosa
|
r734 | } | ||
else | ||||
{ | ||||
Marek Rosa
|
r735 | extraItemsAvailable = qMax(m_model->columnCount() - end, 0); | ||
Marek Rosa
|
r734 | } | ||
Marek Rosa
|
r735 | // if there are excess items available (below the mapped area) use them to repopulate mapped area | ||
int removedItemsCount = qMin(count(), qMin(end, m_mapFirst + m_mapCount - 1) - qMax(start, m_mapFirst) + 1); | ||||
int toBeAdded = qMin(extraItemsAvailable, removedItemsCount); | ||||
for (int k = 0; k < toBeAdded; k++) | ||||
emit pointAdded(qMax(start - m_mapFirst, m_mapFirst) + k); | ||||
Marek Rosa
|
r734 | } | ||
} | ||||
else | ||||
{ | ||||
Marek Rosa
|
r735 | // data was removed from XYSeries interal storage. Nothing more to do | ||
Marek Rosa
|
r734 | } | ||
Marek Rosa
|
r545 | } | ||
Marek Rosa
|
r519 | bool QXYSeries::setModel(QAbstractItemModel* model) { | ||
Marek Rosa
|
r630 | |||
// disconnect signals from old model | ||||
if(m_model) | ||||
{ | ||||
disconnect(m_model, 0, this, 0); | ||||
m_mapX = -1; | ||||
m_mapY = -1; | ||||
Marek Rosa
|
r734 | m_mapFirst = 0; | ||
m_mapCount = 0; | ||||
m_mapLimited = false; | ||||
Marek Rosa
|
r630 | m_mapOrientation = Qt::Vertical; | ||
} | ||||
// set new model | ||||
if(model) | ||||
{ | ||||
m_model = model; | ||||
return true; | ||||
} | ||||
else | ||||
{ | ||||
m_model = NULL; | ||||
return false; | ||||
} | ||||
Marek Rosa
|
r519 | } | ||
Marek Rosa
|
r527 | void QXYSeries::setModelMapping(int modelX, int modelY, Qt::Orientation orientation) | ||
{ | ||||
Marek Rosa
|
r630 | if (m_model == NULL) | ||
return; | ||||
Marek Rosa
|
r527 | m_mapX = modelX; | ||
m_mapY = modelY; | ||||
Marek Rosa
|
r734 | m_mapFirst = 0; | ||
Marek Rosa
|
r527 | m_mapOrientation = orientation; | ||
Marek Rosa
|
r630 | if (m_mapOrientation == Qt::Vertical) | ||
{ | ||||
Marek Rosa
|
r734 | // m_mapCount = m_model->rowCount(); | ||
Marek Rosa
|
r630 | connect(m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(modelUpdated(QModelIndex, QModelIndex))); | ||
Marek Rosa
|
r734 | connect(m_model,SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(modelDataAboutToBeAdded(QModelIndex,int,int))); | ||
Marek Rosa
|
r630 | connect(m_model,SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(modelDataAdded(QModelIndex,int,int))); | ||
Marek Rosa
|
r734 | connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(modelDataAboutToBeRemoved(QModelIndex,int,int))); | ||
Marek Rosa
|
r630 | connect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(modelDataRemoved(QModelIndex,int,int))); | ||
} | ||||
else | ||||
{ | ||||
Marek Rosa
|
r734 | // m_mapCount = m_model->columnCount(); | ||
Marek Rosa
|
r630 | connect(m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(modelUpdated(QModelIndex, QModelIndex))); | ||
Marek Rosa
|
r734 | connect(m_model,SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(modelDataAboutToBeAdded(QModelIndex,int,int))); | ||
Marek Rosa
|
r630 | connect(m_model,SIGNAL(columnsInserted(QModelIndex, int, int)), this, SLOT(modelDataAdded(QModelIndex,int,int))); | ||
Marek Rosa
|
r734 | connect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(modelDataAboutToBeRemoved(QModelIndex,int,int))); | ||
Marek Rosa
|
r630 | connect(m_model, SIGNAL(columnsRemoved(QModelIndex, int, int)), this, SLOT(modelDataRemoved(QModelIndex,int,int))); | ||
} | ||||
Marek Rosa
|
r527 | } | ||
Marek Rosa
|
r734 | void QXYSeries::setModelMappingShift(int first, int count) | ||
{ | ||||
m_mapFirst = first; | ||||
if (count == 0) | ||||
m_mapLimited = false; | ||||
else | ||||
{ | ||||
m_mapCount = count; | ||||
m_mapLimited = true; | ||||
} | ||||
} | ||||
Marek Rosa
|
r527 | |||
Michal Klocek
|
r466 | #include "moc_qxyseries.cpp" | ||
QTCOMMERCIALCHART_END_NAMESPACE | ||||