Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use enums in Qt signals and slots

Tags:

c++

enums

qt

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;
}
like image 203
Kotte Avatar asked Sep 11 '12 11:09

Kotte


People also ask

How signals and slots work in Qt?

In Qt, we have an alternative to the callback technique: We use signals and slots. A signal is emitted when a particular event occurs. Qt's widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. A slot is a function that is called in response to a particular signal.

Are Qt signals and slots thread safe?

It is generally unsafe to provide slots in your QThread subclass, unless you protect the member variables with a mutex. On the other hand, you can safely emit signals from your QThread::run() implementation, because signal emission is thread-safe.

What is enum in Qt?

An enumerated type, or enum, is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type. Enums are incredibly useful when portraying status, options, or modes that are exclusive within a grouping.


2 Answers

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 :-)

like image 62
Kotte Avatar answered Sep 20 '22 13:09

Kotte


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)));
like image 40
drescherjm Avatar answered Sep 19 '22 13:09

drescherjm