I'm trying to update data set in QChart
on zooming (to avoid drawing lots of data points per pixel). For this I connect to QValueAxis::rangeChanged
signal, and in the handler I remove the series and replace it with a new one.
But soon after the handler returns I get a segfault.
Here's the reduced test case:
#include <vector>
#include <QtCharts>
#include <QApplication>
class ChartView : public QWidget
{
public:
ChartView(QWidget* parent=nullptr)
: QWidget(parent)
, chartView_(new QChartView)
, axisX_(new QValueAxis)
{
const auto layout=new QVBoxLayout(this);
layout->addWidget(chartView_);
axisX_->setMinorTickCount(-1);
connect(axisX_, &QValueAxis::rangeChanged, this, &ChartView::updateChart);
chartView_->chart()->addAxis(axisX_, Qt::AlignBottom);
chartView_->setRubberBand(QChartView::RectangleRubberBand);
updateChart();
}
private:
void updateChart()
{
qDebug() << "Entering updateChart()";
chartView_->chart()->removeAllSeries();
const auto series=new QLineSeries;
for (int i=0;i<10;++i)
series->append(i, 1);
chartView_->chart()->addSeries(series);
{
QSignalBlocker block(axisX_); // prevent recursive call of updateChart()
series->attachAxis(axisX_);
}
qDebug() << "Leaving updateChart()";
}
private:
QtCharts::QChartView* chartView_;
QtCharts::QValueAxis* axisX_=nullptr;
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
ChartView view;
view.resize(800,600);
view.show();
return app.exec();
}
When I launch the app and simply make a rubber band selection, it immediately crashes. I've tried compiling Qt with Address Sanitizer, and here's what it gives me (full output here):
#0 0x7f9328931211 in QtCharts::AbstractDomain::blockRangeSignals(bool) domain/abstractdomain.cpp:149
#1 0x7f932882299a in QtCharts::ChartDataSet::zoomInDomain(QRectF const&) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/chartdataset.cpp:451
#2 0x7f932885bd0d in QtCharts::QChartPrivate::zoomIn(QRectF const&) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/qchart.cpp:925
#3 0x7f93288575dd in QtCharts::QChart::zoomIn(QRectF const&) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/qchart.cpp:412
#4 0x7f9328860fe9 in QtCharts::QChartView::mouseReleaseEvent(QMouseEvent*) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/qchartview.cpp:233
<snip>
0x60b00001b238 is located 40 bytes inside of 112-byte region [0x60b00001b210,0x60b00001b280)
freed by thread T0 here:
#0 0x7f9328cad9d8 in operator delete(void*, unsigned long) (/usr/lib/gcc/x86_64-linux-gnu/7/libasan.so+0xe19d8)
#1 0x7f9328935bfc in QtCharts::XYDomain::~XYDomain() domain/xydomain.cpp:43
#2 0x7f93288678a8 in QScopedPointerDeleter<QtCharts::AbstractDomain>::cleanup(QtCharts::AbstractDomain*) (/home/ruslan/opt/qt5-sanitize/lib/libQt5Charts.so.5+0x13c8a8)
#3 0x7f9328867726 in QScopedPointer<QtCharts::AbstractDomain, QScopedPointerDeleter<QtCharts::AbstractDomain> >::reset(QtCharts::AbstractDomain*) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtbase/include/QtCore/../../src/corelib/tools/qscopedpointer.h:159
#4 0x7f9328865dc2 in QtCharts::QAbstractSeriesPrivate::setDomain(QtCharts::AbstractDomain*) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/qabstractseries.cpp:438
#5 0x7f932881d5ba in QtCharts::ChartDataSet::removeSeries(QtCharts::QAbstractSeries*) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/chartdataset.cpp:171
#6 0x7f9328856b5d in QtCharts::QChart::removeSeries(QtCharts::QAbstractSeries*) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/qchart.cpp:293
#7 0x7f9328856e19 in QtCharts::QChart::removeAllSeries() /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/qchart.cpp:304
#8 0x55f99f5904ae in ChartView::updateChart() /home/ruslan/tmp/qtcharts-crash-test/main.cpp:30
<snip>
previously allocated by thread T0 here:
#0 0x7f9328cac458 in operator new(unsigned long) (/usr/lib/gcc/x86_64-linux-gnu/7/libasan.so+0xe0458)
#1 0x7f932881c706 in QtCharts::ChartDataSet::addSeries(QtCharts::QAbstractSeries*) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/chartdataset.cpp:106
#2 0x7f9328856adf in QtCharts::QChart::addSeries(QtCharts::QAbstractSeries*) /home/ruslan/packages/qt-everywhere-src-5.12.3/qtcharts/src/charts/qchart.cpp:281
#3 0x55f99f59051a in ChartView::updateChart() /home/ruslan/tmp/qtcharts-crash-test/main.cpp:36
When I use Qt::QueuedConnection
to connect to the QValueAxis::rangeChanged
signal, the problem goes away. Apparently, at the point of emitting rangeChanged
the state of the QChart
and its related objects is not yet consistent, while after returning to the event loop it becomes consistent.
My question here is: is it really wrong to change QChart
data from the QValueAxis::rangeChanged
signal handler? Or is it simply a Qt bug, which should be reported?
I can't seem to find any mentioning of this in the docs, but I don't actually have a good idea how it could be formulated.
I'm using Qt 5.12.3 and g++ 7.4.0.