enums and enum classes in Qt Queued Slots - c++

Following advice from Qt documentation and this question's answers, I have code structured like so:
emulator.h:
class Emulator : public QObject
{
Q_OBJECT
public:
enum HaltCause {
Breakpoint,
ReadWatch,
WriteWatch,
UserHalted,
Reset,
SingleStep,
};
Q_ENUM(HaltCause)
...
signals:
void emulationHalted(HaltCause cause);
...
};
My MainWindow class has a matching slot:
private slots:
...
void onEmulationHalted(Emulator::HaltCause cause);
In mainwindow.cpp, the constructor contains:
...
qRegisterMetaType<Emulator::HaltCause>();
...
and in a method invoked later on in the MainWindow class:
...
connect(m_emulator, &Emulator::emulationHalted, this, &MainWindow::onEmulationHalted);
...
The Emulator class is threaded, so the connection between its signal and MainWindow's slot is queued.
Regardless of seemingly following all the guidelines for getting something into Qt's meta-object system, I still get this debug message when the Emulator's thread emits the signal:
QObject::connect: Cannot queue arguments of type 'HaltCause'
(Make sure 'HaltCause' is registered using qRegisterMetaType().)
I've tried this with Enumeration::HaltCause being both a simple enum and a strongly-typed enum class. Both yield the same results.
What am I missing?

Turns out the unqualified HaltCause used in the declaration of Emulator's slot confused the meta-object system. The resolution was to change
signals:
void EmulationHalted(HaltCause cause);
to
signals:
void EmulationHalted(Emulator::HaltCause cause);
in the Emulator declaration (emulator.h).

Related

Why does adding a function argument cause a SLOT() to be unrecognised?

