Qt DataVisualization访问QBarDataItem时触发读取访问冲突
问题:Qt DataVisualization修改选中Bar值时触发读取访问冲突
我在使用Qt DataVisualization开发功能,核心需求是选中Bar后,通过QLineEdit修改该Bar的值——设置一个QLineEdit,在触发QLineEdit::editingFinished信号时,将输入的值设置到选中的Bar上。
但在第二个for循环中访问QBarDataItem时触发了读取访问冲突(Read Access Violation),错误出现在Qt的qarraydatapointer.h文件的以下代码行:
bool needsDetach() const noexcept { return !d || d->needsDetach(); }
代码片段
主文件
#include <QApplication> #include <QFile> #include "Window.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); // 创建窗口 Window window; QFile file(app.applicationDirPath() + "/Test.txt"); // 写入选中Bar的x和y坐标 QObject::connect(window.getSeries(), &QBar3DSeries::selectedBarChanged, [&]() { if (!file.open(QIODevice::ReadWrite)) { QTextStream stream(&file); stream << window.getSeries()->selectedBar().x() << ", " << window.getSeries()->selectedBar().y() << ", " << window.getSeries()->dataProxy()->rowAt(window.getSeries()->selectedBar().x())->data()->value() << "\n"; } }); QFile file1(app.applicationDirPath() + "/Data.txt"); if (file1.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&file1); QBarDataRow *data = new QBarDataRow; while (!in.atEnd()) { QString line = in.readLine(); *data << line.toFloat(); } window.getSeries()->dataProxy()->addRow(data); } window.show(); file.close(); file1.close(); return app.exec(); }
Window.h
#ifndef WINDOW_H #define WINDOW_H #include <QMainWindow> #include <QtDataVisualization> #define WIDTH 1400 #define HEIGHT 800 struct Data { Data() { data1 = new QBarDataRow; data2 = new QBarDataRow; data3 = new QBarDataRow; data4 = new QBarDataRow; data5 = new QBarDataRow; } ~Data() { delete data1; delete data2; delete data3; delete data4; delete data5; } public: QList<QBarDataRow *> data{ data1, data2, data3, data4, data5 }; QBarDataRow *data1; QBarDataRow *data2; QBarDataRow *data3; QBarDataRow *data4; QBarDataRow *data5; }; class Window : public QMainWindow { Q_OBJECT public: Window(QWidget *parent = nullptr); ~Window(); QBar3DSeries *getSeries(); protected: void resizeEvent(QResizeEvent *event) override; private: Q3DBars *m_bars; QBar3DSeries *m_series; Data *m_data; QWidget *m_graph; }; #endif // WINDOW_H
Window.cpp
#include "Window.h" #include <QLineEdit> static void setData(Data *data, QBar3DSeries *series) { *data->data1 << 1.0f << 3.0f << 6.5f << 5.0f << 2.2f; *data->data2 << 2.0f << 1.5f << 5.0f << 2.0f << 2.4f; *data->data3 << 3.0f << 2.5f << 4.0f << 1.0f << 3.4f; *data->data4 << 2.9f << 2.5f << 2.3f << 3.1f << 1.7f; //*data->data5 << 1.6f << 1.1f << 1.8f << 4.8f << 3.4f; series->dataProxy()->addRow(data->data1); series->dataProxy()->addRow(data->data2); series->dataProxy()->addRow(data->data3); series->dataProxy()->addRow(data->data4); //series->dataProxy()->addRow(data->data5); } Window::Window(QWidget *parent) : QMainWindow(parent) { // 初始化变量 m_bars = new Q3DBars; m_series = new QBar3DSeries; m_data = new Data; setWindowTitle("Physik"); resize(WIDTH, HEIGHT); m_bars->rowAxis()->setRange(0, 4); m_bars->columnAxis()->setRange(0, 4); m_bars->scene()->activeCamera()->setCameraPosition(30, 30); // 设置可显示的数据 setData(m_data, m_series); m_bars->addSeries(m_series); // 创建QWidget作为QMainWindow的子控件 m_graph = QWidget::createWindowContainer(m_bars, this); m_graph->setGeometry(0, 0, WIDTH * 0.7, HEIGHT); //-------------------------------------------------------------- QLineEdit *lineEdit = new QLineEdit(this); lineEdit->setGeometry(WIDTH * 0.75, HEIGHT * 0.1, WIDTH * 0.85, HEIGHT * 0.15); connect(lineEdit, &QLineEdit::editingFinished, [&]() { QPoint position = m_series->selectedBar(); for (size_t i{}; i < m_data->data.size(); ++i) { if (i == position.x()) { size_t j{}; for (auto &value : *m_data->data[i]) { if (j == position.y()) { value.setValue(lineEdit->text().toFloat()); } ++j; } break; } } }); } Window::~Window() {} QBar3DSeries *Window::getSeries() { return m_series; } void Window::resizeEvent(QResizeEvent *event) { QMainWindow::resizeEvent(event); if (m_graph) { m_graph->resize(event->size().width() * 0.7, event->size().height()); } }
问题根源
当调用series->dataProxy()->addRow(data->data1)时,Qt的数据代理会复制传入的QBarDataRow数据,而非直接持有你创建的指针。后续通过自己保存的m_data->data[i]访问时,可能因Qt内部对数据结构进行detach操作,导致原指针指向的内存失效,触发访问冲突。
修复方案
直接通过QBarDataProxy获取和修改数据,同时添加有效性校验避免越界:
修改Window.cpp中editingFinished的槽函数为:
connect(lineEdit, &QLineEdit::editingFinished, [this, lineEdit]() { QPoint position = m_series->selectedBar(); // 校验选中位置是否有效 if (position.x() < 0 || position.x() >= m_series->dataProxy()->rowCount() || position.y() < 0 || position.y() >= m_series->dataProxy()->columnCount()) { return; } // 通过数据代理获取目标行 QBarDataRow *row = m_series->dataProxy()->rowAt(position.x()); if (row) { // 修改对应Bar的值 (*row)[position.y()].setValue(lineEdit->text().toFloat()); // 通知数据代理更新视图 m_series->dataProxy()->resetArray(); } });
额外注意事项
- 主函数中
selectedBarChanged槽的文件打开逻辑错误:原代码是if (!file.open(...)),应该改为if (file.open(QIODevice::WriteOnly | QIODevice::Append)),否则仅在文件打开失败时才写入。 - 移除
Data结构体中对QBarDataRow的保存,因为Qt数据代理已接管数据,原指针不再可靠。 - 始终校验选中位置的有效性,避免数组越界访问。
内容的提问来源于stack exchange,提问作者Snakezz GP
相关产品推荐
相关产品推荐

