Custom Type Sending Classes for communication between threads
-
I am new to programming custom type sending signals into Qt and need help, I tried studying some of it but I have limited time and the situation of application is quite complex, essentially I have a central class storing data and other classes that is instanced at runtime by the user interfacing with MainWindow, also created in this moment is a thread that constantly gathers data from a microcontroller elsewhere in the world (outside of the thread, the code for this miraculously works fine). however, passing this data to the class from inside the thread has not happened, I used signals emitted by the thread to pass information to the QNetworkAccessManager of the class bound to the thread, with which it then communicates with the microcontroller, but no information or evidence that the slot is even executed is presented.
MainWindow's dynamic connection of the worker's signals to the class
connect(unitWorkerAddress[unitTotal], SIGNAL(finishedSignal(int)), this, SLOT(threadFinishSlot(int)));
connect(unitWorkerAddress[unitTotal], SIGNAL(networkPostSignal(QNetworkRequest, QByteArray)),
unitAddress[unitTotal], SLOT(threadNetworkPostSlot(QNetworkRequest request, QByteArray array)) );The slot in the class stored in the heap on the main thread
void BioBloomUnit::threadNetworkPostSlot(QNetworkRequest inputRequest, QByteArray inputArray)
{
qDebug() << "13";qDebug() << inputArray.data(); qDebug() << "14"; unitNetworkManagerAddress->post(inputRequest, inputArray); qDebug() << "threadNetworkPressSlot finished";
}
On a worker bound to that class and created alongside it doing some processes and emitting signal to some of the classes slots from an alternate thread
emit networkPostSignal(request, postData);
-
@Hubbard I don't see any of the threading code in here so I can't comment on that. Here are some of my guesses...
-
I am assuming you used
moveToThread()
for your threading. If not that can be the issue since you may or may not have a properQEventLoop
in your thread. -
Are you sure
unitTotal
is a valid value and you are getting the correct addresses that you expect for bothunitAddress
andunitWorkerAddress
? -
Are none of your signals/slots working between your thread and mainwindow thread?
-
If you run the program via the console Qt will output information when a signal/slot is not connected like you'd expect. Try running where you can see the console output. It is possible you are not hooking up the signals and slots like you think you are.
Beyond that, if you could show some more code where the signals are connected in your MainWindow, how your thread is created (probably near the signal connections), and the actual thread implementation. With that information I may see something that is mapped wrong.
-
-
I know these things and there isn't a slot warning and the qDebugs don't come out
unitAddress.append(new BioBloomUnit(this)); //Instance a new unit class unitAddress[unitTotal]->setUnitNumber(unitTotal); unitAddress[unitTotal]->setMacAddress("2C:3A:E8:37:D8:6A"); ribbonAddress.append(new UnitRibbon(unitAddress[unitTotal], this)); //Instance a new unit ribbon class ribbonAddress[unitTotal]->setRibbonNumber(unitTotal); itemAddress.append(new QListWidgetItem(ui->UnitList)); //Instance a new list widget item itemAddress[unitTotal]->setSizeHint(ribbonAddress[unitTotal]->size()); //Let list widget item know about size of unit ribbon ui->UnitList->setItemWidget(itemAddress[unitTotal], ribbonAddress[unitTotal]); //Set the unit ribbon in the list widget item ui->UnitList->addItem(itemAddress[unitTotal]); //Add the item to the list connect(ribbonAddress[unitTotal]->ui->RibbonButton, SIGNAL(released()), unitAddress[unitTotal], SLOT(unitRibbonPressSlot()) ); //Connect the unit ribbon button to the unitRibbonPressSlot of the unit class connect(ribbonAddress[unitTotal]->ui->ConfigureButton, SIGNAL(released()), unitAddress[unitTotal], SLOT(unitRibbonConfigureButtonPressSlot()) ); //Connect the unit ribbon's configure button to the unitRibbonConfigureButtonPressSlot of the unit class unitAddress[unitTotal]->setPlantProfileTemplate(unitAddress[unitTotal]->configureWindowAddress->plantProfile[0]); ribbonAddress[unitTotal]->updateData(); unitWorkerAddress.append(new UnitWorker(unitAddress[unitTotal])); //Instance a new worker for the unit unitThreadAddress.append(new QThread); //Instance a new thread for the worker unitWorkerAddress[unitTotal]->moveToThread(unitThreadAddress[unitTotal]); //Move the unit's worker to the unit's thread connect(unitWorkerAddress[unitTotal], SIGNAL(error(QString)), this, SLOT(errorString(QString))); connect(unitThreadAddress[unitTotal], SIGNAL(started()), unitWorkerAddress[unitTotal], SLOT(process())); //connect(unitWorkerAddress[unitTotal], SIGNAL(finished()), unitThreadAddress[unitTotal], SLOT(quit())); //connect(unitWorkerAddress[unitTotal], SIGNAL(finished()), unitWorkerAddress[unitTotal], SLOT(deleteLater())); //connect(unitThreadAddress[unitTotal], SIGNAL(finished()), unitThreadAddress[unitTotal], SLOT(deleteLater())); qDebug() << "1"; connect(unitWorkerAddress[unitTotal], SIGNAL(finishedSignal(int)), this, SLOT(threadFinishSlot(int))); connect(unitWorkerAddress[unitTotal], SIGNAL(networkPostSignal(QNetworkRequest, QByteArray)), unitAddress[unitTotal], SLOT(threadNetworkPostSlot(QNetworkRequest request, QByteArray array)) ); unitThreadAddress[unitTotal]->start(); qDebug() << "2"; unitTotal += 1; //Increment the number of units on the system by 1
}
its a code designed to allow a user to create as many instances of biobloomunit as they need, and having biobloomunit still maintaining contact with its respective microcontroller
-
@Hubbard Hmm, I'm assuming you are using a QueuedConnection for your signals. Is your main thread blocked somewhere or is it getting back to it's event loop? If the signal is emitted in your worker thread your main thread can't process it until flow returns to the QEventLoop. This works the same for the main thread event -> worker thread.
Are both in their event loops (not blocked on some call in a slot somewhere)?
I don't see anything that jumps out as wrong with the code, but without being able to see the bigger picture it's pretty tough to diagnose. Threading is complicated. Solving problems in threading can be one of the hardest things. Not being able to see the thread functions or the code just makes it that much harder. So I'm just throwing out some guesses here.
If all else fails I can write up a quick example showing what you are trying to accomplish and maybe that can help you figure out where your code/events are wrong. I prefer we try other things first though so I don't need to spend an hour writing proof of concept code. :)
-
@ambershark Please do, just the NetworkAccessManager part, I can't create heap allocations for the threads own one yet can't 'borrow' it from its owner unit
-
Hi,
Out of curiosity, why are you sending a QNetworkRequest with a separate payload to your
threadNetworkPostSlot
? From the looks of it, it seems that you should send the details like the address of the device and the payload and let that other class manage whatever is needed to send the request you want.That way you avoid putting too much knowledge of the network interaction in you unit address widget.
-
@Hubbard Ok here you go. This doesn't use sockets as I figured that was overkill for a simple example on threaded signaling between custom components.
CMakeLists.txt -- this is the build, feel free to adapt to qmake if that if what you prefer:
cmake_minimum_required(VERSION 3.8) project(threadexample) set(CMAKE_CXX_STANDARD 11) set(CMAKE_AUTOMOC ON) find_package(Qt5 COMPONENTS Core Widgets Gui REQUIRED) set(SOURCE_FILES main.cpp MainWindow.cpp MainWindow.h Worker.cpp Worker.h) add_executable(threadexample ${SOURCE_FILES}) qt5_use_modules(${PROJECT_NAME} Core Widgets Gui)
main.cpp
#include <QApplication> #include "MainWindow.h" int main(int ac, char **av) { QApplication app(ac, av); MainWindow mw; mw.show(); return app.exec(); }
MainWindow.h
#pragma once #include <QMainWindow> class QScrollArea; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent=nullptr); private slots: void clickMeClicked(); private: QScrollArea *sa_; };
MainWindow.cpp:
#include "MainWindow.h" #include "Worker.h" #include <QVBoxLayout> #include <QScrollArea> #include <QPushButton> #include <QDebug> #include <QThread> #include <QLabel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { sa_ = new QScrollArea(); sa_->setLayout(new QVBoxLayout()); auto *cw = new QWidget(); auto *layout = new QVBoxLayout(); auto *clickMe = new QPushButton("Click Me"); connect(clickMe, &QPushButton::clicked, this, &MainWindow::clickMeClicked); layout->addWidget(sa_); layout->addWidget(clickMe); cw->setLayout(layout); setCentralWidget(cw); resize(600, 400); qDebug() << QThread::currentThreadId() << "- main thread"; } void MainWindow::clickMeClicked() { // add a layout to our scrollarea.. this emulate the BioBloom control since I have no idea what that is auto *label = new QLabel("XXX"); sa_->layout()->addWidget(label); // create a thread for our worker // NOTE: I am leaking memory here, but this is just for example purposes, make sure to clean your memory // by attaching an exit condition to the thread and handling the finished() signal with a deleteLater(). auto *thread = new QThread(); auto *worker = new Worker(); worker->moveToThread(thread); connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, SIGNAL(update(int)), label, SLOT(setNum(int))); thread->start(); }
Worker.h
#pragma once #include <QObject> class Worker : public QObject { Q_OBJECT public: Worker(QObject *parent=nullptr); public slots: void process(); void ding(); signals: void finished(); void update(int); private: unsigned int currentTick_; };
Worker.cpp
#include <QTimer> #include <QThread> #include <QDebug> #include "Worker.h" Worker::Worker(QObject *parent) : QObject(parent), currentTick_(0) { } void Worker::process() { // start a timer that kicks off a signal on our thread every 1 second // this will simulate a network event since I'm not spending the time // to add networking to this just as a test example auto *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(ding())); timer->start(1000); } void Worker::ding() { qDebug() << QThread::currentThreadId() << "- ding"; emit update(currentTick_++); }
And finally, here is a screenshot of it running so you can see all the different threads updating the main gui thread objects: