How to use enums in Qt signals and slots - c++

I have some trouble with using enum types in signals. Basicly I have two classes, a state machine and a thread handling the state machine. When the state is changed I want to send a signal with the new state. I also want to represent the state using an enum. In my full blown code the state machine is implemented in a separate shared library, but the code below gives the exact same error.
When I run the code I get the following behaviour:
kotte#EMO-Ubuntu:sigenum $ ./sigenum
Object::connect: No such slot MyThread::onNewState(state)
Test signal
Test signal
...
I have four files in my sample code: statemachine.h, statemachine.cpp, main.h and main.cpp. The main function simply starts the thread, the thread then creates an instance of the StateMachine and processes signals from the StateMachine. I am pretty new to Qt, so I was a bit puzzled when I realised that you have to enclose the enum with Q_ENUMS and register it with the type system. So It's fully possible that I've made some rookie mistake
The code below is a bit long, but I wanted it to be as similar to my real code as possible.
statemachine.h looks like:
// statemachine.h
#ifndef _STATEMACHINE_H
#define _STATEMACHINE_H
#include <QtCore>
class StateMachine : public QObject
{
Q_OBJECT
Q_ENUMS(state)
public:
enum state {S0, S1, S2};
void setState(state newState);
signals:
void stateChanged(state newState);
void testSignal(void);
};
Q_DECLARE_METATYPE(StateMachine::state);
#endif
And it is implemented as:
// statemachine.cpp
#include <QtCore>
#include "statemachine.h"
void StateMachine::setState(state newState)
{
emit stateChanged(newState);
emit testSignal();
}
The thread is defined as
// main.h
#ifndef _MAIN_H
#define _MAIN_H
#include <QtCore>
#include "statemachine.h"
class MyThread : public QThread
{
Q_OBJECT
private:
void run(void);
private slots:
void onNewState(StateMachine::state);
void onTestSignal(void);
private:
StateMachine *myStateMachine;
};
#endif
And it is implemented as follows:
// main.cpp
#include <QtCore>
#include <QApplication>
#include "statemachine.h"
#include "main.h"
void MyThread::run()
{
myStateMachine = new StateMachine();
qRegisterMetaType<StateMachine::state>("state");
// This does not work
connect(myStateMachine, SIGNAL(stateChanged(state)),
this, SLOT(onNewState(state)));
// But this does...
connect(myStateMachine, SIGNAL(testSignal()),
this, SLOT(onTestSignal()));
forever {
// ...
myStateMachine->setState(StateMachine::S0);
}
}
void MyThread::onTestSignal()
{
qDebug() << "Test signal";
}
void MyThread::onNewState(StateMachine::state newState)
{
qDebug() << "New state is:" << newState;
}

By using fully qualified names everywhere I got it to work
If I change the declaration of stateChanged() to
signals:
void stateChanged(StateMachine::state newState);
And registers the type with
qRegisterMetaType<StateMachine::state>("StateMachine::state");
and also uses this name in the connect statement
connect(myStateMachine, SIGNAL(stateChanged(StateMachine::state)),
this, SLOT(onNewState(StateMachine::state)));
Wouldn't have solved this without the input from drescherjm, thanks :-)

I believe the following is state is not defined in your MyThread class.
Use the following
connect(myStateMachine, SIGNAL(stateChanged(StateMachine::state)),
this, SLOT(onNewState(StateMachine::state)));
Edit:
Maybe this will work
connect(myStateMachine, SIGNAL(stateChanged(state)),
this, SLOT(onNewState(StateMachine::state)));

You should get rid of SIGNAL and SLOT since Qt can detect mismatches at compile time. You can also avoid having to use Q_DECLARE_METATYPE and qRegisterMetaType() by using Q_ENUM instead of Q_ENUMS - this was introduced in Qt 5.5, Finally enum class is a strongly typed version of enum:
// statemachine.h
#ifndef _STATEMACHINE_H
#define _STATEMACHINE_H
#include <QtCore>
class StateMachine : public QObject
{
Q_OBJECT
Q_ENUM(state)
public:
enum class state {S0, S1, S2};
void setState(state newState);
signals:
void stateChanged(state newState);
void testSignal(void);
};
#endif
// main.cpp
#include <QtCore>
#include <QApplication>
#include "statemachine.h"
#include "main.h"
void MyThread::run()
{
myStateMachine = new StateMachine();
connect(myStateMachine, &StateMachine::stateChanged, this, &MyThread::NewState);
connect(myStateMachine, &StateMachine::testSignal, this, &MyThread::onTestSignal);
forever {
// ...
myStateMachine->setState(StateMachine::S0);
}
}

Related

Issue with QNetworkAccessManager Singleton Injection

I'm writing a RSS Feed Reader in Qt. My approach is to save every Feed in an object of the class Feed. The download is handled inside the Feed class, therefore I need to make sure that every object of Feed can access the (one) NetworkAccessManager. To manage this (and to make sure the NetworkAccessManager has only one instance) I want to make a Singleton injection. The problem is, with my current code I get the following error message
...main.cpp:-1: Fehler: undefined reference to `NetworkMgr::qnam'
Following is the code of which I think it is the most relevant for the problem, if you need anything more let me know.
I try to get the pointer on the instance of the NetworkAccessManager directly inside the constructor of the Feed class and then safe it in a private variable.
#ifndef FEED_H
#define FEED_H
#include <QObject>
#include <QGuiApplication>
#include <QtQuick>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPixmap>
#include <iostream>
#include <QDomDocument>
#include "NetworkMgr.h"
using namespace std;
class Feed : public QObject {
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
public:
Feed(QObject* parent = NULL) : QObject(parent){
m_nMgr = NetworkMgr::getInstance();
connect(m_nMgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(parse(QNetworkReply*)));
m_active = false;
};
QString name();
void setName(QString);
QString url();
void setUrl(QString);
int id();
void setId(int);
bool active();
void setActive(bool);
public slots:
void get();
private:
QString m_name;
QString m_url;
int m_id;
bool m_active;
QNetworkAccessManager* m_nMgr;
private slots:
void parse(QNetworkReply* reply);
signals:
void nameChanged();
void urlChanged();
void idChanged();
void activeChanged();
};
#endif
This is my Singleton class:
#ifndef NETWORKMGR_H
#define NETWORKMGR_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
class NetworkMgr : public QObject {
Q_OBJECT
NetworkMgr(QObject* parent = NULL) : QObject(parent){};
~NetworkMgr(){
delete qnam;
}
static QNetworkAccessManager* qnam;
public:
static QNetworkAccessManager* getInstance(){
if(qnam == NULL){
qnam = new QNetworkAccessManager();
}
return qnam;
}
NetworkMgr(NetworkMgr const& copy) = delete;
NetworkMgr& operator=(NetworkMgr const& copy) = delete;
};
//QNetworkAccessManager* NetworkMgr::qnam = NULL;
#endif
As you can see, I also tried to initialize qnam to nullpointer, but then I get the error:
...Feed.h:17: Fehler: multiple definition of NetworkMgr::qnam' debug/main.o: In function Feed::~Feed()':
...Feed.h:17: multiple definition of `NetworkMgr::qnam'
And this is how I tried to call it as a test from main.cpp:
Feed feed;
feed.setUrl("https://www.deskmodder.de/blog/feed/");
feed.get();
I tried getting the instance of NetworkAccessManager outside the constructor as well, but the error stays the same.
I would really appreciate it, if anybody here has an idea what I could try to get rid of this error.. Before I take a completely different approach, I'd like to try it this way.
Thanks in advance to anyone contributing to fix my problem!

QObject::connect cant connect signal to a slot of *this* object

The title might be hard to understand, sorry for that.
I have problem connection signal to slot. There are no compile errors, but the connect returns false.
There is error line printed in the console:
QObject::connect: No such slot QObject::clientConnected_() in ../server/server.cpp:8
I think the problem is that connect does not see the slot clientConnected_() in the class Server. Or maybe looks for it in class QObject for some reason.
Code is as follows:
server.h
#ifndef SERVER_H
#define SERVER_H
#include <QtNetwork>
#include <QTcpServer>
#include <QObject>
class Server : public QObject
{
Q_OBJECT
public:
bool startListening(const quint16 port);
public slots:
static void clientConnected_();
private:
QTcpServer * server_;
};
#endif // SERVER_H
server.cpp
#include "server.h"
#include <iostream>
bool Server::startListening(const quint16 port)
{
server_ = new QTcpServer();
QObject::connect(server_,SIGNAL(newConnection()),this,SLOT(clientConnected_()));
return server_->listen(QHostAddress::Any,port);
}
void Server::clientConnected_()
{
std::cout << "Connection established!" << std::endl;
return;
}
Any ideas?
static void clientConnected_();
change to
private slots:
void clientConnected_();
As was mentioned, youd should decalre your 'void clientConnected_()' as a non-static (you can't connect to static member, but if you want you can call static method from your slot). And also you have to declare slots in 'slot' section to make MOC able to parse your slots.
More information you can find here http://doc.qt.io/qt-5/signalsandslots.html

Slots and signals in QThread

I'm tryng to connect() a signal in thread 2 to slot in a main thread, I have constructor like that, which runs in thread 2 as a child class of QThread:
InputHandler::InputHandler()
{
connect(this, SIGNAL(write(User*, Message)), TTCServer::ttcserver, SLOT(write(User*, Message)));
qDebug() << "new thread created!";
}
but I get an runtime error
QObject::connect: No such signal QThread::write(User*, Message)
My inputhandler.h looks like that:
#ifndef INPUTHANDLER_H
#define INPUTHANDLER_H
#include <QThread>
#include <QDebug>
#include <QString>
#include "message.h"
#include "data.h"
class InputHandler : public QThread
{
public:
InputHandler();
void run();
private:
Message message;
void login(User* user, QString login);
void sendLogins(User* user);
void startGameWith(User* user, QString opponentLogin);
signals:
void write(User* user, Message message);
};
#endif // INPUTHANDLER_H
And If it does matter, I'm also inncluding ttcserver.h:
#ifndef TTCSERVER_H
#define TTCSERVER_H
#include <QTcpServer>
#include <QObject>
#include <QDebug>
#include "data.h"
#include "user.h"
#include "message.h"
#include "inputhandler.h"
class TTCServer : public QTcpServer
{
Q_OBJECT
public:
explicit TTCServer();
static TTCServer* ttcserver;
void run();
signals:
public slots:
void newConnection();
void write(User* user, Message message);
private:
QTcpServer* server;
Message* message;
void handleInputFrom(User* user);
};
#endif
// TTCSERVER_H
void write method is definied in ttcserver.cpp like that:
void TTCServer::write(User* user, Message message)
{
qDebug() << "Signal recieved!";
}
So why write(User*, Message) in a connect() function tries to be QThread::write() instead of InputHandler::write()?
User and Message are classes, to be clear.
You forgot to add the Q_OBJECT macro into InputHandler:
class InputHandler : public QThread
{
Q_OBJECT
public:
InputHandler();
void run();
<...>
Since the macro is missing, moc will not create the corresponding code for signals/slots to work in that class, thus it tries to connect to QObject, since it does have the macro, and is the base class.

How to send data between QThreads?

I have an event in one thread and need to invoke handler function in another. Usually I use function connect(), but in case of two threads I had an error:
QObject::connect: Cannot queue arguments of type 'QVector<unsigned char>'
(Make sure 'QVector<unsigned char>' is registered using qRegisterMetaType().)
I tried to use qRegisterMetaType(), but don't understood clearly how and where should I declare it.
I wrote code sample, which just invoke execution in thread0 from thead1. I didn't include my attempts of using qRegisterMetaType() because they all failed =)
thread0.h:
#ifndef THREAD0_H
#define THREAD0_H
#include <QThread>
#include <QVector>
class thread0 : public QThread
{
Q_OBJECT
public:
thread0();
~thread0();
protected:
void run() Q_DECL_OVERRIDE;
public slots:
void printBuff(QVector<unsigned char> vec);
};
#endif // THREAD0_H
thread1.h:
#ifndef THREAD1_H
#define THREAD1_H
#include <QThread>
#include <QVector>
class thread1 : public QThread
{
Q_OBJECT
public:
thread1();
~thread1();
protected:
void run() Q_DECL_OVERRIDE;
signals:
void sendToPrint(QVector<unsigned char> vec);
};
#endif // THREAD1_H
main.cpp:
#include <QCoreApplication>
#include "thread0.h"
#include "thread1.h"
#include <QObject>
#include <QMetaType>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
thread0 *th0 = new thread0();
thread1 *th1 = new thread1();
QObject::connect(th1, &thread1::sendToPrint, th0, &thread0::printBuff);
th0->start();
th1->start();
return a.exec();
}
and thread1.cpp:
#include "thread1.h"
thread1::thread1()
{
}
thread1::~thread1()
{
}
void thread1::run()
{
QVector<unsigned char> vec = {0, 1, 2, 3};
emit sendToPrint(vec);
}
P.S. If I use direct connection code works.
QObject::connect(th1, &thread1::sendToPrint, th0, &thread0::printBuff, Qt::DirectConnection);
Add this in your main()
qRegisterMetaType<QVector<unsigned char> >("QVector<unsigned char>");
Between threads, Qt uses a Queued Connection. This mechanism requires each type passed as a parameter by the signal to the slot to be known of the Qt meta-object system. Most Qt types are already registered, but not template ones as you need one qRegisterMetaType per specialization of the template
If you use direct connection, your code may seem to work, but printBuff will run in th1, not th0. And in fact, th0 thread will do nothing. This may cause the program to crash if printBuff was designed to run in thread0 only, due to thread safety issues.

Qt: Passing Signal containing 2d array from one thread to another

I am trying to learn Qt and I am attempting to do so by making a little titres game. Currently I have a 2d array which represents the game board.
Every second this 2d array is changed by a thread (representing the passage of time) and then this thread emits a signal telling the main GUI to update based on the new game board.
My Thread is as follows:
gamethread.h
#ifndef GAMETHREAD_H
#define GAMETHREAD_H
#include <QtCore>
#include <QThread>
#include<QMetaType>
class GameThread : public QThread
{
Q_OBJECT
public:
explicit GameThread(QObject *parent = 0);
void run();
private:
int board[20][10]; //[width][height]
void reset();
signals:
void TimeStep(int board[20][10]);
};
#endif // GAMETHREAD_H
gamethread.cpp
#include "gamethread.h"
#include <QtCore>
#include <QtDebug>
//Game Managment
GameThread::GameThread(QObject *parent) :
QThread(parent)
{
reset();
}
void GameThread::reset()
{
...
}
//Running The Game
void GameThread::run()
{
//Do Some Stuff
emit TimeStep(board);
}
and the main UI which should receive the signal and update based on the new board is:
tetris.h
#ifndef TETRIS_H
#define TETRIS_H
#include <QMainWindow>
#include "gamethread.h"
namespace Ui{
class Tetris;
}
class Tetris : public QMainWindow
{
Q_OBJECT
public:
explicit Tetris(QWidget *parent = 0);
~Tetris();
GameThread *mainThread;
private:
Ui::Tetris *ui;
private slots:
int on_action_Quit_activated();
void on_action_NewGame_triggered();
public slots:
void onTimeStep(int board[20][10]);
};
#endif // TETRIS_H
tetris.cpp
#include <QMessageBox>
#include <QtGui>
#include <boost/lexical_cast.hpp>
#include <string>
#include "tetris.h"
#include "ui_tetris.h"
Tetris::Tetris(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Tetris)
{
ui->setupUi(this);
mainThread = new GameThread(this);
connect(mainThread, SIGNAL(TimeStep(int[20][10])),
this, SLOT(onTimeStep(int[20][10])),
Qt::QueuedConnection);
}
Tetris::~Tetris()
{
delete ui;
}
void Tetris::onTimeStep(int board[20][10])
{
//receive new board update my display
}
void Tetris::on_action_NewGame_triggered()
{
mainThread->start();
}
When I run this I get:
QObject::connect: Cannot queue arguments of type 'int[20][10]'
(Make sure 'int[20][10]' is registered using qRegisterMetaType().)
I have looked into qRegisterMetaType and Q_DECLARE_METATYPE but I am not even remotely sure how to use them or even if I must use them. Can someone give the QT newbie some assistance?
You can wrap the board data in a class. It won't work if you merely typedef'd it, since Qt will try to use non-array operator new to create instances of board data. The compiler will detect it and rightfully complain.
It's bad style to derive from QThread like you are doing and use it as a generic QObject. QThread is conceptually a thread controller, not a thread itself. See this answer for the idiomatic way to do it. Your GameThread should be a QObject, not a QThread.
So:
struct Board {
int data[20][10];
}
Q_DECLARE_METATYPE(Board);
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
qRegisterMetatype<Board>("Board");
...
Game * game = new Game;
QThread thread;
game->connect(&thread, SIGNAL(started()), SLOT(start());
game->connect(game, SIGNAL(finished()), SLOT(deleteLater()));
thread.connect(&game, SIGNAL(finished()), SLOT(quit());
game.moveToThread(&thread);
thread.start(); // you can start the thread later of course
return app.exec();
}
class Game: public QObject
{
QTimer timer;
Board board;
public slots:
void start() {
connect(&timer, SIGNAL(timeout()), SLOT(tick()));
timer.start(1000); // fire every second
}
void finish() {
timer.stop();
emit finished();
}
protected slots:
void tick() {
... // do some computations that may take a while
emit newBoard(board);
// Note: it probably doesn't apply to trivial computations in
// a Tetris game, but if the computations take long and variable
// time, it's better to emit the board at the beginning of tick().
// That way the new board signal is always synchronized to the timer.
}
signals:
void newBoard(const Board &);
void finished();
}
What would happen if later you decided to change the size of the board? I think it would be better to encapsulate the concept of a board in an object, and pass around a pointer to said object.