Creating a dynamic slot in Qt - qt

I am trying to create slots dynamically and connect them. I am able to dynamically create pushButtons and connect them with existing slots. But what if I have a class with some member functions and I want to use these functions as slots.
From a general perspective, I want to create a template for generating slots given a function pointer. This allows us to create slots without changing the existing code and don't have to recompile using MOC.
If this doesn't make sense let me know. Thank you.
-CV

It does make a lot of sense. I assume QSignalMapper is not what you want. If your functions don't have arguments, maybe something like this is enough:
class SlotForwarder : public QObject
{
Q_OBJECT
public:
typedef void (*Function)(); // or whatever. I never get this syntax right...
SlotForwarder(Function function, QObject* parent = 0)
: QObject(parent)
, m_fptr(function)
{}
public slots:
void forward()
{
m_fptr();
}
private:
Function m_fptr;
};
Create one for each function you want to encapsulate and connect to the forward as usual.
Now, if they do have arguments, maybe this Qt Quarterly article might be of assistance.
Dynamic Signals and Slots by Eskil A. Blomfeldt
The technique involves reimplementing the qt_metacall method yourself. The method has this signature:
int QObject::qt_metacall(QMetaObject::Call call, int id, void **arguments)
Call is the kind of metacall: slot, signal, property read or write, etc. Every slot has an id. The parameters are packed (by value or as pointers) inside the arguments array. Reading the code that the moc generates is a good way to understand how it all works.
Data about raw function signatures is available only during compile time, but slots are resolved at runtime. Because of that mismatch, you will need to wrap the functions into a template adapter type that presents a constant interface to your implementation of qt_metacall and converts the arguments array into something the function can understand (cf. Python unpack operator). Boost.Signals does that kind of template hackery.

A continuation of andref's code so as to use any member function as a slot
class SlotForwarder : public QObject
{
Q_OBJECT
public:
typedef void (*Function)();
SlotForwarder(Function function, QObject* parent = 0)
: QObject(parent)
, m_fptr(function)
{}
public slots:
void forward()
{
m_fptr();
}
private:
Function m_fptr;
};
int main(){
QApplication a(argc, argv);
MyClass myClassObject; //contains a function called MemberFunc
//create a slotforwarder object so
SlotForwarder *memberFuncSlot = new SlotForwarder (std::tr1::bind(&MyClass::MemberFunc, &myClassObject));
QObject::connect(ui.button,SIGNAL(clicked()),memberFuncSlot,SLOT(forward());
}

Related

Qt: How to know if slot was called by signal-slot mechanism by calling the signal like a function

Inside a slot I check the QObject::sender() but now I want to call this slot directly like a function.
Can I know how this slot was called inside of the slot? Either by signal-slot mechanism or simply by calling the slot like a function?
You can check the sender() in both cases. In case of the slot being called via a signal/slot mechanism the sender will return a pointer while when called as a method it will return null pointer.
Simple example:
class Test : public QObject
{
Q_OBJECT
signals:
void signal();
public slots:
void slot() { qDebug() << sender(); }
};
And the use:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Test test;
Test test2;
QObject::connect(&test, &Test::signal, &test2, &Test::slot);
test.signal(); //slot is caled by signal
test2.slot(); //slot is called as method directly
return a.exec();
}
And the output:
Test(0xa8e8aff5b0)
QObject(0x0)
Add a default parameter to your slot:
public slots:
void slot(bool calledBySignal = true);
Set the parameter to false when calling the slot directly:
void MyClass::method()
{
[...]
slot(false);
[...]
}
Leave the connect() calls as they are; don't add a bool parameter to the signals, nor change SLOT(slot()) to SLOT(slot(bool)).
Disadvantage: It's easy to forget setting the parameter.
If your slot doesn't need to be public, because connect()ing to it is handled from inside the class only, you should make it private and add a wrapper method that's to be called instead, but still you'll need some discipline when calling it from inside the class. Johannes' suggestion would solve these issues.
Another idea to distinguish between direct function call vs. Signal/Slot-invocation:
Use the method int QObject::senderSignalIndex() const and check for -1
if(QObject::senderSignalIndex() == -1){
//called directly as a function
} else {
// invoked via Signal/SLot mechanism
}
Returns the meta-method index of the signal that called the currently executing slot, which is a member of the class returned by sender(). If called outside of a slot activated by a signal, -1 is returned.
see Qt 4.8 documentation
This looks like a clean way to distinguish and there is no chance of getting into trouble with a null-pointer in contrast to using QObject * QObject::sender() const.
Regards,
Felix

Error while inheriting QObject

