Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Connect a signal to the slot of a QMetaProperty

I'm not sure if something like this is possible, but I am attempting to dynamically generate a GUI based on properties that have been registered into Qt's property system. My assumption is that since I have registered a property using Q_PROPERTY() in this fashion:

Q_PROPERTY(propertyType propertyName WRITE setPropertyName READ getPropertyName NOTIFY propertynameSignal)

I should be able to retrieve the signature of the write or read functions for connection using the connect() function. The goal of this is to have a dialog connected to this object for modifying properties, but without having to hand write all of it. Is this possible, am going about it the wrong way, or should I just hardcode the dialog?

EDIT 1: I'll share some of the code (stripped down) I have which will hopefully make it more obvious as to what I'm trying to do:

Class declaration for particular class:

class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool someBool READ getBool WRITE setBool)
  public:
    bool someBool;

    bool getBool();
    void setBool(bool);
}

QDialog subclass that points to this Object:

void MyDialog::generateUI()
{
    const QMetaObject* metaObject = _MyObjectPtr->metaObject();
    for (int i = 0; i < metaObject->propertyCount(); ++i)
    {
        QMetaProperty property = metaObject->property(i);
        if (!strcmp(property.typeName(), "bool")
        {
            QCheckBox* checkBox = new QCheckBox(this);
            bool state = metaObject->property(property->name()).toBool();
            checkBox->setCheckState((state) ? Qt::Checked : Qt::Unchecked);

            // Add checkBox to QDialog layout widgets here
        }
    }
}

Forgive all the My* renamings, but I need to keep my actual class/member names private.

I can confirm that the object pointed to by MyObjectPtr is being read from properly, because the starting values reflect values that I expect when I change them around. The trouble is connecting this back to that object. I can change the values inside the checkbox GUI-side, but the values aren't being sent to the actual object pointed to by _MyObjectPtr.

like image 266
HD_Mouse Avatar asked Sep 13 '13 19:09

HD_Mouse


People also ask

How do I connect Qt signals and slots?

To connect the signal to the slot, we use QObject::connect(). There are several ways to connect signal and slots. The first is to use function pointers: connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed);

Can we connect one signal with multiple slots?

You can connect as many signals as you want to a single slot, and a signal can be connected to as many slots as you need. It is even possible to connect a signal directly to another signal. (This will emit the second signal immediately whenever the first is emitted.)

How does Qt signal slot work?

And there are 2 types of connection there. First one: slots are called at the time signal was emitted. And the second one: slots calls are placed in event loop queue. You can manually select the method in connect, but connecting signal and slots from different threads are always queued.


1 Answers

To retrieve the signature of QObject's methods (signals, slots, etc.) you can use meta object (QMetaObject) information. For example the following code (taken from Qt documentation) extracts all methods' signatures of the object:

const QMetaObject* metaObject = obj->metaObject();
QStringList methods;
for(int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i)
    methods << QString::fromLatin1(metaObject->method(i).signature());

To check whether the method is a slot or a signal, you can use QMetaMethod::methodType() function. For signature use QMetaMethod::signature() (refer to the example above).

QMetaObject reference

UPDATE After @HD_Mouse updated the question with additional information about his idea to created dynamic GUI based on an object's properties, I came up with the following code that could solve the problem:

Add member variable that will store the mapping between GUI component and corresponding property index:

class MyDialog : public QDialog
{
    [..]
private:
    /// Mapping between widget and the corresponding property index.
    QMap<QObject *, int> m_propertyMap;
};

When a GUI component created (check box), connect its changing signal to the special slot that will handle the corresponding property update.

void MyDialog::generateUI()
{
    const QMetaObject* metaObject = _MyObjectPtr->metaObject();
    for (int i = 0; i < metaObject->propertyCount(); ++i)
    {
        QMetaProperty property = metaObject->property(i);
        if (!strcmp(property.typeName(), "bool")
        {
            QCheckBox* checkBox = new QCheckBox(this);
            bool state = metaObject->property(property->name()).toBool();
            checkBox->setCheckState((state) ? Qt::Checked : Qt::Unchecked);

            // Add checkBox to QDialog layout widgets here

            // Store the property and widget mapping.
            connect(checkBox, SIGNAL(stateChanged(int)),
                    this, SLOT(onCheckBoxChanged(int)));
            m_propertyMap[checkBox] = i;
        }
    }
}

When a check box state changed, find the corresponding property (use mapping) and update it according to the check box state:

void MyDialog::onCheckBoxChanged(int state)
{
    QObject *checkBox = sender();
    QMetaObject* metaObject = _MyObjectPtr->metaObject();
    int propertyIndex = m_propertyMap.value(checkBox);
    QMetaProperty property = metaObject->property(i);

    // Update the property
    _MyObjectPtr->setProperty(property.name(), bool(state == Qt::Checked));
}
like image 99
vahancho Avatar answered Sep 30 '22 08:09

vahancho