Since the existing library forces you to poll for data, the only thing to do is to run it on a timer. It's your choice as to if the object that does this job will run in the main thread, or in a worker thread. There's no need to use Qt Concurrent nor QRunnable - using a QObject makes life somewhat simpler since you can easily provide feedback to the GUI.
For example, making some assumptions about your client's API:
class Worker : public QObject {
Client m_client;
QSerialPort m_port;
QBasicTimer m_timer;
void processFrame() {
if (m_client.GetFrame().Result != Result::Success) return;
QByteArray frame = QByteArray::fromRawData(
m_client.GetFrame().Data, m_client.GetFrame().Size);
... process the frame
if (m_port.write(frame) != frame.size()) {
... process the error
}
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == m_timer.timerId()) processFrame();
}
public:
Worker(QObject * parent = 0) : QObject(parent) {}
Q_SLOT bool open(const QString & name) {
m_port.close();
m_port.setPortName(name);
if (!m_port.open(name)) return false;
if (!m_port.setBaudRate(9600)) return false;
if (!m_port.setDataBits(QSerialPort::Data8)) return false;
... other settings go here
return true;
}
Q_SLOT void start() { m_timer.start(200, this); }
Q_SLOT void stop() { m_timer.stop(); }
...
}
/// A thread that's always safe to destruct
class Thread : public QThread {
using QThread::run; // lock the default implementation
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() { quit(); wait(); }
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Worker worker;
Thread thread; // worker must be declared before thread!
if (true) {
// this code is optional, disabling it should not change things
// too much unless the client implementation blocks too much
thread.start();
worker.moveToThread(&thread);
}
QPushButton button("Start");
QObject::connect(&button, &QPushButton::clicked, [&worker]{
// Those are cross-thread calls, they can't be done directly
QMetaObject::invoke(&worker, "open", Q_ARG(QString, "COM1");
QMetaObject::invoke(&worker, "start");
});
button.show();
return app.exec(argc, argv);
}