I have a QChartView which displays some 2D points which are representing each one a specific project I want to label each point with the project name AND NOT with it's x,y coordinates as the default behaviour
Is there any way to achieve override the function that creates or render the labels?
Why this could be difficult to achieve without changing the Qt source code
QXYSeries::setPointLabelsFormat wouldn't be of much help to you. It does indeed allow you to change the format of the labels, but the only variable part of it are the coordinates of the points.
All the drawing is done in the private part of the Qt classes. Here is the whole story:
The labels are drawn in the private part of QXYSeries (painter->drawText(position, pointLabel);
):
void QXYSeriesPrivate::drawSeriesPointLabels(QPainter *painter, const QVector<QPointF> &points,
const int offset)
{
if (points.size() == 0)
return;
static const QString xPointTag(QLatin1String("@xPoint"));
static const QString yPointTag(QLatin1String("@yPoint"));
const int labelOffset = offset + 2;
painter->setFont(m_pointLabelsFont);
painter->setPen(QPen(m_pointLabelsColor));
QFontMetrics fm(painter->font());
// m_points is used for the label here as it has the series point information
// points variable passed is used for positioning because it has the coordinates
const int pointCount = qMin(points.size(), m_points.size());
for (int i(0); i < pointCount; i++) {
QString pointLabel = m_pointLabelsFormat;
pointLabel.replace(xPointTag, presenter()->numberToString(m_points.at(i).x()));
pointLabel.replace(yPointTag, presenter()->numberToString(m_points.at(i).y()));
// Position text in relation to the point
int pointLabelWidth = fm.width(pointLabel);
QPointF position(points.at(i));
position.setX(position.x() - pointLabelWidth / 2);
position.setY(position.y() - labelOffset);
painter->drawText(position, pointLabel);
}
}
drawSeriesPointLabels is called from the paint method of ScatterChartItem (this class is not included in the official documentation):
void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
if (m_series->useOpenGL())
return;
QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
painter->save();
painter->setClipRect(clipRect);
if (m_pointLabelsVisible) {
if (m_pointLabelsClipping)
painter->setClipping(true);
else
painter->setClipping(false);
m_series->d_func()->drawSeriesPointLabels(painter, m_points,
m_series->markerSize() / 2
+ m_series->pen().width());
}
painter->restore();
}
The ScatterChartItem in turn is created in the private part of QScatterSeries and can't be substituted with a custom class:
void QScatterSeriesPrivate::initializeGraphics(QGraphicsItem* parent)
{
Q_Q(QScatterSeries);
ScatterChartItem *scatter = new ScatterChartItem(q,parent);
m_item.reset(scatter);
QAbstractSeriesPrivate::initializeGraphics(parent);
}
What you might wanna try
Hide the original labels with setPointLabelsVisible(false);
The labels will be drawn separately afterwards.
Subclass QChartView and reimplement the paintEvent
, invoking first QChartView::paintEvent
and then calling a custom function (lets say drawCustomLabels
), which is a modified version of QXYSeriesPrivate::drawSeriesPointLabels
. By calling drawCustomLabels
pass:
QXYSeries::points
,Here is an example of how the drawCustomLabels
might look like:
void MyChartView::drawCustomLabels(QPainter *painter, const QVector<QPointF> &points, const int offset)
{
if (points.count() == 0)
return;
QFontMetrics fm(painter->font());
const int labelOffset = offset + 2;
painter->setFont(m_pointLabelsFont); // Use QXYSeries::pointLabelsFont() to access m_pointLabelsFont
painter->setPen(QPen(m_pointLabelsColor)); // Use QXYSeries::pointLabelsColor() to access m_pointLabelsColor
for (int n(0); n < points.count(); n++) {
QString pointLabel = "..."; // Set the desired label for the n-th point of the series
// Position text in relation to the point
int pointLabelWidth = fm.width(pointLabel);
QPointF position(points.at(n));
position.setX(position.x() - pointLabelWidth / 2);
position.setY(position.y() - labelOffset);
painter->drawText(position, pointLabel);
}
}