I am using QT framework. I have been using SIGNAL-SLOT for a while. I like it. :-)
But I cannot make it work when I use QThread. I always create new thread using “moveToThread(QThread …)” function.
Any suggestion? :-)
The error message is:
Object::connect: No such slot connection::acceptNewConnection(QString,int) in ..\MultiMITU600\mainwindow.cpp:14
Object::connect: (sender name: 'MainWindow')
I have read about similar problems but those were not connected to QThread.
Thanks, David
EDITED: you asked for source code
Here is one:
Here is the code:
The main class which contains the signal and the new thread:
mainwindow header:
class MainWindow : public QMainWindow
{
…
QThread cThread;
MyClass Connect;
...
signals:
void NewConnection(QString port,int current);
…
};
The constructor of the above class: .cpp
{
…
Connect.moveToThread(&cThread1);
cThread.start(); // start new thread
….
connect(this,SIGNAL(NewConnection(QString,int)),
&Connect,SLOT(acceptNewConnection(QString,int))); //start measuring
…
}
The class that contains the new thread and SLOT
Header:
class MyClass: public QObject
{
Q_OBJECT
….
public slots:
void acceptNewConnection(QString port, int current);
}
And the .cpp file of the above class:
void MyClass::acceptNewConnection(QString port, int current){
qDebug() << "This part is not be reached";
}
Finally I use emit in the class where the connection was made:
void MainWindow::on_pushButton_3_clicked()
{
…
emit NewConnection(port, 1);
}
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
public slots:
void acceptConnection(QString port, int current) {
qDebug() << "received data for port " << port;
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0) : QMainWindow(parent) {
myClass.moveToThread(&thread);
thread.start();
connect(this, SIGNAL(newConnection(QString,int)), &myClass, SLOT(acceptConnection(QString,int)));
emit newConnection("test", 1234);
}
signals:
void newConnection(QString, int);
private:
QThread thread;
MyClass myClass;
};
output:
received data for port "test"
Is your void MainWindow::on_pushButton_3_clicked() slot connected to a signal?
Also, for the sake of the clarity and readability of your code, keep the established naming convention and use lower case for object instances and member objects and methods.
Related
i am new to qt and trying to implement client server program. At client side I want to write from one thread(main) and read socket from another, but I get following notification.
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
[ Tue Jan 8 15:21:19 2019] SW Update File Transfered"
It do reads the socket but it gives this notifciation. Do I have to worry about it or just ignore it?
the code is as follows.
tcp_client.h
namespace Ui {
class TCP_Client;
}
class TCP_Client : public QMainWindow
{
Q_OBJECT
public:
explicit TCP_Client(QWidget *parent = nullptr);
MyThread *mThread;
~TCP_Client();
private slots:
void on_pushButton__SW_Update_clicked();
public:
Ui::TCP_Client *ui;
TCP_clientClass *client;
};
tcp_client.cpp
void on_pushButton__SW_Update_clicked{
/*some work done here*/
client->tcpSocket->write(buffer,16);
mThread = new MyThread(client->tcpSocket,this);
mThread->start();
}
mythread.h
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QTcpSocket *ID, QObject *parent =0);
void run ();
bool Stop;
signals :
void NumberChanged(QByteArray a);
public slots :
private:
QTcpSocket *socket1;
};
mythread.cpp
MyThread::MyThread(QTcpSocket *ID,QObject *parent) : QThread(parent)
{
socket1 = new QTcpSocket();
socket1=ID;
}
void MyThread::run()
{
QByteArray a;
qDebug()<<"Inside running thread";
socket1->waitForReadyRead(-1);
a= socket1->readAll();
qDebug() << a;
}
Basically, I am writing when button is pushed and inside I create a thread so that it can read data sent by server.
I have two simple classes(class A and class B).
In a.h, I just declared a QPushButton:
QPushButton *testBtn = new QPushButton(this);
In b.h:
class B : public QMainWindow
{
Q_OBJECT
public:
explicit B(QWidget *parent = nullptr);
A testingA;
public slots:
void testing();
};
and b.cpp:
B::B(QWidget *parent) : QMainWindow(parent)
{
connect(testingA.testBtn, &QPushButton::clicked, this, &B::testing);
}
void B::testing()
{
qDebug() << "testing";
}
I am trying to connect the signal in class A to the slot in class B, but from the code I provide, it's not working.
So what is the right way to do it? Thanks
Edit:
According to PRIME's answer, I made a few changes.
In A's constructor, added:
connect(testBtn, &QPushButton::clicked, [this](){OnButtonClicked();});
to emit the own defined OnButtonClicked() signal;
and in B's construtor, changed to this:
connect(&testingA, &A::OnButtonClicked, this, &B::testing);
But when I clicked the button, the testing slot still not triggered.
Edit 2:
After doing some researches and trying a few times, I found that if I created B's object in A's constructor, and then connect A's signal to B's slot in A, it will work.
But I really can not figure out why I can not connect A's signal to B's slot in B.
This is what's in the main.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
B b;
A w;
w.show();
return a.exec();
}
Is that because of some reasons that A's object is out of scope in B?
Can someone tell me where I did wrong? Thanks so much.
Don't do it like this, hide your button in the class A, emit your own defined signal from class A lets call it OnButtonClicked.
Cascading code(inside A's c'tor):
connect(testBtn , &QPushButton::clicked, [this](){OnButtonClicked();});
You will also have to declare this new signal in class A now:
So class A must have folowing besides whatever it has right now:
class A
{
Q_OBJECT
signals:
void OnButtonClicked();
};
No special slot is needed since you are using a Lambda as a slot for the signal OnButtonClicked.
Connection in class B( do it in the c'tor ):
connect(testingA, &A::OnButtonClicked, this, &B::testing);
You can connect signal-to-signal in your sender object, for example widget containing the button:
class MyWidget : public QWidget
{
Q_OBJECT
QPushButton *pushButton;
public:
explicit MyWidget(QWidget *parent = nullptr) : QWidget(parent), pushButton(new QPushButton(this)) {
connect(pushButton, &QPushButton::click, this, &MyWidget::buttonClicked);
}
signals:
void buttonClicked();
public slots:
};
By the way you would normally send signals by using emit keyword, e.g.:
emit buttonClicked();
Then the consumer:
class TestObject : public QObject
{
Q_OBJECT
public:
explicit TestObject(QObject *parent = nullptr) : QObject(parent) { }
public slots:
void onButtonClicked() {
qDebug() << "clicked";
}
};
And connect both instances:
MyWidget widget;
TestObject to;
QObject::connect(&widget, &MyWidget::buttonClicked, &to, &TestObject::onButtonClicked);
in your class A you should use the signal testing of the class B, if you clicked on your button the OnButtonClicked function will be activated
A:
public slots:
void OnButtonClicked();
void A::OnButtonClicked()
{
...
emit testing(1);
}
B:
signals:
void testing(int level);
then to connect both you can do this
connect(startButton, &QPushButton::clicked, board, &A::OnButtonClicked);
I'm developing a multithreaded application, I need to instantiate n devices by modbus.
So I created a controller (ServiceSM) that instantiates N threads (ServiceSlots).
The devices are varied so I had to create "drivers" for each type of device, one of the drivers uses the QModbusClient class, so I created a controller to manage the device type.
schema
To test the operation of the state machine and connection to the device, I made an example code to run in a graphical interface.
I deleted some snippets of code that do not matter to make it easier to understand
In the MD4040driver class
When my code runs this section, the following messages appear.
If I instantiate the DeviceDriver class in the graphical interface, it works perfectly, the problem occurs when I instantiate it inside a thread.
when calls
modbusDevice->connectDevice()
MD4040drive::sm_conn() - try connect - this my message
Error:
QObject::connect: Cannot queue arguments of type
'QModbusDevice::State' (Make sure 'QModbusDevice::State' is registered
using qRegisterMetaType().)
QObject: Cannot create children for a parent that is in a different
thread. (Parent is QTcpSocket(0x24a6ce8), parent's thread is
ServiceSlots(0xea66488), current thread is QThread(0x2418a78)
QObject: Cannot create children for a parent that is in a different
thread. (Parent is QTcpSocket(0x24a6ce8), parent's thread is
ServiceSlots(0xea66488), current thread is QThread(0x2418a78)
void MD4040drive::sm_conn()
{
if (modbusDevice->state() != QModbusDevice::ConnectedState) {
modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, this->cfg.modbus.porta );
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, this->cfg.modbus.ip);
modbusDevice->setTimeout( this->cfg.modbus.timeout );
modbusDevice->setNumberOfRetries(this->cfg.modbus.retries);
qDebug() << "MD4040drive::sm_conn() - try connect";
if (!modbusDevice->connectDevice()) {
qDebug() << "Erro: " << modbusDevice->errorString();
} else {
qDebug() << "Aguardando conexão...";
}
}
else{
//already connected...
this->getDados_NO_TH();
}
}
rest my code(parts)
devicedriverviewgui.h
devicedriverviewgui.cpp
class DeviceDriverViewGUI : public QDialog
{
Q_OBJECT
public:
explicit DeviceDriverViewGUI(QWidget *parent = 0);
~DeviceDriverViewGUI();
private slots:
void on_pbTry_clicked();
private:
Ui::DeviceDriverViewGUI *ui;
ServiceSlots *serviceSlot;
};
void DeviceDriverViewGUI::on_pbTry_clicked()
{
Equip equip_try = Equip();
serviceSlot = new ServiceSlots();
serviceSlot->setEquipamento(equip_try);
serviceSlot->start();
}
serviceslots.h
serviceslots.cpp
class ServiceSlots : public QThread
{
Q_OBJECT
public:
ServiceSlots();
void run();
private:
QTimer *timer;
DeviceDriver *device;
private slots:
void sm_getData();
void device_response(bool boEnd);
};
void ServiceSlots::run()
{
int e;
eventLoop = new QEventLoop();
timer = new QTimer();
connect(timer, SIGNAL(timeout()),this, SLOT(sm_controler()));
timer->start(TICK_SM_SLOT);
this->device = new DeviceDriver();
e = eventLoop->exec();
qDebug() << "Exit loop"<< e;
}
void ServiceSlots::sm_controler()
{
if(this->idleState){;}
else{
this->sm_getData();
this->idleState = true;
}
}
void ServiceSlots::sm_getData()
{
connect(device,SIGNAL(end(bool)),this,SLOT(device_response(bool)));
device->requestDeviceDriver(&this->equipamento,&this->next_date);
}
devicedriver.h
devicedriver.cpp
class DeviceDriver : public QObject
{
Q_OBJECT
public:
DeviceDriver();
void requestDeviceDriver(Equip *equip,QDateTime *date);
private:
//Drivers de dispositivos..
MD4040drive *md4040;
private slots:
//Request data to driver...
void request();
signals:
void end(bool boEnd);
};
void DeviceDriver::request()
{
connect(md4040,SIGNAL(end(bool)),this,SLOT(md4040_end(bool)));
this->md4040->requestMD4040drive(&this->equip,&this->date);
}
DeviceDriver::DeviceDriver(){
----
md4040 = new MD4040drive();
---
}
void DeviceDriver::requestDeviceDriver(Equip *equip, QDateTime *date){
this->equip = *equip;
this->date = *date;
this->request();
}
md4040drive.h
md4040drive.cpp
class MD4040drive : public QObject // QObject//public QObject QRunnable QThread
{
Q_OBJECT
public:
explicit MD4040drive(QObject *parent = 0);
~MD4040drive();
void requestMD4040drive(Equip *equip,QDateTime *date);
private:
void run();
QModbusClient *modbusDevice;
private slots:
void m_conn();
signals:
void end(bool boRun);
};
MD4040drive::MD4040drive(QObject *parent): QObject(parent)
{
modbusDevice = new QModbusTcpClient();
connect(modbusDevice, &QModbusClient::stateChanged,this, &MD4040drive::onStateChanged);
}
void MD4040drive::requestMD4040drive(Equip *equip, QDateTime *date)
{
this->equip = *equip;
this->date = *date;
this->run();
}
void MD4040drive::run()
{
this->sm_conn();
}
void MD4040drive::sm_conn()
{
if (modbusDevice->state() != QModbusDevice::ConnectedState) {
modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, this->cfg.modbus.porta );
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, this->cfg.modbus.ip);
modbusDevice->setTimeout( this->cfg.modbus.timeout );
modbusDevice->setNumberOfRetries(this->cfg.modbus.retries);
qDebug() << "MD4040drive::sm_conn() - try connect";
if (!modbusDevice->connectDevice()) {
qDebug() << "Erro: " << modbusDevice->errorString();
} else {
qDebug() << "Aguardando conexão...";
}
}
else{
//already connected...
this->getDados_NO_TH();
}
}
There are a few problems:
You need to call qRegisterMetaType<QModbusDevice::State>() in main().
You need to maintain parent-child relationships between all objects that are potentially moved to other threads as a group.
The ServiceSlots and DeviceDriver classes seem to be unnecessary.
The ubiquitous this-> is non-idiomatic C++. Don't write this-> unless you need to disambiguate a member from a local variable.
Prefer to hold objects by value if they have the same lifetime as the parent object. Let the compiler generate memory management code for you!
Leverage C++11.
Fist of all, let's have a helper SafeThread class that provides us with a thread that is safely destructible at any time:
class SafeThread : public QThread {
Q_OBJECT
using QThread::run;
public:
using QThread::QThread;
~SafeThread() { quit(); wait(); }
};
The DeviceDriverViewGUI class can hold the drive and its thread by value:
class DeviceDriverViewGUI : public QDialog
{
Q_OBJECT
public:
explicit DeviceDriverViewGUI(QWidget *parent = nullptr);
private:
Ui::DeviceDriverViewGUI ui;
MD4040drive drive;
SafeThread driveThread{this};
Equip equipamento;
QDateTime nextDate;
Q_SLOT void on_pbTry_clicked();
};
Then, the pushbutton can be connected directly to the drive's thread context, and run the requestMD4040drive in the proper thread:
DeviceDriverViewGUI::DeviceDriverViewGUI(QWidget *parent) : QDialog(parent)
{
ui.setupUi(this);
// vvvvvv -- gives the thread context
connect(ui.pbTry, &QPushButton::clicked, &drive, [this]{
Q_ASSERT(QThread::currentThread() == drive.thread()); // ensured by the thread context
drive.requestMD4040drive(&equipamento, nextDate);
});
connect(&drive, &MD4040drive::end, this, [this](bool end){
//...
});
drive.moveToThread(&driveThread);
driveThread.start();
}
When done this way, you don't need any extraneous helper objects nor timers to queue requests. Qt handles all of it.
When passing Qt value classes to functions, pass them by const reference, not by pointer. The MD4040drive should look roughly as follows:
class MD4040drive : public QObject
{
Q_OBJECT
public:
explicit MD4040drive(QObject *parent = nullptr);
void requestMD4040drive(Equip *equip, const QDateTime &date);
Q_SIGNAL void end(bool boRun);
private:
Equip *equip = nullptr;
QDateTime date;
QModbusTcpClient modbusDevice{this};
Cfg cfg;
Q_SLOT void onStateChanged();
Q_SLOT void m_conn();
void sm_conn();
void getDados_NO_TH() {}
};
The implementation:
MD4040drive::MD4040drive(QObject *parent): QObject(parent)
{
connect(&modbusDevice, &QModbusClient::stateChanged,this, &MD4040drive::onStateChanged);
}
void MD4040drive::requestMD4040drive(Equip *equip, const QDateTime &date)
{
this->equip = equip;
this->date = date;
sm_conn();
}
void MD4040drive::sm_conn()
{
if (modbusDevice.state() != QModbusDevice::ConnectedState) {
modbusDevice.setConnectionParameter(QModbusDevice::NetworkPortParameter, cfg.modbus.porta );
modbusDevice.setConnectionParameter(QModbusDevice::NetworkAddressParameter, cfg.modbus.ip);
modbusDevice.setTimeout( this->cfg.modbus.timeout );
modbusDevice.setNumberOfRetries(this->cfg.modbus.retries);
qDebug() << "MD4040drive::sm_conn() - try connect";
if (!modbusDevice.connectDevice()) {
qDebug() << "Erro: " << modbusDevice.errorString();
} else {
qDebug() << "Aguardando conexão...";
}
}
else{
//already connected...
getDados_NO_TH();
}
}
The configuration class might look as follows - notice that the compiler will generate the necessary constructors and destructors for you:
struct Cfg {
struct Modbus {
int porta = 0;
QString ip = QStringLiteral("127.0.0.1");
int timeout = 1000;
int retries = 2;
} modbus;
};
Make sure 'QModbusDevice::State' is registered using qRegisterMetaType()
Means you need to call qRegisterMetaType<QModbusDevice::State>(); before connecting signal/slot that would pass this type of parameter between threads.
And/or add Q_DECLARE_METATYPE(QModbusDevice::State) macro at global scope (never understood clearly which one is actually needed, putting both works for sure...).
See this post for more details: Emitting signals with custom types does not work
I create simple multi threading server:
Create server
If new connection create new QThreadpool - QRunnable
In runnable send message to client and wait request
If client was been disconnected runnable write qDebug and runnable quit.
server.h
class Server : public QTcpServer
{
Q_OBJECT
public:
explicit Server(QObject *parent = 0);
void StartServer();
protected:
void incomingConnection(int handle);
private:
QThreadPool *pool;
};
server.cpp:
#include "server.h"
Server::Server(QObject *parent) :
QTcpServer(parent)
{
pool = new QThreadPool(this);
pool->setMaxThreadCount(10);
}
void Server::StartServer()
{
this->listen(QHostAddress(dts.ipAddress),80));
}
void Server::incomingConnection(int handle)
{
Runnable *task = new Runnable();
task->setAutoDelete(true);
task->SocketDescriptor = handle;
pool->start(task);
}
runnable.h
class Runnable : public QRunnable
{
public:
Runnable();
int SocketDescriptor;
protected:
void run();
public slots:
void disconnectCln();
};
runnable.cpp
#include "runnable.h"
Runnable::Runnable()
{
}
void Runnable::run()
{
if(!SocketDescriptor) return;
QTcpSocket *newSocketCon = new QTcpSocket();
newSocketCon->setSocketDescriptor(SocketDescriptor);
!!! How make it???!!! QObgect::connect(newSocketCon, SIGNAL(disconnected()), this, SLOTS(disconnectCln()));
newSocketCon->write(mes.toUtf8().data());
newSocketCon->flush();
newSocketCon->waitForBytesWritten();
}
void Runnable::disconnectCln()
{
qDebug() << "Client was disconnect";
}
You seem to have neglected to actually ask a question, but here's the immediate problem I spot with your code: Your Runnable class does not inherit from QObject, and thus cannot have signals and slots. You will need to do that to have any hope of making it work.
class Runnable : public QObject, public QRunnable
{
Q_OBJECT
public:
Runnable();
int SocketDescriptor;
protected:
void run();
public slots:
void disconnectCln();
};
There are two important things to note here. 1) if you use multiple inheritance, QObject must come first in the list. 2) To use signals and slots you must include the Q_OBJECT macro in your class definition.
I have the class :
class SupervisionManager : public QThread {
Q_OBJECT public:
explicit SupervisionManager(ComAds* com, ComRegEtat* comEt,
ComRegOrdonnanceur* comOrdo,
QObject *parent = 0);
~SupervisionManager();
protected:
virtual void run();
private:
void actionFromPlc();
ComRegEtat::Antichoc antichoc;
signals:
void majAntichoc(ComRegEtat::Antichoc&);
};
and the implementation:
void SupervisionManager::run() {
manage=true;
while(manage)
{
actionFromPlc();
usleep(5000);
}
}
void SupervisionManager::actionFromPlc() {
antichoc.SAS = false;
emit majAntichoc(antichoc);
}
And I connect this signal with :
connect(manager, SIGNAL(majAntichoc(ComRegEtat::Antichoc&)),
preparation, SLOT(affichageAntichoc(ComRegEtat::Antichoc&)));
How do to emit a signal with a struct in its parameter list?
I think I have to use a QSignalMapper but I don't understand how.
In absolutely same way as you emit other things..
ComRegEtat::Antichoc myStruct;
.. some initialisation code
emit majAntichoc(myStruct);
I dont know for sure about latest Qt (after they changed signals/slot be templates based), but before 'emit' was just empty define, so you should look on emit like on function call...
With my code, the slot isn't called whereas the signal is emitted.
I found the solution :
signals:
void majAntichoc(ComRegEtat::Antichoc *);
and
slot :
void affichageAntichoc(ComRegEtat::Antichoc *);
And I don't have to use a QSignalMapper.
Thanks