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

如何在线程中更新QCustomPlot数据?解决跨线程事件发送错误

Fixing "cannot send events to objects owned by a different thread" with QCustomPlot in Qt

Hey there, let's break down this problem and fix it step by step. That error message is Qt's strict reminder that GUI widgets (including QCustomPlot) can only be modified from the thread they were created in—which is almost always the main (UI) thread. Your worker thread is trying to update the plot directly, which violates Qt's thread-safety rules.

Here are the most reliable solutions to get your real-time data display working:

This is the cleanest, most Qt-idiomatic way to handle cross-thread UI updates. The idea is to have your worker thread emit a signal with the new data, and connect that signal to a slot in your main window class that updates the QCustomPlot. Qt automatically queues cross-thread signal-slot connections, so the slot runs safely in the main thread.

Step 1: Define the Worker Class

In your header file (e.g., worker.h):

#include <QThread>

class Worker : public QThread
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
    void stop(); // To safely terminate the thread

signals:
    // Signal to send new data to the main thread
    void newDataReady(double x, double y);

protected:
    void run() override;

private:
    bool m_running = false;
};

Step 2: Implement the Worker Thread

In worker.cpp:

#include "worker.h"
#include <QRandomGenerator> // Replace with your real data source

Worker::Worker(QObject *parent) : QThread(parent) {}

void Worker::stop()
{
    m_running = false;
}

void Worker::run()
{
    m_running = true;
    double x = 0.0;
    while (m_running) {
        // Generate or fetch your real-time data here
        double y = QRandomGenerator::global()->generateDouble() * 100;
        
        // Emit the signal with the new data
        emit newDataReady(x, y);
        
        x += 0.1;
        msleep(100); // Simulate data acquisition delay
    }
}

Step 3: Connect Signal to Slot in Main Window

In your main window header (e.g., mainwindow.h):

#include <QMainWindow>
#include "qcustomplot.h"
#include "worker.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_startButton_clicked();
    void on_stopButton_clicked();
    // Slot to update QCustomPlot with new data
    void updatePlot(double x, double y);

private:
    Ui::MainWindow *ui;
    Worker *m_worker;
};

In mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    
    // Initialize QCustomPlot (configure axes, etc.)
    ui->customPlot->addGraph();
    ui->customPlot->xAxis->setLabel("X");
    ui->customPlot->yAxis->setLabel("Y");
    ui->customPlot->xAxis->setRange(0, 10);
    ui->customPlot->yAxis->setRange(0, 100);
    
    // Create worker instance
    m_worker = new Worker(this);
    
    // Connect worker's signal to our update slot
    connect(m_worker, &Worker::newDataReady, this, &MainWindow::updatePlot);
}

MainWindow::~MainWindow()
{
    m_worker->stop();
    m_worker->wait();
    delete ui;
}

void MainWindow::on_startButton_clicked()
{
    if (!m_worker->isRunning()) {
        m_worker->start();
    }
}

void MainWindow::on_stopButton_clicked()
{
    m_worker->stop();
}

void MainWindow::updatePlot(double x, double y)
{
    // This slot runs in the main thread, so it's safe to modify QCustomPlot
    ui->customPlot->graph(0)->addData(x, y);
    
    // Auto-scroll the x-axis if needed
    if (x > ui->customPlot->xAxis->range().upper) {
        ui->customPlot->xAxis->setRange(x, 10, Qt::AlignRight);
    }
    
    ui->customPlot->replot();
}

2. Use QMetaObject::invokeMethod (Quick Fix for Simple Cases)

If you don't want to create a separate signal, you can use QMetaObject::invokeMethod to call the plot update function directly from the worker thread, forcing it to run in the main thread.

In your worker's run() method:

void Worker::run()
{
    m_running = true;
    double x = 0.0;
    while (m_running) {
        double y = QRandomGenerator::global()->generateDouble() * 100;
        
        // Call the plot update function in the main thread
        QMetaObject::invokeMethod(ui->customPlot, [=]() {
            ui->customPlot->graph(0)->addData(x, y);
            if (x > ui->customPlot->xAxis->range().upper) {
                ui->customPlot->xAxis->setRange(x, 10, Qt::AlignRight);
            }
            ui->customPlot->replot();
        }, Qt::QueuedConnection);
        
        x += 0.1;
        msleep(100);
    }
}

⚠️ Note: Make sure your worker has safe access to the QCustomPlot pointer (but storing it as a member is not ideal—signal-slot is still better practice).

Key Rules to Remember

  • Never modify GUI widgets directly from a non-main thread: Qt's widget system is not thread-safe for direct access.
  • Don't use moveToThread on QCustomPlot: Moving a GUI widget to a worker thread will cause more errors, as widgets must live in the main thread.
  • Always clean up threads properly: Call stop() (or set a flag) and wait() before deleting the worker thread to avoid crashes.

内容的提问来源于stack exchange,提问作者M.Hu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:17:28