I'm doing a DLL with no GUI (TEMPLATE = lib), using QSerialPort. I don't create threads and I don't need any: I have no GUI and having a blocking serial port operation is no problem, it is what I want.
When doing:
while (!serial_uart->isWritable());
while (!serial_uart->write(frame));
I get:
QObject::startTimer: Timers can only be used with threads started with QThread
Question: how to use QSerialPort in a library without GUI without triggering this error?
Note: I first thought the problem was coming from serial_uart->waitForReadyRead(timeout) but even without this and only serial_uart->write() I already have this problem.
Minimal reproducible DLL example:
test.cpp
#include "test.h"
extern "C" {
__declspec(dllexport) Test* new_Test() { return new Test(); }
__declspec(dllexport) void DoTest(Test *t) { t->DoTest(); }
}
Test::Test() :QObject()
{
qDebug("Hello");
}
void Test::DoTest()
{
this->serialport = new QSerialPort();
this->serialport ->setPortName("COM12");
this->serialport->setBaudRate(QSerialPort::Baud19200);
this->serialport->open(QIODevice::ReadWrite);
while (!this->serialport->isWritable());
while (!this->serialport->write("hello"));
}
test.h
#include <QSerialPort>
class Test : public QObject
{
Q_OBJECT
public:
Test();
void DoTest();
QSerialPort *serialport;
};
test.pro
TEMPLATE = lib
TARGET = test
QT += serialport
INCLUDEPATH += .
HEADERS += test.h
SOURCES += test.cpp
When I call the release/test.dll from Python I have this:
from ctypes import *
dll = CDLL(r"release\test.dll")
dll.new_Test.restype = c_void_p
dll.new_Test.argtypes = []
dll.DoTest.restype = None
dll.DoTest.argtypes = [c_void_p]
t = dll.new_Test()
dll.DoTest(t)
Hello
QObject::startTimer: Timers can only be used with threads started with QThread
Most of the QIODevice based classes (like Qt sockets or serial port) want to live in a Qt based thread and also their functions needs to be called from the same thread where the object was created.
For that reason I've usually solved this by:
Create wrapper class (QObject based with Q_OBJECT macro for signal/slot functionality) for the QIODevice based class you are about to use. For each function you are planning on using create a slot function on your wrapper class which then calls the equivalent funtion in the QIODevice:
Create a QThread class that in its run function creates an instance of MySerialPort (with new MySerialPort) and just calls exec() after that. Now MySerialPort lives in an event loop and is able to send and receive signals/slots.
The thread could also return a pointer to the instance for easier access from outside to connect signals and slots.
In your main code create signals that match the slots of the MySerialPort and connect them.
Emit the signals to access the QSerialPort.