I have a class as follows:
handler.h:
#ifndef HANDLER_H
#define HANDLER_H
#include <QObject>
class handler : public QObject
{
Q_OBJECT
public:
explicit handler(QObject *parent = nullptr);
~handler();
public slots:
void returnHandler(int input);
};
#endif // HANDLER_H
handler.cpp:
#include "handler.h"
#include "otherclass.h"
handler::handler(QObject *parent) : QObject(parent)
{
}
handler::~handler()
{
}
void handler::returnHandler(int input)
{
otherclass *otherclassPointer = otherclass::getInstance();
otherclassPointer->returnFunction(input);
}
As shown, this is a very simple class, which aims to receive an input and pass the input to a function in an external class ('otherclass'). In my main application ('main.cpp'), I create a QThread, and call the returnHandler slot when the QThread is started, as follows:
main.cpp:
QThread* newThread = new QThread();
handler* handlerPointer = new handler();
handlerPointer->moveToThread(newThread);
connect(newThread, SIGNAL(started()), handlerPointer, SLOT(returnHandler(someInput)));
newThread->start();
The issue I'm having is this:
I'm currently get the following error:
QObject::connect: No such slot handler::returnHandler(someInput) in ../app/main.cpp:100
However, if I remove the int input from the handler class (both the .h and .cpp files), the SLOT() is recognized and called successfully when the QThread is started.
Why does adding an argument cause the slot to no longer be recognized?
EDIT: Following some very informative and appreciated comments/answers below, I've modified the approach as follows:
Create a signal in the handler class, which matches the parameters of the returnHandler slot. E.g. void handlerSignal(int).
Used the handlerSignal() SIGNAL instead of the QThread::started() signal in the connect().
Emit the handlerSignal() once the QThread is started.
`
QThread* newThread = new QThread();
handler* handlerPointer = new handler();
handlerPointer->moveToThread(newThread);
connect(handlerPointer, SIGNAL(handlerSignal(int)), handlerPointer, SLOT(returnHandler(int)));
newThread->start();
emit handlerPointer->handlerSignal(someInput);
Thanks!
Two things:
Qt expects the signal and the slot to have the same parameter types.
In SLOT(), you have to provide types, and not names for the parameters.SLOT(returnHandler(int)) instead of SLOT(returnHandler(someInput))
Qt uses the signals and slots's names and argument list to identify them. I your case, Qt looks for a slot named 'returnHandler' and having only one parameter, from type 'someInput'.
connect takes strings as the identification of the signal & slot to connect. The macros SIGNAL and SLOT stringise their arguments (using the preprocessor functionality for that). The argument to SIGNAL or SLOT must therefore be the function name, with parameter types in the parentheses. You cannot do argument binding with them.
If you need to connect to a nullary signal, you need a nullary slot.

QObject::connect: signal not found

I have a MainWindow class which is declared in mainwindow.h and defined in mainwindow.cpp respectively like this:
In mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
...
void addNewTab(QString fullFilePath, QString textString="");
public slots:
void disableMenuItem();
...
private:
...
};
In mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
...
connect(this, &MainWindow::addNewTab, this, &MainWindow::disableMenuItem);
...
}
void MainWindow::addNewTab(QString fullFilePath, QString textString)
{
...
}
void MainWindow::disableMenuItem()
{
...
}
Everything compiles and run fine except for the following message on the console:
QObject::connect: signal not found in MainWindow
The message come from the connect call in the constructor above. What does that message mean in my case, and where am I doing wrong?
As drescherjm and Learner mentioned, you forgot to add a signals: section to your header file, and declare your signal within it.
Qt connects signals to slots at runtime, not at compile time, so mis-connected signals and slots cannot be detected until the program is actually run; that's why this problem is reported when it is.
Qt uses the moc preprocessor to turn signals and slots into standard c++, so that's why the signals: and slots: sections of your header will not cause problems when compiling.
Signals are fully defined by moc, so you do not need to define them in your .cpp file, but they still need to be in the header so moc knows to create them.
EDIT:
It appears that you are trying to use a signal with the name of one of your class functions. I don't think that's going to work. the documentation for the new signal/slot syntax indicates that you can connect TO anything, it doesn't have to be a slot, but I believe you still need to define your signal as a signal.

QObject::connect: No such signal

I need to know what am I doing wrong.
I tried researching about it but I can't really find anything that it's related to my case. I am new to QT and debugging signal and slots is kinda technical for me.
What I wanted to do is just simple: make a thread that will continuously send signal to my QProgressBar widget.
Here's my essential code snippets:
thread.h
class MyThread : public QThread
{
public:
MyThread(QWidget * parent = 0);
signals:
void valueChanged(int value);
protected:
void run();
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
MyThread * test = new MyThread(this);
connect(test,SIGNAL(valueChanged(int)),ui->progressBar,SLOT(setValue(int)));
test->start();
}
thread.cpp
MyThread::MyThread(QWidget * parent)
{
}
void MyThread::run(){
emit valueChanged(10); //for simplicity
}
void MyThread::valueChanged(int value){
}
I only have a single progressBar on my UI and my main is the same as the default.
Anyway, upon running of the code. I kept on getting this no such signal from my thread class. May I know what am I doing wrong?. I would also like to clarify if my understanding is right regarding signals and slots in my own words: it means that the slot will be triggered everytime the signal is called.
I believe the error message is due to a missing Q_OBJECT macro at the top of your MyThread declaration. The documentation at http://doc.qt.io/qt-5/signalsandslots.html explains this is necessary for any class that wants to declare signals and slots.
Change your class definition to:
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread(QWidget * parent = 0);
signals:
void valueChanged(int value);
protected:
void run();
};
Take a look at the linked documentation, specifically the A Small Example section, for a complete explanation why this is needed.
You must not implement a signal in a .cpp file. MOC will do that and there must only be one implementation.
Just delete this part:
void MyThread::valueChanged(int value){
}
If your code works, that might be luck because the linker throws away the right implementation. You should not rely on that.

How can i call a my function in a qtslot?

I'm trying to call my function as a function slot in Qt,But i don't know how to go about it.
it seems the following approach is wrong :
Update:
According to an answer i updated my source code,but still something is apparently wrong with it.Trying to compile this snippet of code causes these errors:
C2515:' no appropriate default constructor is available.'
And
C2665: QObject::connect':none of the 3 overloads could convert all the
arguments.'
respectively in Visual studio 2010.
#include <QtGui/QApplication>
#include <QPushButton>
#include <QObject>
#include <QMessageBox>
class myclass;
int main(int argc,char *argv[])
{
QApplication a(argc,argv);
QPushButton btnshowmessage("show");
myclass *my=new myclass();
QObject::connect(&btnshowmessage,SIGNAL(clicked()),my,SLOT(warningmessage()));
btnshowmessage.show();
return a.exec();
}
//////////////////////////////////////////////////////////////////////////
class myclass: public QObject
{
Q_OBJECT
public:myclass(){}
public slots:
void warningmessage()
{
QMessageBox::warning(0,"Warning","Test Message!",QMessageBox::Ok);
}
};
You use signals and slots to connect one Object's signal to another Object's slot. Every signal or slot should be inside a class which must be also derived from QObject class and contain the Q_OBJECT macro.
So to make your code work, put the slot into some class of yours:
class MySlotClass:public QObject
{
Q_OBJECT
public slots:
void MyFunction()
{
QMessageBox::warning(0,"WarningTest","This is a waring text message",QMessageBox::Ok);
}
}
and connect like this:
MySlotClass m = new MySlotClass();
Qobject::connect(&btnShowaMessageBox,SIGNAL(clicked()), &m ,SLOT(MyFunction()));
Currently Qt does not allow connection of signals to functions that are not declared as slots on some QObject derivative. I believe Qt5 may offer this possibility, but connect will have different syntax to allow this.
basically your slot function must be in a QObject derived class and declared in a
public slots:
section.
Read the documentation on signals/slots.

QObject::connect() with enum parameters

I'm trying to use QObject::connect() with enum parameters. I got this message...
QObject::connect: Incompatible sender/receiver arguments
Calendar::calendarExceptionThrown(Calendar*,ExceptionType)
--> CalendarDBView::handleCalendarException(Calendar*,Calendar::ExceptionType)
The connect code is:
connect(cal, SIGNAL(calendarExceptionThrown(Calendar*,ExceptionType)),
this, SLOT(handleCalendarException(Calendar*,Calendar::ExceptionType)));
From what I've read, I figured I needed to add this to the class definition of Calendar:
Q_ENUMS(ExceptionType)
I added this to main.cpp:
qRegisterMetaType<Calendar::ExceptionType>("ExceptionType");
I'm still getting the warning message. This related thread didn't solve my issue. What am I doing wrong?
I believe if you simply change ExceptionType to Calendar::ExceptionType in the SIGNAL part of your connect, it will fix the problem. moc expects the strings for both the SIGNAL and SLOT to match.
You need to be very consistent with your signal and slots declarations, and with what you put in the SIGNAL and SLOT macros. moc isn't a full C++ compiler, it's just a relatively smart parser. But ultimately the signal and slot names are just strings.
For example, this "works":
#include <QtCore>
class A: public QObject {
Q_OBJECT
public:
enum MyEnum { e0 };
public:
A(QObject *parent=0): QObject(parent) {
connect(this, SIGNAL(fire(A::MyEnum)), this, SLOT(foo(A::MyEnum)));
}
public slots:
void foo(A::MyEnum) {
qDebug() << "In slot A::foo()";
}
signals:
void fire(A::MyEnum);
public:
void test() { emit fire(e0); }
};
It would also work if you remove all the A:: qualifiers for MyEnum. But it will fail if you leave some in but remove others.
So qualify all the names if these signals/slots need to be visible/accessible outside your class.