I've got a class applicationManager that ought to emit signal when there is an info message or an error occurs, while I'm suppose to log that messages consuming it by QXmppLogger class object. There is a QXmppLoggable class that got methods like info() and warning() that emits a signal logmessage() of internal QXmppLogger. So, in order to emit a signal I've inherited QXmppLogable class which inherits QObject itself, hence to be able to use info() and warning() and connected emitted SIGNAL(logmessage()) by info() and warning() methods, to SLOT (log()) of QXmppLogger object. Here is the code snippet:
header "imApplicationManager.h"
class applicationManagement: public QXmppLoggable
{
//Q_OBJECT
public:
bool createDataBaseConnection();
applicationManagement();
~applicationManagement();
void setLogger(QXmppLogger *logger);
private:
QXmppLogger *logger;
};
and related "imApplicationManager.cpp"
applicationManagement::applicationManagement()
{
QObject::connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
logger, SLOT(log(QXmppLogger::MessageType,QString)));
}
applicationManagement::~applicationManagement()
{
// db..closing
}
bool applicationManagement::createDataBaseConnection(){
//...database conncetion...
if (!dataBase.open()){
warning(QString("Cannot open database %1").arg(dataBase.lastError().text()));
qDebug()<< "Cannot open database " << dataBase.lastError();
return false;
}
info(QString("Database succeeded!"));
return true;
}
void applicationManagement::setLogger(QXmppLogger *logger)
{
if(this->logger != logger)
this->logger = logger;
}
in the main.cpp
#include "imApplicationManager"
#incLude "QXmppLogger"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
applicationManagement aa;
QXmppLogger log;
aa.setLogger(&log);
return a.exec();
}
Compilation informs of no error but when luanched there is a Sigmentation fault.
How can I fix that?
OK, decided to post a full answer:
First of all, you do NEED the Q_OBJECT macro. The errors you get from it will likely disappear when you Clean All, Run qmake and Rebuild All from the Build menu of QtCreator. Qt relies on a LOT of generated boilerplate code you never have to type, and without Q_OBJECTthat code will not be generated. That is the reason you sometimes need to clean up after you made changes, run qmake (which generates the code) and rebuild with the now up-to-date generated code.
Second - in the constructor of applicationManagement you connect to an uninitialized pointer logger. This is probably why you get segmentation fault. You can use two approaches to fix that:
Pass the pointer to the logger in the constructor of applicationManagement, this way you have something to connect to in the constructor. This way you cannot instantiate an applicationManagement before a logger, unless you use logger(new QXmppLogger) in the constructor initialization list.
Move the connection from the applicationManagement constructor to the setLogger()method. Do not forget to disconnect a previous connection if any.
It has been mentioned that the Q_OBJECT macro is needed, but I think a little explanation would be useful too, as understanding why it's needed helps to remember to use it.
Qt adds to C++, amongst other features, the functionality of signals and slots and this is done with the Meta-Object Compiler (or moc for short). When compiling Qt code, the moc parses the header files and creates source code for you, which can be seen in the moc files. Occasionally these get out of sync and cleaning these files will fix this issue.
If you look more closely at the Q_OBJECT macro, it is defined as this: -
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
QT_TR_FUNCTIONS \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
struct QPrivateSignal {};
As can be seen here, the Q_OBJECT macro adds function definitions to the class and its the implementation of these in the QObject parent class that provides the added C++ features such as signals / slots, properties, RTTI etc. When calling signals and slots, internally Qt uses the qt_metacall function. There's an interesting explanation of the implementation of these here. Therefore without the Q_OBJECT macro in a class derived from QObject, signals and slots won't work.
You use logger but it is not initialized before:
QObject::connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
logger, SLOT(log(QXmppLogger::MessageType,QString)));
try to rewrite your constructor like that:
applicationManagement::applicationManagement(QXmppLogger *logger_):
logger(logger_)
{
QObject::connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
logger, SLOT(log(QXmppLogger::MessageType,QString)));
}
or connect that signal after initializing logger.

Unable to connect signal to slot in another class

