summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Kosinski <lukasz@scythe-studio.com>2021-04-13 14:07:33 +0200
committerKeith Kyzivat <keith.kyzivat@qt.io>2021-05-07 06:07:40 -0400
commit512078eea7e96c0a2cc25db46078fc1dc66f3d8f (patch)
tree2b6b42dcbf64ea57ef54df0f36763a8f2274abf0
parent1a8f494acb672aebb5476eed7660e7711701a2e8 (diff)
QXYSeries: Support for selection in a plot
In order to support selection of points in a chart there is a support for this in scatterchartitem and linechartitem. The interface for that is available on QXYSeries level. Selected points can have changed color and they are a bit bigger than other points. Selected points are visible regardless of other points visibility. Task-number: QTBUG-89445 Change-Id: I6d11afa84288f4f949e53b005410659b0ecf3c55 Reviewed-by: Keith Kyzivat <keith.kyzivat@qt.io> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
-rw-r--r--src/charts/linechart/linechartitem.cpp27
-rw-r--r--src/charts/scatterchart/scatterchartitem.cpp22
-rw-r--r--src/charts/xychart/qxyseries.cpp233
-rw-r--r--src/charts/xychart/qxyseries.h17
-rw-r--r--src/charts/xychart/qxyseries_p.h5
-rw-r--r--src/charts/xychart/xychart_p.h2
6 files changed, 297 insertions, 9 deletions
diff --git a/src/charts/linechart/linechartitem.cpp b/src/charts/linechart/linechartitem.cpp
index ef35a4f2..f2b30df2 100644
--- a/src/charts/linechart/linechartitem.cpp
+++ b/src/charts/linechart/linechartitem.cpp
@@ -64,6 +64,8 @@ LineChartItem::LineChartItem(QLineSeries *series, QGraphicsItem *item)
QObject::connect(series, SIGNAL(pointLabelsFontChanged(QFont)), this, SLOT(handleUpdated()));
QObject::connect(series, SIGNAL(pointLabelsColorChanged(QColor)), this, SLOT(handleUpdated()));
QObject::connect(series, SIGNAL(pointLabelsClippingChanged(bool)), this, SLOT(handleUpdated()));
+ connect(series, &QLineSeries::selectedColorChanged, this, &LineChartItem::handleUpdated);
+ connect(series, &QLineSeries::selectedPointsChanged, this, &LineChartItem::handleUpdated);
handleUpdated();
}
@@ -329,11 +331,12 @@ void LineChartItem::updateGeometry()
void LineChartItem::handleUpdated()
{
- // If points visibility has changed, a geometry update is needed.
- // Also, if pen changes when points are visible, geometry update is needed.
bool doGeometryUpdate =
(m_pointsVisible != m_series->pointsVisible())
- || (m_series->pointsVisible() && (m_linePen != m_series->pen()));
+ || (m_series->pointsVisible()
+ && (m_linePen != m_series->pen()
+ || m_selectedColor != m_series->selectedColor()
+ || m_selectedPoints != m_series->selectedPoints()));
bool visibleChanged = m_series->isVisible() != isVisible();
setVisible(m_series->isVisible());
setOpacity(m_series->opacity());
@@ -343,6 +346,8 @@ void LineChartItem::handleUpdated()
m_pointLabelsVisible = m_series->pointLabelsVisible();
m_pointLabelsFont = m_series->pointLabelsFont();
m_pointLabelsColor = m_series->pointLabelsColor();
+ m_selectedColor = m_series->selectedColor();
+ m_selectedPoints = m_series->selectedPoints();
bool labelClippingChanged = m_pointLabelsClipping != m_series->pointLabelsClipping();
m_pointLabelsClipping = m_series->pointLabelsClipping();
if (doGeometryUpdate)
@@ -414,14 +419,24 @@ void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt
painter->restore();
- if (m_pointsVisible) {
+ if (m_pointsVisible || !m_selectedPoints.isEmpty()) {
// draw points that lie inside clipRect only
qreal ptSize = m_linePen.width() * 1.5;
painter->setPen(Qt::NoPen);
painter->setBrush(m_linePen.color());
for (int i = 0; i < m_linePoints.size(); ++i) {
- if (clipRect.contains(m_linePoints.at(i)))
- painter->drawEllipse(m_linePoints.at(i), ptSize, ptSize);
+ if (clipRect.contains(m_linePoints.at(i))) {
+ if (m_series->isPointSelected(i)) {
+ painter->save();
+ if (m_selectedColor.isValid())
+ painter->setBrush(m_selectedColor);
+
+ painter->drawEllipse(m_linePoints.at(i), ptSize * 1.5, ptSize * 1.5);
+ painter->restore();
+ } else if (m_pointsVisible) {
+ painter->drawEllipse(m_linePoints.at(i), ptSize, ptSize);
+ }
+ }
}
}
}
diff --git a/src/charts/scatterchart/scatterchartitem.cpp b/src/charts/scatterchart/scatterchartitem.cpp
index 515f3678..0f81ee54 100644
--- a/src/charts/scatterchart/scatterchartitem.cpp
+++ b/src/charts/scatterchart/scatterchartitem.cpp
@@ -68,6 +68,8 @@ ScatterChartItem::ScatterChartItem(QScatterSeries *series, QGraphicsItem *item)
QObject::connect(series, SIGNAL(pointLabelsFontChanged(QFont)), this, SLOT(handleUpdated()));
QObject::connect(series, SIGNAL(pointLabelsColorChanged(QColor)), this, SLOT(handleUpdated()));
QObject::connect(series, SIGNAL(pointLabelsClippingChanged(bool)), this, SLOT(handleUpdated()));
+ connect(series, &QXYSeries::selectedColorChanged, this, &ScatterChartItem::handleUpdated);
+ connect(series, &QXYSeries::selectedPointsChanged, this, &ScatterChartItem::handleUpdated);
setZValue(ChartPresenter::ScatterSeriesZValue);
setFlags(QGraphicsItem::ItemClipsChildrenToShape);
@@ -262,8 +264,18 @@ void ScatterChartItem::setPen(const QPen &pen)
void ScatterChartItem::setBrush(const QBrush &brush)
{
- foreach (QGraphicsItem *item , m_items.childItems())
- static_cast<QAbstractGraphicsShapeItem*>(item)->setBrush(brush);
+ const auto &items = m_items.childItems();
+ for (auto item : items) {
+ if (m_markerMap.contains(item)) {
+ auto index = m_series->points().indexOf(m_markerMap[item]);
+ if (m_selectedPoints.contains(index))
+ static_cast<QAbstractGraphicsShapeItem *>(item)->setBrush(m_selectedColor);
+ else
+ static_cast<QAbstractGraphicsShapeItem *>(item)->setBrush(brush);
+ } else {
+ static_cast<QAbstractGraphicsShapeItem *>(item)->setBrush(brush);
+ }
+ }
}
void ScatterChartItem::handleUpdated()
@@ -282,7 +294,9 @@ void ScatterChartItem::handleUpdated()
bool recreate = m_visible != m_series->isVisible()
|| m_size != m_series->markerSize()
- || m_shape != m_series->markerShape();
+ || m_shape != m_series->markerShape()
+ || m_selectedColor != m_series->selectedColor()
+ || m_selectedPoints != m_series->selectedPoints();
m_visible = m_series->isVisible();
m_size = m_series->markerSize();
m_shape = m_series->markerShape();
@@ -292,6 +306,8 @@ void ScatterChartItem::handleUpdated()
m_pointLabelsVisible = m_series->pointLabelsVisible();
m_pointLabelsFont = m_series->pointLabelsFont();
m_pointLabelsColor = m_series->pointLabelsColor();
+ m_selectedColor = m_series->selectedColor();
+ m_selectedPoints = m_series->selectedPoints();
bool labelClippingChanged = m_pointLabelsClipping != m_series->pointLabelsClipping();
m_pointLabelsClipping = m_series->pointLabelsClipping();
diff --git a/src/charts/xychart/qxyseries.cpp b/src/charts/xychart/qxyseries.cpp
index e991755a..ca93f07a 100644
--- a/src/charts/xychart/qxyseries.cpp
+++ b/src/charts/xychart/qxyseries.cpp
@@ -246,6 +246,22 @@ QT_BEGIN_NAMESPACE
\sa pointLabelsVisible
*/
/*!
+ \property QXYSeries::selectedColor
+ \brief The color of the selected points.
+
+ This is the fill (brush) color of points marked as selected. If not specified,
+ value of QXYSeries::color is used as default.
+ \sa color
+ \since 6.2
+*/
+/*!
+ \qmlproperty color XYSeries::selectedColor
+ The color of the selected points. This is the fill (brush) color of points marked
+ as selected.
+ If not specified, value of QXYSeries::color is used as default.
+ \sa color
+*/
+/*!
\fn void QXYSeries::pointLabelsClippingChanged(bool clipping)
This signal is emitted when the clipping of the data point labels changes to
\a clipping.
@@ -607,6 +623,159 @@ void QXYSeries::replace(const QList<QPointF> &points)
}
/*!
+ Returns true if point at given \a index is among selected points and false otherwise.
+ \note Selected points are drawn using the selected color if it was specified.
+ \sa selectedPoints(), setPointSelected(), setSelectedColor()
+ \since 6.2
+ */
+bool QXYSeries::isPointSelected(int index)
+{
+ Q_D(QXYSeries);
+ return d->isPointSelected(index);
+}
+
+/*!
+ Marks point at \a index as selected.
+ \note Emits QXYSeries::selectedPointsChanged
+ \sa setPointSelected()
+ \since 6.2
+ */
+void QXYSeries::selectPoint(int index)
+{
+ setPointSelected(index, true);
+}
+
+/*!
+ Deselects point at given \a index.
+ \note Emits QXYSeries::selectedPointsChanged
+ \sa setPointSelected()
+ \since 6.2
+ */
+void QXYSeries::deselectPoint(int index)
+{
+ setPointSelected(index, false);
+}
+
+/*!
+ Marks point at given \a index as either selected or deselected.
+ \note Selected points are drawn using the selected color if it was specified. Emits QXYSeries::selectedPointsChanged
+ \sa setPointSelected(), setSelectedColor()
+ \since 6.2
+ */
+void QXYSeries::setPointSelected(int index, bool selected)
+{
+ Q_D(QXYSeries);
+
+ bool callSignal = false;
+ d->setPointSelected(index, selected, callSignal);
+
+ if (callSignal)
+ emit selectedPointsChanged();
+}
+
+/*!
+ Marks all points in the series as selected,
+ \note Emits QXYSeries::selectedPointsChanged
+ \sa setPointSelected()
+ \since 6.2
+ */
+void QXYSeries::selectAllPoints()
+{
+ Q_D(QXYSeries);
+
+ bool callSignal = false;
+ for (int i = 0; i < d->m_points.count(); ++i)
+ d->setPointSelected(i, true, callSignal);
+
+ if (callSignal)
+ emit selectedPointsChanged();
+}
+
+/*!
+ Deselects all points in the series.
+ \note Emits QXYSeries::selectedPointsChanged
+ \sa setPointSelected()
+ \since 6.2
+ */
+void QXYSeries::deselectAllPoints()
+{
+ Q_D(QXYSeries);
+
+ bool callSignal = false;
+ for (int i = 0; i < d->m_points.count(); ++i)
+ d->setPointSelected(i, false, callSignal);
+
+ if (callSignal)
+ emit selectedPointsChanged();
+}
+
+/*!
+ Marks multiple points passed in a \a indexes list as selected.
+ \note Emits QXYSeries::selectedPointsChanged
+ \sa setPointSelected()
+ \since 6.2
+ */
+void QXYSeries::selectPoints(const QList<int> &indexes)
+{
+ Q_D(QXYSeries);
+
+ bool callSignal = false;
+ for (const int &index : indexes)
+ d->setPointSelected(index, true, callSignal);
+
+ if (callSignal)
+ emit selectedPointsChanged();
+}
+
+/*!
+ Marks multiple points passed in a \a indexes list as deselected.
+ \note Emits QXYSeries::selectedPointsChanged
+ \sa setPointSelected()
+ \since 6.2
+ */
+void QXYSeries::deselectPoints(const QList<int> &indexes)
+{
+ Q_D(QXYSeries);
+
+ bool callSignal = false;
+ for (const int &index : indexes)
+ d->setPointSelected(index, false, callSignal);
+
+ if (callSignal)
+ emit selectedPointsChanged();
+}
+
+/*!
+ Changes selection state of points at given \a indexes to the opposite one. Makes
+ \note Emits QXYSeries::selectedPointsChanged
+ \sa setPointSelected()
+ \since 6.2
+ */
+void QXYSeries::toggleSelection(const QList<int> &indexes)
+{
+ Q_D(QXYSeries);
+
+ bool callSignal = false;
+ for (const int &index : indexes)
+ d->setPointSelected(index, !isPointSelected(index), callSignal);
+
+ if (callSignal)
+ emit selectedPointsChanged();
+}
+
+/*!
+ Returns a list of points indexes marked as selected.
+ Selected points are visible regardless of points visibility.
+ \sa setPointSelected(), pointsVisible()
+ \since 6.2
+ */
+QList<int> QXYSeries::selectedPoints() const
+{
+ Q_D(const QXYSeries);
+ return QList<int>(d->m_selectedPoints.begin(), d->m_selectedPoints.end());
+}
+
+/*!
Removes the point that has the coordinates \a x and \a y from the series.
\sa pointRemoved()
*/
@@ -635,6 +804,7 @@ void QXYSeries::remove(const QPointF &point)
void QXYSeries::remove(int index)
{
Q_D(QXYSeries);
+ deselectPoint(index);
d->m_points.remove(index);
emit pointRemoved(index);
}
@@ -650,6 +820,13 @@ void QXYSeries::removePoints(int index, int count)
// remove(qreal, qreal) overload in some implicit casting cases.
Q_D(QXYSeries);
if (count > 0) {
+ if (!d->m_selectedPoints.empty()) {
+ QList<int> indexes;
+ for (int i = index; i < index + count; ++i)
+ indexes << i;
+ deselectPoints(indexes);
+ }
+
d->m_points.remove(index, count);
emit pointsRemoved(index, count);
}
@@ -665,6 +842,24 @@ void QXYSeries::insert(int index, const QPointF &point)
Q_D(QXYSeries);
if (isValidValue(point)) {
index = qMax(0, qMin(index, d->m_points.size()));
+
+ if (!d->m_selectedPoints.isEmpty()) {
+ // if point was inserted we need to move already selected points by 1
+ QSet<int> selectedAfterInsert;
+ bool callSignal = false;
+ for (const auto &value : d->m_selectedPoints) {
+ if (value >= index) {
+ selectedAfterInsert << value + 1;
+ callSignal = true;
+ } else {
+ selectedAfterInsert << value;
+ }
+ }
+ d->m_selectedPoints = selectedAfterInsert;
+ if (callSignal)
+ emit selectedPointsChanged();
+ }
+
d->m_points.insert(index, point);
emit pointAdded(index);
}
@@ -788,6 +983,21 @@ QColor QXYSeries::color() const
return pen().color();
}
+void QXYSeries::setSelectedColor(const QColor &color)
+{
+ Q_D(QXYSeries);
+ if (selectedColor() != color) {
+ d->m_selectedColor = color;
+ emit selectedColorChanged(color);
+ }
+}
+
+QColor QXYSeries::selectedColor() const
+{
+ Q_D(const QXYSeries);
+ return d->m_selectedColor;
+}
+
void QXYSeries::setPointsVisible(bool visible)
{
Q_D(QXYSeries);
@@ -1022,6 +1232,29 @@ void QXYSeriesPrivate::drawSeriesPointLabels(QPainter *painter, const QList<QPoi
}
}
+void QXYSeriesPrivate::setPointSelected(int index, bool selected, bool &callSignal)
+{
+ if (index < 0 || index > m_points.count() - 1)
+ return;
+
+ if (selected) {
+ if (!isPointSelected(index)) {
+ m_selectedPoints << index;
+ callSignal = true;
+ }
+ } else {
+ if (isPointSelected(index)) {
+ m_selectedPoints.remove(index);
+ callSignal = true;
+ }
+ }
+}
+
+bool QXYSeriesPrivate::isPointSelected(int index)
+{
+ return m_selectedPoints.contains(index);
+}
+
QT_END_NAMESPACE
#include "moc_qxyseries.cpp"
diff --git a/src/charts/xychart/qxyseries.h b/src/charts/xychart/qxyseries.h
index 43cad845..43d96e9e 100644
--- a/src/charts/xychart/qxyseries.h
+++ b/src/charts/xychart/qxyseries.h
@@ -49,6 +49,7 @@ class Q_CHARTS_EXPORT QXYSeries : public QAbstractSeries
Q_OBJECT
Q_PROPERTY(bool pointsVisible READ pointsVisible WRITE setPointsVisible)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(QColor selectedColor READ color WRITE setSelectedColor NOTIFY selectedColorChanged REVISION 1)
Q_PROPERTY(QString pointLabelsFormat READ pointLabelsFormat WRITE setPointLabelsFormat NOTIFY pointLabelsFormatChanged)
Q_PROPERTY(bool pointLabelsVisible READ pointLabelsVisible WRITE setPointLabelsVisible NOTIFY pointLabelsVisibilityChanged)
Q_PROPERTY(QFont pointLabelsFont READ pointLabelsFont WRITE setPointLabelsFont NOTIFY pointLabelsFontChanged)
@@ -94,6 +95,9 @@ public:
virtual void setColor(const QColor &color);
virtual QColor color() const;
+ void setSelectedColor(const QColor &color);
+ QColor selectedColor() const;
+
void setPointsVisible(bool visible = true);
bool pointsVisible() const;
@@ -114,6 +118,17 @@ public:
void replace(const QList<QPointF> &points);
+ bool isPointSelected(int index);
+ void selectPoint(int index);
+ void deselectPoint(int index);
+ void setPointSelected(int index, bool selected);
+ void selectAllPoints();
+ void deselectAllPoints();
+ void selectPoints(const QList<int> &indexes);
+ void deselectPoints(const QList<int> &indexes);
+ void toggleSelection(const QList<int> &indexes);
+ QList<int> selectedPoints() const;
+
Q_SIGNALS:
void clicked(const QPointF &point);
void hovered(const QPointF &point, bool state);
@@ -124,6 +139,7 @@ Q_SIGNALS:
void pointRemoved(int index);
void pointAdded(int index);
void colorChanged(QColor color);
+ void selectedColorChanged(const QColor &color);
void pointsReplaced();
void pointLabelsFormatChanged(const QString &format);
void pointLabelsVisibilityChanged(bool visible);
@@ -132,6 +148,7 @@ Q_SIGNALS:
void pointLabelsClippingChanged(bool clipping);
void pointsRemoved(int index, int count);
void penChanged(const QPen &pen);
+ void selectedPointsChanged();
private:
Q_DECLARE_PRIVATE(QXYSeries)
diff --git a/src/charts/xychart/qxyseries_p.h b/src/charts/xychart/qxyseries_p.h
index 18340201..f9518c07 100644
--- a/src/charts/xychart/qxyseries_p.h
+++ b/src/charts/xychart/qxyseries_p.h
@@ -67,12 +67,17 @@ public:
void drawSeriesPointLabels(QPainter *painter, const QList<QPointF> &points,
const int offset = 0);
+ void setPointSelected(int index, bool selected, bool &callSignal);
+ bool isPointSelected(int index);
+
Q_SIGNALS:
void updated();
protected:
QList<QPointF> m_points;
+ QSet<int> m_selectedPoints;
QPen m_pen;
+ QColor m_selectedColor;
QBrush m_brush;
bool m_pointsVisible;
QString m_pointLabelsFormat;
diff --git a/src/charts/xychart/xychart_p.h b/src/charts/xychart/xychart_p.h
index 63f1115f..66849218 100644
--- a/src/charts/xychart/xychart_p.h
+++ b/src/charts/xychart/xychart_p.h
@@ -98,6 +98,8 @@ private:
protected:
QXYSeries *m_series;
QList<QPointF> m_points;
+ QList<int> m_selectedPoints;
+ QColor m_selectedColor;
XYAnimation *m_animation;
bool m_dirty;