I have a plain C++ object that runs a data acquisition routine in a separate thread and notify process with a Boost signal named acquisitionStageChangedEvent
with the following signature: boost::signal2::signal<void(const std::string &)>
. How can I start the acquisition in a new thread and update the UI with this information without having a cross-thread exception?
Asked
Active
Viewed 601 times
1

Darien Pardinas
- 5,910
- 1
- 41
- 48
-
Why not `QTthread` and QT signal? – user3528438 May 11 '15 at 03:33
-
Possible duplicate of http://stackoverflow.com/questions/5050588/how-in-boost-send-a-signal-in-a-thread-and-have-the-corresponding-slot-executed – Frank Osterfeld May 11 '15 at 05:36
2 Answers
0
Set an std::atomic<bool>
to true in your signal handler and check that flag from a QTimer
.

isanae
- 3,253
- 1
- 22
- 47
-1
Here is a working example on how to launch a background thread and update the UI during progress updates and at the end of the task:
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'
};
In the .cpp file:
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);
}
}
}

Darien Pardinas
- 5,910
- 1
- 41
- 48
-
This is a pretty dirty solution. It's much simpler to just make a data acquisition worker object and let it alone in its own thread. Mixing boost signal/slot/thread with Qt ones is just the wrong starting point. – user3528438 May 11 '15 at 03:57
-
1@user3528438, you are aware that one does not always has a choice how things are done? Ever worked with legacy code in a paid project? – Greenflow May 11 '15 at 06:57
-
@user3528438 The point is not mixing boost::signals with Qt signals/slots, rather to create a thin Qt UI wrapper around a cross-platform library that doesn't have Qt as a dependency. I know well the phrase that goes "when in Rome do as Romans do", but this is not the case. – Darien Pardinas May 11 '15 at 12:55
-
-
Yes it is! The core of the logic is inside the `DtacqAcquisition` class. The UI only provide button and updates of the operations happening in a Qt-less library. I didn't say the UI is trivial though. – Darien Pardinas May 11 '15 at 21:36