LINUX.ORG.RU

Как работать с QSerialPort в потоке ?

 , , ,


0

2

Создал класс для работы с QSerialPort, отнаследовал от QObject. Помещаю класс в созданный QThread. Но при вызове метода этого класса для открытия порта кидает такую ошибку:

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

Прога работает, но вываливается случайным образом после n успешных обращений к порту. Как это пофиксить правильно. И как в Qt определять на какой строчке кода вылетела, происходит ошибка, но отладчик ее не ловит нормально и просто закрывает прогу.

Ответ на: комментарий от RazrFalcon

Ну у меня вопрос то был, почему в моем случае прога работает и потом падает, она не падает прям сразу на первом обращении к порту. Именно это я нигде не нагуглил на этот вопрос ответ.

user2132 ()

Можно вот так попробовать:

public slots:
    void process(int param)
    {
        if (thread() != QThread::currentThread())
        {
            QMetaObject::invokeMethod(this, "process", Qt::QueuedConnection,
                                      Q_ARG(int, param));
            return;
        }

        qDebug() << "param =" << param;
    }

Если текущий поток это не тот поток, которому принадлежит объект, то вызвать этот же слот через очередь. Метод будет работать асинхронно относительно вызывающей стороны.

Но вообще надо делать так, чтобы твой код кидал сигналы, которые соединены со слотами объекта из другого потока. Соединение должно быть через очередь, тогда слот будет вызван в родном потоке и магия заработает.

ox55ff ★★★ ()
Ответ на: комментарий от ox55ff

Проблема в том, что через сигналы если, то усложняется код. Я хочу сначала вызвать слот открытия порта и если успешно, то вызвать метод посылки запроса. Но если у меня сигнал на открытие порта, то получается из того класса результат в вызывающий класс кидать ответным сигналом ?

user2132 ()
Ответ на: комментарий от user2132

Переноси логику внутрь класса. А наружу будет торчать высокоуровневый интерфейс. Зачем ты выносишь код в отдельный поток, если там только тупая работа с классом QSerialPort?

ox55ff ★★★ ()
Ответ на: комментарий от ox55ff

Мне нужно по результатам от порта обновлять gui содержимое формы проги. Я засунул логику обновления gui в тот же класс, который с портом работает. Правильно ли я понял, что надо логику с gui засунуть в отдельный класс и туда сигналами из класса порта ходить.

user2132 ()
Ответ на: комментарий от user2132

Зачем ты вынес QSerialPort в отдельный поток? Я с ним не работал, он что тормозит и надолго блокирует поток? Если нет, то я бы не стал заморачиваться со вторым потоком. Только лишняя писанина.

ox55ff ★★★ ()
Ответ на: комментарий от ox55ff

Если использовать его как блокирующий без сигнала ReadyRead, то будет тормозить как правило. Я заметил, что почти без тормозов, если читать через connect сигнала readyread.

user2132 ()
Ответ на: комментарий от user2132

Ну раз тормозит, то делай отдельный поток. Что уж поделать. С GUI работать можно строго только из главного потока. Поэтому в основном потоке держи код, который обрабатывает пользовательский ввод и даёт команды через сигналы в класс порта. Класс порта в своём потоке выполняет работу и отчитывается главному потоку через сигнал. Это в общем.

ox55ff ★★★ ()
Ответ на: комментарий от user2132

Сначала вызываешь quit, потом wait. В wait можно передать сколько нужно ждать. После этого можно делать delete на объект QThread, если он у тебя в куче, а не на стеке.

ox55ff ★★★ ()
Ответ на: комментарий от ox55ff

А вот как грохать, если создал так QThread thread; Брал стандартный пример из документации qt, они там делают через указатель, а я сделал без и не работало. И как его сделать без указателя нормальное завершение у меня так и не вышло.

user2132 ()

Перемести в свой поток. Либо создай через указатель в своем потоке, когда тот уже запущен. Смотри moveToThread(). Методы вызывай через слоты подключенные через QueuedConnection.

LinuxDebian ★★★★ ()
Последнее исправление: LinuxDebian (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Я вообще не использую поток для QSerialPort, ибо он уже и так ассинхронный.

Это да, но там есть особенность в виндах, что при использовании GUI приложения при клике мышой и ее удержании на заголовке окна (или при перетаскивании окна, или его ресайзе), евент луп останавливается и сериал порт может просрать данные (переполнение FIFO к примеру).

Поэтому, народ, в любом случае, суйте в поток через moveToThread().

ЗЫ: Такие вот дела... Просто напоминалка чтоб в будущем не было нытья. :)

kuzulis ()
Ответ на: комментарий от kuzulis

Это под любой системой плата за разделение цикла обработки событий между кучей ничего не знающих про друг друга сущностей. Запусти пару тысяч таймеров в ui треде с разрешением кванта cpu и получишь сходный эффект.

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

Не, неверно. Это только виндовая «фича» такая.

Это не только к QSP относится, даже QTimer останавливается:

#include "dialog.h"
#include "ui_dialog.h"

#include <QTimer>
#include <QDateTime>
#include <QDebug>

Dialog::Dialog(QWidget *parent) :
    QDialog(parent)
{
    const auto t = new QTimer(this);
    connect(t, &QTimer::timeout, [this]() {
        qDebug() << QDateTime::currentDateTime();
    });
    t->start(1000);
}

Если кликнуть правой клавишей мыши на заголовке окна и держать.

Так что надо всЁ через moveToThread() мувить, чтобы не обделаться. :)

PS: То-ли это винда кривая, то-ли касяк в Qt event loop.

kuzulis ()
Последнее исправление: kuzulis (всего исправлений: 3)
Ответ на: комментарий от RazrFalcon

Я вообще не использую поток для QSerialPort, ибо он уже и так ассинхронный

+1, я даже для QTcpSocket не юзаю поток, если обмен не очень мощный. А тут какой-то жалкий порт максимум мегабит

I-Love-Microsoft ★★★★★ ()