Solved MainWindow GUI cannot call SLOT in worker thread UDP object
-
Hello,
I am unable to emit() a signal from the MainWindow object to a worker thread that will send & receive UDP Data.
The goal is to learn how to communicate from the GUI thread to the UDP thread.
I do not understand why the SLOT is not working. I used the Mandlebrot example but in reverse, by communicating from GUI to thread. I tried QT4 & QT5 connect(signal, slot) functions without success. I get zero errors/warnings @ compile timeMandlebrot example:
connect(&thread, SIGNAL(renderedImage(QImage,double)), this, SLOT(updatePixmap(QImage,double)));
Mine in mainwindow.cpp:
connect( this, SIGNAL(&MainWindow::runInitializeInthreadUDP), threadObj, SLOT(&MyUDP::Initialize), Qt::QueuedConnection);
They only message I get from the "Application Output" is:
QObject::connect: Parentheses expected, signal MainWindow::&MainWindow::runInitializeInthreadUDP in mainwindow.cpp:36
Please see the attached code and if anyone is able to help, that would be very much appreciated.
If more info is needed, please ask.Thank you,
Keith#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); qDebug()<<"From main GUI thread: "<<QThread::currentThreadId(); //Create GUI, but it is not shown until GUI.show is called MainWindow GUI; //generate GUI screen when this main function is finished GUI.show(); return a.exec(); }
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QtCore> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); //Class constructor private: Ui::MainWindow *ui; signals: void runInitializeInthreadUDP(); private slots: void on_pushButton_clicked(); }; #endif // MAINWINDOW_H
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QtCore> #include "myudp.h" #include "myThread.h" myThread* threadObj = new myThread(); Worker* worker = new Worker(); //MainWindow Class Constructor MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //Create UDP object and startup worker thread //https://wiki.qt.io/QThreads_general_usage MyUDP udpObj; udpObj.moveToThread(threadObj); connect(worker, SIGNAL (&Worker::threadFinishedSignal), threadObj, SLOT (quit())); connect(worker, SIGNAL (&Worker::threadFinishedSignal), worker, SLOT (deleteLater())); connect(threadObj, SIGNAL (&Worker::threadFinishedSignal), threadObj, SLOT (deleteLater())); threadObj->start(); // // //THIS NEXT PART DOES NOT WORK(Initialize() SLOT() function is never called)!!!! // // //initialize UDP ports //QT5 VERSION: connect( this, SIGNAL(&MainWindow::runInitializeInthreadUDP), threadObj, SLOT(&MyUDP::Initialize), Qt::QueuedConnection); //QT4 VERSION: // connect( this, SIGNAL(runInitializeInthreadUDP()), threadObj, SLOT(Initialize()), Qt::QueuedConnection); emit runInitializeInthreadUDP(); } //also does not work when pressing button on GUI (this _clicked function runs, but does not call SLOT &MyUDP::Initialize) void MainWindow::on_pushButton_clicked() { emit runInitializeInthreadUDP(); }
//#ifndef WORKERTHREAD_H //#define WORKERTHREAD_H #include <QtCore> //http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/ class myThread : public QThread { Q_OBJECT private: void run() { qDebug()<<"From worker thread: "<<currentThreadId(); connect(this, &QThread::finished, this, &QObject::deleteLater); exec(); } }; //https://wiki.qt.io/QThreads_general_usage class Worker : public QObject { Q_OBJECT signals: void threadFinishedSignal(); }; //#endif // WORKERTHREAD_H
#ifndef UDP_H #define UDP_H //Base class QObject #include <QUdpSocket> #include <QtCore> class MyUDP : public QObject { Q_OBJECT public: MyUDP(); //Constructor for UDP Class Object ~MyUDP(); //Destructor for UDP Class Object private: QUdpSocket *socket; public slots: void UDPreadInputData(); //Setup UDP port and connect port to fire function each time a UDP packet is received void Initialize(); }; #endif // UDP_H
#include "myudp.h" #include <QtCore> //Settings for UDP addresses QHostAddress remoteAddress = QHostAddress("192.168.1.1"); QHostAddress localAddress = QHostAddress("192.168.1.2"); quint16 sendPort = 1234; quint16 receivePort = 5678; //UDP buffer that will contain the data received from microcontroller QByteArray Buffer; MyUDP::MyUDP() //Constructor for UDP Class Object { } MyUDP::~MyUDP() //Destructor for UDP Class Object { } void MyUDP::Initialize() { socket = new QUdpSocket(this); //We need to bind the UDP socket to an address and a port // socket->bind(QHostAddress::LocalHost,receivePort); socket->bind(localAddress,receivePort); //Call the "UDPreadyRead" function each time the socket gets data on the defined UDP port connect(socket,SIGNAL(readyRead()),this,SLOT(UDPreadInputData())); qDebug()<<"UDP worker thread initialized"; } //Read something void MyUDP::UDPreadInputData() { Buffer.resize(socket->pendingDatagramSize()); int bufferSize = Buffer.size(); //read sing UDP packet from hardware ethernet tranceiver on computer socket->readDatagram(Buffer.data(), bufferSize, &remoteAddress, &receivePort); // http://lists.qt-project.org/pipermail/qt-interest-old/2009-June/008318.html // Read float32 data types out with a QDataStream QDataStream ds( Buffer.mid( 0, bufferSize ) ); //By default, a QDataStream is big endian and must be flipped depending on "processor architecture". ds.setByteOrder(QDataStream::LittleEndian); ds.setFloatingPointPrecision(QDataStream::SinglePrecision); //Throw out UDP Ethernet header and two bytes that are unused at beginning of packet (10 bytes) for(int i; i<5; i++) { qint16 temp; //each int16 is 2 bytes ds >> temp; } //Read the first two "start of sample bytes" qint16 HeaderByte1; ds >> HeaderByte1; //54321 start of sample int16 }
-
Hi @kma005,
I think this line:
connect( this, SIGNAL(&MainWindow::runInitializeInthreadUDP), threadObj, SLOT(&MyUDP::Initialize), Qt::QueuedConnection);
is mixing up the new and old slot syntaxes. Should probably be more like either:
connect( this, SIGNAL(runInitializeInthreadUDP(type, ...)), threadObj, SLOT(Initialize(type, ...)), Qt::QueuedConnection); // or connect( this, &MainWindow::runInitializeInthreadUDP, threadObj, &MyUDP::Initialize, Qt::QueuedConnection);
Note, the first version has argument types, and no
&
characters. And the second version has noSIGNAL
norSLOT
macros.See https://wiki.qt.io/New_Signal_Slot_Syntax for further explanation.
Cheers.
-
Paul,
Please see in "mainwindow.cpp". I have already tried what you said for Qt4 compatibility.
Both methods were already attempted, but without success.
//QT5 VERSION:
connect( this, SIGNAL(&MainWindow::runInitializeInthreadUDP), threadObj, SLOT(&MyUDP::Initialize), Qt::QueuedConnection);
//QT4 VERSION:
// connect( this, SIGNAL(runInitializeInthreadUDP()), threadObj, SLOT(Initialize()), Qt::QueuedConnection);
emit runInitializeInthreadUDP();Thank you,
Keith -
Hi @kma005,
Your Qt5 version is wrong, as I already described (should not have any
SIGNAL
norSLOT
macros).And both versions are trying to connect to an
Initialize
slot that does not exist on the receiverthreadObj
- it exists onudpObj
only (if you corrected the Qt5 syntax, the compiler would have pointed that out).Also I suspect you'll need to allocate
udpObj
on the heap, eg:MyUDP * udpObj = new MyUDP;
Otherwise it will be destroyed after the
MainWindow
constructor returns.Cheers.
-
Aside from the fact that class myThread does not have any slots, do you start an event loop within the thread? Without an event loop, the thread cannot receive signals.
-
THANK YOU PAUL COLBY!!!
The issue was the new syntax does not use "SIGNAL() and SLOT()" macros.
The "mainwindow.cpp" connect() function now does not have the "SIGNAL() and SLOT()" macros.Also the biggest issue was udpObj was destroyed when the mainwindow constructor finished.
I made a "MyUDP *udpObj = new MyUDP();" declaration at the top of "mainwindow.cpp".
next, "udpObj->moveToThread(threadObj);" is using an arrow since "udpObj" is now a pointer.Asperamanca, the thread event loop starts when "threadObj->start();" is called.
the thread exec() function is called in "myThread.h"
When "emit runInitializeInthreadUDP()" is called, "udpObj->Initialize()" will be called because the event loop in myThread is already running from the previous "threadObj->start();"THANK YOU PAUL COLBY!!!
I will try to mark this solved.
KeithPlease see my updated "mainwindow.cpp" file:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QtCore> #include "myudp.h" #include "myThread.h" myThread *threadObj = new myThread(); Worker *worker = new Worker(); MyUDP *udpObj = new MyUDP(); //MainWindow Class Constructor MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //Create UDP object and startup worker thread //https://wiki.qt.io/QThreads_general_usage udpObj->moveToThread(threadObj); connect(worker, SIGNAL (&Worker::threadFinishedSignal), threadObj, SLOT (quit())); connect(worker, SIGNAL (&Worker::threadFinishedSignal), worker, SLOT (deleteLater())); connect(threadObj, SIGNAL (&Worker::threadFinishedSignal), threadObj, SLOT (deleteLater())); threadObj->start(); // // //THIS NEXT PART DOES NOT WORK(Initialize() SLOT() function is never called)!!!! // // //initialize UDP ports //QT5 VERSION: // connect( this, SIGNAL(&MainWindow::runInitializeInthreadUDP), threadObj, SLOT(&MyUDP::Initialize), Qt::QueuedConnection); connect( this, &MainWindow::runInitializeInthreadUDP, udpObj, &MyUDP::Initialize, Qt::QueuedConnection); //QT4 VERSION: // connect( this, SIGNAL(runInitializeInthreadUDP()), threadObj, SLOT(Initialize()), Qt::QueuedConnection); emit runInitializeInthreadUDP(); } //also does not work when pressing button on GUI (this _clicked function runs, but does not call SLOT &MyUDP::Initialize) void MainWindow::on_pushButton_clicked() { emit runInitializeInthreadUDP(); }
-
@kma005 said in MainWindow GUI cannot call SLOT in worker thread UDP object:
connect() function now does not have the "SIGNAL() and SLOT()" macros
This is not correct. SIGNAL/SLOT connect variant is still available (old syntax). Since Qt5 there is the new connect syntax without SIGNAL/SLOT. So, you can use both.
Your problem was that you were mixing new and old connect syntax at the same time. You either use old or new syntax.Here you're mixing:
connect(worker, SIGNAL (&Worker::threadFinishedSignal), threadObj, SLOT (quit()));
It should be (old syntax):
connect(worker, SIGNAL(threadFinishedSignal()), threadObj, SLOT (quit()));
New syntax:
connect(worker, &Worker::threadFinishedSignal, threadObj, &myThread::quit);
-
@jsulm said in MainWindow GUI cannot call SLOT in worker thread UDP object:
@kma005 said in MainWindow GUI cannot call SLOT in worker thread UDP object:
connect() function now does not have the "SIGNAL() and SLOT()" macros
This is not correct. SIGNAL/SLOT connect variant is still available (old syntax).
I think @kma005 was saying that his updated
connect
invocation inmainwindow.cpp
is no longer usingSIGNAL()
andSLOT()
macros, ie because he's removed them now. I don't think he was suggesting that the older syntax is no longer available.Cheers.
-
@Paul-Colby OK, then I misunderstood the post
-
@Paul-Colby ,well no, @jsulm is correct.
If thePlease see my updated "mainwindow.cpp" file:
is indeed the one @kma005 uses, than he's still using old and new syntax mixed in one statement:udpObj->moveToThread(threadObj); connect(worker, SIGNAL (&Worker::threadFinishedSignal), threadObj, SLOT (quit())); connect(worker, SIGNAL (&Worker::threadFinishedSignal), worker, SLOT (deleteLater())); connect(threadObj, SIGNAL (&Worker::threadFinishedSignal), threadObj, SLOT (deleteLater())); threadObj->start(); // // //THIS NEXT PART DOES NOT WORK(Initialize() SLOT() function is never called)!!!! // // //initialize UDP ports //QT5 VERSION: // connect( this, SIGNAL(&MainWindow::runInitializeInthreadUDP), threadObj, SLOT(&MyUDP::Initialize), Qt::QueuedConnection); connect( this, &MainWindow::runInitializeInthreadUDP, udpObj, &MyUDP::Initialize, Qt::QueuedConnection); //QT4 VERSION: // connect( this, SIGNAL(runInitializeInthreadUDP()), threadObj, SLOT(Initialize()), Qt::QueuedConnection); emit runInitializeInthreadUDP();
the only connect that will work from that code-example is:
connect( this, &MainWindow::runInitializeInthreadUDP, udpObj, &MyUDP::Initialize, Qt::QueuedConnection);
everything depending on the thread finished signal is ignored.
-
All,
I assure you this line works in "mainwindow.cpp" and the "emit runInitializeInthreadUDP();" in mainwindow.cpp calls the function udpObj->Initialize().
connect( this, &MainWindow::runInitializeInthreadUDP, udpObj, &MyUDP::Initialize, Qt::QueuedConnection);I simply omitted the SIGNAL() and SLOT() words and all is right with the world due to Paul Colby.
I am using the QT5 syntax since the QT4 compiler did not catch the issue of threadObj->Initialize() was being called instead of udpObj->Initialize(). I had zero errors using the QT4 connect( this, SIGNAL(runInitializeInthreadUDP()), threadObj, SLOT(Initialize()), Qt::QueuedConnection); QT5 syntax for connect seems to catch those bad connection errors when compiling. When I used the QT5 syntax, it told me that udpObj should be used instead of threadObj in the connect() line.I apologize for the confusion.
Thank you everyone. I just wanted to check back if people had additional questions and i am glad I did to clear this up.-Keith
-
@J.Hilk said in MainWindow GUI cannot call SLOT in worker thread UDP object:
If the Please see my updated "mainwindow.cpp" file: is indeed the one @kma005 uses, than he's still using old and new syntax mixed in one statement:
Yes, that is true - he's still using mixed syntaxes. However, the OP was only ever about the final
connect
- the one involving theMainWindow::runInitializeInthreadUDP
signal, not the others. Its this last one that he's fixed, is no longer user mixed, and that his most recent comments were referring to.Indeed, he still has to go back and fix up the others too. He just wasn't claiming that the older syntax is not supported.
Cheers.