I have 2 classes. Class A and Class B. I am emitting a signal from class A which I want the B to recieve.
I am doing it following way
In Listener File
Header File:
Class Listener:public DDSDataReaderListener
{
//Some code
public:
A m_objectSendData;
}
Implementation File:
void Listener::ondataavailable(DDSDataReader *reader)
{
m_objSendData.GetDDSData();
}
In Class A
Header File:
Class A:public QObject
{
Q_OBJECT
public:
void GetDDSData();
signals:
void Signal_Data();
}
.cpp File
A::A(QWidget *parent):QObject(parent)
{
}
void A::GetDDSData()
{
emit Signal_Data();
}
In Class B
Header File:
Class B:public QObject
{
Q_Object
public:
A objGetData;
public slots:
void getData();
}
Implementation File:
B::B(QWidget *parent):QObject(parent)
{
//Some part of code
connect(&objGetData,SIGNAL(Signal_Data()),this,SLOT(getData());
}
void B::getData()
{
//Watever is to be updated
}
I tried debugging. It is going till emit part correctly. However it is not reaching the slot.
Can someone please help me with this.
Thank You.
Without full code, it's quite difficult to identify the exact issue of the problem, so I'll outline a few important points to check.
To ensure you can use the signal and slots mechanism, you should ensure that your class is derived, from QObject or a class already derived from QObject in its hierarchy and your class must contain the Q_OBJECT macro, for example: -
class A : public QObject // derived from QObject
{
Q_OBJECT // your class must have this macro for signals and slots
public:
A();
};
Omitting the macro is probably the most common of mistakes.
To specify a slot, you add it to either the public or private slot section of your class: -
class B : public QObject // derived from QObject
{
Q_OBJECT // your class must have this macro for signals and slots
public:
B();
public slots:
void SlotB(); // slot declared public
private slots:
void SlotBPrivate(); // slot declared private.
};
Once a signal is declared in a class, a slot to receive the signal should match the arguments passed in and when you connect a signal to a slot, you must not add the function argument names.
Therefore: -
connect(&objectA, SIGNAL(SignalA(int in), this, SIGNAL(SlotA(int param)); //will fail due to the argument names
It should be: -
connect(&objectA, SIGNAL(SignalA(int), this, SIGNAL(SlotA(int));
Finally, if you're using Qt 5, you can use the new connection call, which doesn't require you to specify any argument, but instead takes the addresses of slot and signal functions.
connect(&objectA, &A::SignalA, this, &B::SlotA));
Since it references the address of a function, in actuality, the functions don't need to be classed as a slot and will still be called.
Hope that helps.
Actually I believe an answer is given in one of the comments.
One more thing, you didn't show enough code but I suspecting that you program leaves scope of objectA variable and your emitting object is just destroyed before it can emit any signal (objectA is local variable created on stack not on heap). – Marek R 1 hour ago
you allocate your Object on stack, so it gets destroyed as soon as it gets out of scope, together with destroy it gets disconnected from all signals/slots it has connections to.
So that's why you don't see any errors/warning messages because code itself is completely legit. You should new() your object to get it allocated in heap.

Naming attributes and methods in Qt

I'd like to have this:
class Test {
private:
int a;
public:
int a();
int setA(int val);
}
It seems to me that the Qt libraray does this all the time.
But I get a "declaration blabla" compiler error. Why is that?
Do I really have to name the method getA()?
I've even tried with Q_PROPERTY:
class Test : public QObject {
Q_OBJECT
Q_PROPERTY(int a READ a WRITE setA)
public:
int a(){return a}
int setA(int val){a=val;}
}
This also does not work.
In Qt itself, the data members are usually in a Private class (the Pimpl idiom), so it's not an issue there.
If you don't use Pimpl (which is a bit tedious and only really necessary if you have to guarantee binary compatibility, or have a very large project where reducing includes has a significant enough effect), the most common way is to prepend the member with a prefix, e.g. m_:
Q_PROPERTY(int a READ a WRITE setA)
public:
void setA( int a ) { m_a = a; }
int a() const { return m_a; }
private:
int m_a;
Another advantage is also that member and local variables are always easy to tell from each other.
Alternatives:
Access the variable via this->a (more tedious than m_a)
Use getA() (makes ugly API, IMHO, but of course depends on your API style - if everything else uses get*, one should just follow suit)

QtScript plus enums

I am adding QScript to my Qt application. I have already added metadata and use some of the metadata functions to interrogate through C++ code. That works fine - I can navigate the object heirarchy and print out values (including enums).
But, I can't seen to get enums working in Qt script.
I have my class...
class HalPin : public QObject
{
Q_OBJECT
public:
enum EHalPinType
{
Bit = HAL_BIT,
Float = HAL_FLOAT,
S32 = HAL_S32,
U32 = HAL_U32
};
enum EHalPinDirection
{
In = HAL_IN,
Out = HAL_OUT,
IO = HAL_IO
};
Q_ENUMS(EHalPinType)
Q_ENUMS(EHalPinDirection)
public:
explicit HalPin(QObject *parent = 0);
signals:
public slots:
};
Q_DECLARE_METATYPE(HalPin::EHalPinType)
Q_DECLARE_METATYPE(HalPin::EHalPinDirection)
Q_DECLARE_METATYPE(HalPin*)
I have another class that has a method that takes the enums as arguments...
class EmcHal : public QObject
{
Q_OBJECT
public:
explicit EmcHal(QString moduleName, QObject *parent = 0);
signals:
public slots:
QObject *createHalPin( HalPin::EHalPinType, HalPin::EHalPinDirection, QString name );
};
This class is exposed in another class - sorry I should have simplified the example. If I write the following jscript code,
var nextPagePin1 = Emc.hal.createHalPin();
I get an error I expect...
SyntaxError: too few arguments in call to createHalPin(); candidates are createHalPin(HalPin::EHalPinType,HalPin::EHalPinDirection,QString)
So, it appears that the enum types are known to qtscript.
What I am struggling to do is to set the enum arguments from jscript. I've tried many combinations...
Bit
EHalPinType.Bit
HalPin.EHalPinType.Bit
and many more.
If I try to use integers, I get...
TypeError: cannot call createHalPin(): argument 1 has unknown type `HalPin::EHalPinType' (register the type with qScriptRegisterMetaType())
which seems to imply jscript doesn't know about my enums.
Any suggestions?
Do I need to use qRegisterMetaType or qScriptRegisterMetaType to access my enums? The documentation doesn't suggest I need to do this. Do I need to implement the converter functions for the qScriptRegisterMetaType method.
Or is my syntax just wrong for the jscript?
Does someone have a working example?
Thanks,
Frank
To answer my own question...
Well, not so much an answer to why, but a "meh, this works" example...
As I mentioned above, I wasn't able to get the enums working in both the metadata and jscript at the same time using the qt macros. Even though the enum appeared in qscript (I checked in the browser of the script debugger), it didn't evaluate to the correct integer.
I had to add a QMetaObject for the enum. That gave me the enum items, and correct integer values.
But that still gave me the unknown type error, so I needed to use qScriptRegisterMetaType() to register conversion functions for the types.
This is the class I use for 1 enum. It is as minimal as I can make it. I should be able to use macros to shrink it down a bit more, but there are limitations on what can be macroised, because of the qt moc requirements.
#include <QObject>
#include <QMetaType>
#include <QScriptEngine>
#include "hal.h"
class CEHalPinType : public QObject
{
Q_OBJECT
public:
explicit CEHalPinType(QObject *parent = 0) : QObject(parent) {}
explicit CEHalPinType(const CEHalPinType &other) : QObject(other.parent()) {}
virtual ~CEHalPinType() {}
enum EHalPinType
{
Bit = HAL_BIT,
Float = HAL_FLOAT,
S32 = HAL_S32,
U32 = HAL_U32
};
Q_ENUMS( EHalPinType )
private:
static QScriptValue toScriptValue(QScriptEngine *engine, const EHalPinType &s)
{
return engine->newVariant((int)s);
}
static void fromScriptValue(const QScriptValue &obj, EHalPinType &s)
{
s = (EHalPinType)obj.toInt32();
}
static QScriptValue qscriptConstructor( QScriptContext *context, QScriptEngine *engine )
{
return engine->newQObject( new CEHalPinType(context->argument(0).toQObject()), QScriptEngine::ScriptOwnership);
}
public:
static void Init( const char *name, QScriptEngine *engine )
{
qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue);
QScriptValue metaObject = engine->newQMetaObject( &staticMetaObject, engine->newFunction(qscriptConstructor) );
engine->globalObject().setProperty( name, metaObject );
}
};
Q_DECLARE_METATYPE(CEHalPinType::EHalPinType)
And my jscript looks like...
var nextPagePin = Emc.hal.createHalPin(EHalPinType.Bit,EHalPinDirection.In,"nexis.NextPage");
Oops. I jumped the gun on this one. Although the scripting worked, I broke the ability to convert enums to strings using the qmetaobject data.
And there doesn't seem to be an automatic way of doing it.
The problem is, I moved the enums out of the class where the properties that used the enums were defined. Although the Q_ENUMS and Q_PROPERTY compile, if I use the QMetaProperty to to read an enum, it doesn't work. The QVariant that is returned shows the correct data type, "CEHalPinType::EHalPinType", but it fails the isEnum() test and canConvert(QVariant::String) fails too. This is because when the qmetaobject code goes searching for the enum type, it only looks in the current class and its derived classes. It doesn't search other classes. Which is why it worked when the enum was a member of the class which also had the properties.
My work around, as suggested elsewhere, was to create my own QMap of known enums, storing the string name to qmetaobject mapping. I used a templated base class and used T::staticMetaObject to get the meta object.

Resources