【问题标题】:How to setup QSerialPort on a separate thread?如何在单独的线程上设置 QSerialPort?
【发布时间】:2014-06-26 21:33:39
【问题描述】:

按照官方文档,我正在尝试这样做:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    QThread *thread = new QThread;    
    Worker *worker= new Worker();

    worker->moveToThread(thread);

    //init connections

    thread->start();
}

Worker 构造函数:

Worker::Worker(QObject *parent) :
    QObject(parent)
{
    serial = new QSerialPort(this);  //passing the parent, which should be the current thread      
}

没有编译错误,但是当我执行它时会抛出这个:

QObject: Cannot create children for a parent that is in a different thread. 
(Parent is QSerialPort(0x11bd1148), parent's thread is QThread(0x11bd2ef8), current thread is QThread(0x3e47b8)

也就是说,它告诉我serial 的父线程是主线程,而不是我创建的线程。

如果我不在构造函数中而是在主进程中实例化序列,结果相同,这是在我们调用thread->start() 后触发的:

Worker::Worker(QObject *parent) :
    QObject(parent)
{             
}

Worker::doWork()
{
    if(!serial)
        serial= new QSerialPort(this); 

    //...
}

我错过了什么?


以发送函数为例(一个槽):

void Worker::send(const QByteArray &data)
{
    serial->write(data);
    if( serial->waitForBytesWritten(TIMEOUT) )
        qDebug() << "sent: " << data;
}

【问题讨论】:

  • 我认为没有必要。是 QSerialPort 类 [1]:qt-project.org/doc/qt-5/qserialport.html#QSerialPort
  • 是的。在没有创建 foo 之前,不会抛出错误。该程序似乎运行良好,但该错误也不是一件好事。
  • 我用其他类型的对象测试过,似乎没有问题,所以问题完全是 QSerialPort :(
  • 删除那些,看看你是否仍然收到相同的消息。
  • @LaszloPapp 因为我正在运行一个 GUI 应用程序,并且有一个任务,我必须通过串行端口(逐行)发送文件。如果我在主线程中执行该任务,GUI 将冻结。

标签: c++ qt qthread qtcore qtserialport


【解决方案1】:

简而言之,像这样使用 QtSerialPort 模块是个坏主意。

我们基于 QIODevice 设计了这个模块,它已经为您的 GUI 应用程序提供了非阻塞机制以使用 QSerialPort 类。

您应该查看以下信号:

void QIODevice::bytesWritten(qint64 bytes) [signal]

每次将数据负载写入设备时都会发出此信号。 bytes 参数设置为写入此有效负载的字节数。 bytesWritten() 不是递归发出的;如果您重新进入事件循环或在连接到 bytesWritten() 信号的插槽内调用 waitForBytesWritten(),则不会重新发送该信号(尽管 waitForBytesWritten() 可能仍会返回 true)。

还有……

void QIODevice::readyRead() [signal]

每次有新数据可用于从设备读取时,都会发出一次此信号。只有在有新数据可用时才会再次发出它,例如当新的网络数据负载到达您的网络套接字时,或者当新的数据块已附加到您的设备时。

readyRead() 不会递归发出;如果您重新进入事件循环或在连接到 readyRead() 信号的插槽内调用 waitForReadyRead(),则不会重新发送该信号(尽管 waitForReadyRead() 仍可能返回 true)。

实现从 QIODevice 派生的类的开发人员注意:当新数据到达时,您应该始终发出 readyRead()(不要仅仅因为缓冲区中还有数据要读取而发出它)。不要在其他情况下发出 readyRead()。

我通过命令行编写了两个示例,您可以在此处找到:

Command Line Writer Async Example

Command Line Reader Sync Example

【讨论】:

  • 我需要从串口读取数据并给它一个时间戳,这需要有点准确。由于QT's message loop freezes for example when you hold the left mouse button on the title bar 不可能使用QT 的消息循环获得准确的时间戳,因此需要一个线程。除了创建一个单独的进程然后将数据和时间戳传递到主进程之外,还有什么方法可以使用 QtSerialPort 获得可靠的时间戳?
  • 另外一个问题是调用open时QSerialPort会阻塞。
  • 有没有异步调用open()的方法?当我拔下并插入 USB 串行端口时,即使它是通过排队的 SIGNAL/SLOT 方式调用的,它也会在 open() 调用上停留 3~4 秒。如果使用另一个线程,它会抱怨“无法从另一个线程启用事件通知器”。这是一个错误吗?
  • 为我关于阻止 open() 调用的评论找到了解决方案。为了防止 open() 调用冻结 GUI,应该创建 QSerialPort(!)并在非 GUI 线程中打开。这意味着不应在 Worker 类的构造函数中创建 QSerialPort。并且不要将 'this'(of worker) 传递给 QSerialPort 的构造函数。
  • 有人对此有更多信息吗?我尝试使用 QSerialPort 而不将它放在不同的线程中,它阻止了 GUI。我想了解如何正确使用它。
猜你喜欢
  • 1970-01-01
  • 2021-12-12
  • 1970-01-01
  • 2017-11-06
  • 2011-12-24
  • 1970-01-01
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多