【问题标题】:How to update the Qt GUI from a Boost signal that is raised in another thread?如何从另一个线程中引发的 Boost 信号更新 Qt GUI?
【发布时间】:2015-05-11 00:33:16
【问题描述】:

我有一个普通的 C++ 对象,它在单独的线程中运行数据采集例程,并使用名为 acquisitionStageChangedEvent 的 Boost 信号通知进程,其签名如下:boost::signal2::signal<void(const std::string &)>。如何在新线程中开始采集并使用此信息更新 UI 而不会出现跨线程异常?

【问题讨论】:

标签: c++ qt concurrency boost-signals2


【解决方案1】:

在您的信号处理程序中将std::atomic<bool> 设置为true,并从QTimer 检查该标志。

【讨论】:

    【解决方案2】:

    这是一个关于如何在进度更新期间和任务结束时启动后台线程和更新 UI 的工作示例:

    namespace Ui
    {
        class DtacqAcquisitionWidget;
    }
    
    class DtacqAcquisitionWidget : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit AcquisitionWidget(QWidget *parent = 0);
    
       ~DtacqAcquisitionWidget();
    
    private slots:
    
        void on_pushButtonStart_clicked();
    
        void onDtacqChangeState(const QString &stage);
    
        /*... Other slots here */
    private:
        Ui::AcquisitionWidget *ui;
    
        QFutureWatcher<void>  *m_future_watcher; // This is to be able to run UI code at the end of the background thread
    
        anlytcs::Dt100Acq m_dtacq; // The plain C++ object that raises the boost signal 'acquisitionStageChangedEvent'
    };
    

    .cpp 文件中:

    DtacqAcquisitionWidget::DtacqAcquisitionWidget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::DtacqAcquisitionWidget)
    {
        ui->setupUi(this);
    
        // Run the 'onAcquisitionFinished' slot at the end of the thread
        m_future_watcher = new QFutureWatcher<void>(this);
        connect(m_future_watcher, SIGNAL(finished()), this, SLOT(onAcquisitionFinished()));
    
        // Acquisition stages update
        m_dtacq.acquisitionStageChangedEvent.connect([this](const std::string &stage)
        {
            this->onDtacqChangeState(QString::fromStdString(stage));
        });
    }
    
    void DtacqAcquisitionWidget::on_pushButtonStart_clicked()  // Starting the acquisition
    {
        ui->pushButtonStop->setEnabled(true);
        ui->pushButtonStart->setEnabled(false);
        ui->progressBar->setValue(0);
    
        // Start the acquisition in a new thread
        QFuture<void> f = QtConcurrent::run(this, &DtacqAcquisitionWidget::acquireChannelData);    
        m_future_watcher->setFuture(f);
    }
    
    void DtacqAcquisitionWidget::onDtacqChangeState(const QString &stage)
    {
        if (thread() != QThread::currentThread())
        {      
            QMetaObject::invokeMethod(this, "onDtacqChangeState", 
                Qt::BlockingQueuedConnection, Q_ARG(QString, stage));
        }
        else
        {
            ui->labelDtacqState->setText(stage);
            ui->progressBar->setValue(ui->progressBar->value() + 40);
        }    
    }
    
    void DtacqAcquisitionWidget::onAcquisitionFinished()
    {
        // Set what to update here in the GUI here when the acquisition thread finishes     
        ui->labelDtacqState->setText("DONE!");
    }
    
    void DtacqAcquisitionWidget::acquireChannelData()
    {
        // This is running on a background thread (Non UI thread)
        double time_sec = ui->spinBoxAcqTimeSec->value();
        uint32_t channel_mask = getChannelMask();
    
        std::string data_path = "C:/Users/pardinad/Desktop/DtacqData/";
        m_dtacq.startAcquisition(time_sec, channel_mask, 250);
        ui->labelDtacqState->setText("Acquisition Started!");
    
        if(m_dtacq.completeAcquisition())   
        {
            for (auto & dch : m_dtacq.dataChannels())
            {
                std::stringstream ss;
                ss << data_path << std::setw(2) << std::setfill('0') << dch.channelNumber() << ".DAT";
                std::string ch_file = ss.str();
                dch.saveToFile(ch_file);
            }
        }
    }
    

    【讨论】:

    • 这是一个非常肮脏的解决方案。只创建一个数据采集工作对象并让它单独在自己的线程中要简单得多。将 boost 信号/插槽/线程与 Qt 混合只是错误的起点。
    • @user3528438,你知道人们并不总是可以选择如何做事吗?曾经在付费项目中使用过遗留代码吗?
    • @user3528438 重点不是将 boost::signals 与 Qt 信号/插槽混合,而是围绕一个没有 Qt 作为依赖项的跨平台库创建一个精简的 Qt UI 包装器。我很清楚“当在罗马时,像罗马人那样做”这句话,但事实并非如此。
    • @DarienPardinas 那么,您的解决方案是“薄包装”吗?
    • 是的!逻辑的核心在DtacqAcquisition 类内部。 UI 仅提供按钮和更新在 Qt-less 库中发生的操作。我并没有说 UI 是微不足道的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 2020-07-13
    相关资源
    最近更新 更多