如何在线程中更新 QCustomPlot 数据?

Posted

技术标签:

【中文标题】如何在线程中更新 QCustomPlot 数据?【英文标题】:How to update QCustomPlot Data in thread? 【发布时间】:2018-02-26 21:12:06 【问题描述】:

我想在 QCustomPlot 的帮助下在 c++ 中显示实时数据。所以我这样做:

在头文件中:

class QtGuiApplication : public QMainWindow

Q_OBJECT

public:
QtGuiApplication(QWidget *parent = Q_NULLPTR);


private:
Ui::QtGuiApplicationClass ui;

//plot
QCustomPlot*        plot_;
std::thread thread_Displayer_;
bool thread_run_flag_ = false;
void thread_Displayer_fn();

;

在源文件中,我使用一个按钮来启动线程......就像这样:

void     QtGuiApplication::btn_Display_clicked()


if (thread_run_flag_)

    ui.btn_Dispaly->setText("Display");
    thread_run_flag_ = false;
    if (thread_Displayer_.joinable())
    
        thread_Displayer_.join();
    

else

    thread_run_flag_ = false;
    if (thread_Displayer_.joinable())
    
        thread_Displayer_.join();
    
    ui.btn_Dispaly->setText("Stop");
    thread_run_flag_ = true;
    thread_Displayer_ = std::thread(&QtGuiApplication::thread_Displayer_fn, 
      this);





void     QtGuiApplication::thread_Displayer_fn()

double y_max_ = 0;
double y_min_ = 0;
while (thread_run_flag_)

    QVector<double> x(16384), y(16384); 
    for (int i = 0; i<16384; ++i)
    
        x[i] = i; 
        y[i] = x[i]; 
        if (y[i] > y_max_)
            y_max_ = y[i];
        if (y[i] < y_min_)
            y_min_ = y[i];
    

    plot_->yAxis->setRange(y_min_, y_max_);
    plot_->graph(0)->setData(x, y);
    plot_->replot();

   

  

但是当我启动代码时会出现这个错误:

“无法将事件发送到不同线程拥有的对象”

我该如何解决?

【问题讨论】:

使用给定的代码,我们无法测试和重现您的问题。但是,我认为您必须将来自thread_Displayer_fn() 的信号 emit 连接到主 GUI 中的一个函数,该函数接收数据并更新绘图。 这个方法有必要吗,我创建一个新类并从该类的对象发出信号? 【参考方案1】:

您需要(或者至少这对我来说是这样的)创建一个信号,该信号将在您的线程中 for 循环的每次迭代中发出。然后,将此信号连接到将执行数据实际更新的插槽

class QtGuiApplication : public QWidget

    Q_OBJECT

public:
    explicit QtGuiApplication(QWidget *parent = 0);
    ~QtGuiApplication();
private slots:
    void setPlotData(QVector<double> x,QVector<double> y);
    void pushButtonClicked();
signals:
    void newData(QVector<double> x, QVector<double> y);
private:
    Ui::QtGuiApplication *ui;
    QCustomPlot* plot_;
    std::thread thread_Displayer_;
    bool thread_run_flag_ = false;
    void thread_Displayer_fn();
;

double fRand(double fMin, double fMax)
    double f = (double)rand() / RAND_MAX;
    return fMin + f * (fMax - fMin);


QtGuiApplication::QtGuiApplication(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::QtGuiApplication)

    // Set up UI
    ui->setupUi(this);
    plot_ = ui->qcustomplot;

    // Register data type for signals
    qRegisterMetaType<QVector<double>>("QVector<double>");

    // Prepare graph
    plot_->addGraph();

    // Connect
    connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(pushButtonClicked()));
    connect(this,SIGNAL(newData(QVector<double>,QVector<double>)),this,SLOT(setPlotData(QVector<double>,QVector<double>)));


QtGuiApplication::~QtGuiApplication()

    delete ui;


void QtGuiApplication::pushButtonClicked()

    if (thread_run_flag_)
    
        ui->pushButton->setText("Display");
        thread_run_flag_ = false;
        if (thread_Displayer_.joinable())
        
            thread_Displayer_.join();
        
    
    else
    
        thread_run_flag_ = false;
        if (thread_Displayer_.joinable())
        
            thread_Displayer_.join();
        
        ui->pushButton->setText("Stop");
        thread_run_flag_ = true;
        thread_Displayer_ = std::thread(&QtGuiApplication::thread_Displayer_fn,
          this);
    


void QtGuiApplication::setPlotData(QVector<double> x, QVector<double> y)

    plot_->graph(0)->setData(x, y);
    plot_->rescaleAxes();
    plot_->replot();


void QtGuiApplication::thread_Displayer_fn()

    while (thread_run_flag_)
    
        QVector<double> x(ui->spinBox->value()), y(ui->spinBox->value());
        for (int i = 0; i<ui->spinBox->value(); ++i)
        
            x[i] = i;
            y[i] = fRand(0,10);
        
        emit newData(x,y);
        usleep(ui->doubleSpinBox->value()*1000);// convert to us
    

请注意,我还在 emmit 之后添加了 usleep 函数。不放就不能再按按钮停止,我想是因为数据发送的速率。

Here你可以找到整个例子。

【讨论】:

谢谢。在你发表评论后,我做了和你一样的事情并且它有效。【参考方案2】:

将此函数设为静态并尝试void QtGuiApplication::thread_Displayer_fn()

  static void QtGuiApplication::thread_Displayer_fn()

因为从类 QtGuiApplication 创建的对象已经由主线程拥有,并且您正试图使用​​上述语句从另一个线程(子线程)调用它的方法。

【讨论】:

如果我将此函数设为静态,我将无法访问非静态类数据成员。我在这个函数中需要非静态成员。 声明一个结构,其中包含要传递给此静态方法的所有数据类型的指针。分配所有数据类型的地址并将该结构对象作为参数发送到此静态方法并检查。

以上是关于如何在线程中更新 QCustomPlot 数据?的主要内容,如果未能解决你的问题,请参考以下文章

QCustomPlot版本迭代日志

Qt QCustomPlot 添加多个坐标系区域

Qt QCustomPlot 添加多个坐标系区域

如何在 QCustomPlot 中隐藏网格并仅显示零线?

Qt类声明中Q_OBJECT的作用与报错解决

QCustomPlot开发笔记:QCustomPlot简介下载以及基础绘图