Я пытаюсь обновить набор данных в QChart
при масштабировании (чтобы избежать рисования большого количества точек данных на пиксель). Для этого я подключаюсь к сигналу QValueAxis::rangeChanged
, а в обработчике удаляю серию и заменяю ее новой.
Но вскоре после того, как обработчик возвращается, у меня возникает ошибка.
Вот сокращенный тестовый пример:
#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();
}
Когда я запускаю приложение и просто выбираю резинку, оно сразу вылетает. Я попытался скомпилировать Qt с помощью Address Sanitizer, и вот что он мне дает (полный вывод здесь ):
#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
Когда я использую Qt::QueuedConnection
для подключения к сигналу QValueAxis::rangeChanged
, проблема исчезает. По-видимому, в момент генерации rangeChanged
состояние QChart
и связанных с ним объектов еще не согласовано, а после возврата в цикл обработки событий оно становится согласованным.
Мой вопрос здесь: действительно ли неправильно изменять данные QChart
из обработчика сигнала QValueAxis::rangeChanged
? Или это просто ошибка Qt, о которой следует сообщать?
Кажется, я не могу найти упоминания об этом в документации, но на самом деле я не знаю, как это можно сформулировать.
Я использую Qt 5.12.3 и g ++ 7.4.0.