Основная идея. В многопоточном приложении вычисления проводятся в отдельной ните, по окончанию излучается сигнал, передающий результат в своих аргументах. Слот, принадлежащий уже MainWindow, будет вызван. Результаты вычислений окажутся в аргументах слота и не составит труда вывести их.
Проводя аналогию с микроконтроллерами, сигнал — это переход по вектору прерывания, а слот — сам обработчик прерывания. Чтобы возникло «прерывание», сигнал необходимо излучить: emit mysignalvoid(); тогда Qt начнёт искать ему «обработчик» (слот). В Qt можно на один сигнал повесить много слотов.
На следующем рисунке представлен алгоритм работы демонстрационного приложения. Нить обработки ежесекундно опрашивает по легенде USB HID устройство. Затем излучается (emit) сигнал, который обрабатывает слот, принадлежащий уже MainWindow и имеющий соответственно возможность рисовать на форме.
Итак, имеем классы, способные использовать подход сигналы-слоты. Для этого в их объявлении используется макрос Q_OBJECT.
class Worker : public QObject
{
Q_OBJECT //теперь можем использовать сигналы-слоты в классе
public:
QTimer *timerDeviceRead; //будем каждую секунду вызывать собственный слот GuiUpdateCallback
Worker();
public slots:
void updateElectropipData();
signals:
void GuiUpdatePlease(uint8_t const *arrptr,size_t);
};
class MainWindow : public QMainWindow //класс окна с GUI
{
Q_OBJECT
public slots:
void GuiUpdateCallback(uint8_t const *arrptr, size_t);
private:
Ui::MainWindow *ui; //доступ к кнопкам и иже с ними
QThread *thread;
Чтобы передавать результат вычислений в аргументах необходимо зарегистрировать типы, используемые для аргументов. Сделать это можно в разных местах, но обычно — в конструкторе класса, который потом будет с этими типами работать:
Worker::Worker(){
qRegisterMetaType<std::size_t>("size_t");
qRegisterMetaType<uint8_t const *>("uint8_t const *");
Далее в конструкторе нити обработки создаётся таймер. Каждую секунду будет вызываться слот обработки updateUSBDataCallback. Обратите внимания на синтаксис подключения сигнал-слот в Qt5.
this->timerDeviceRead = new QTimer();
connect(Worker::timerDeviceRead, &QTimer::timeout, this, &Worker::updateUSBDataCallback);
this->timerDeviceRead->start();
Ниже приведено тело слота нити обработки. Здесь должен находится весь долгодумающий код.
void Worker::updateUSBDataCallback(){
size_t mysize = 65;
uint8_t buf[65] = { "I like USB HID" };
emit GuiUpdatePlease(buf,mysize); //излучение сигнала
//Подмена:
for(int i =0;i<64;i++){
buf[i]=i+'0';
if (i+'0' > 250) i=0;
}
}
Для демонстрации здесь сразу после излучения сигнала к слоту MainWindow нагло модифицируется содержимое массива. А так как массив передаётся по указателю, получается грязное чтение. Для предотвращения подобной ситуации сигнал из нити обработки должен быть связан со слотом GuiUpdateCallback() определённым образом:
MainWindow::MainWindow{
connect(worker, &Worker::GuiUpdatePlease, this, &MainWindow::GuiUpdateCallback, Qt::BlockingQueuedConnection);
В таком случае, излучив сигнал, нить обработки блокируется до окончания работы слота GuiUpdateCallback().
Если в тексте программы вас смущает длинное «uint8_t const *», то можно завести синоним TU8PTR:
typedef uint8_t const * TU8PTR;
Исходный код
heleo
Я конечно могу ошибаться с последними версиями Qt, но ранее предварительной регистрации типов аргументов для передачи через слоты и сигналы не было. В документации по слотам и сигналам нет упоминаний об этом тоже. Откуда вас информация что требуется использовать qMetaType? ЕМНИП он требуется в том случае если мы говорим о регистрации нового типа данных для обработки в QVariant.
Sazonov
Регистрация нужна, когда вы передаёте данные сигналом через очередь событий (между потоками или когда связка сделана через QueuedConnection).
Я бы это вообще не упоминал, в Qt есть достаточно базовых типов, которые уже везде зарегистрированы и не несут практически никакого оверхеда. Например, QByteArray