You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
    }
});

额外注意事项

  1. 主函数中selectedBarChanged槽的文件打开逻辑错误:原代码是if (!file.open(...)),应该改为if (file.open(QIODevice::WriteOnly | QIODevice::Append)),否则仅在文件打开失败时才写入。
  2. 移除Data结构体中对QBarDataRow的保存,因为Qt数据代理已接管数据,原指针不再可靠。
  3. 始终校验选中位置的有效性,避免数组越界访问。

内容的提问来源于stack exchange,提问作者Snakezz GP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:39:52