将信号连接到QMetaProperty的插槽

5

我不确定是否可能做到这样的事情,但我尝试根据已注册到Qt属性系统中的属性动态生成GUI。我的假设是,由于我使用Q_PROPERTY()以这种方式注册了一个属性:

Q_PROPERTY(propertyType propertyName WRITE setPropertyName READ getPropertyName NOTIFY propertynameSignal)

我应该能够使用connect()函数检索连接的写或读函数的签名。这样做的目的是将对话框连接到此对象以修改属性,但不必手动编写所有内容。是否可能实现这一点,我正在错误的道路上,还是应该硬编码对话框?
编辑1: 我会分享部分代码(精简版),希望它更容易理解我要做什么:
特定类的类声明:
class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool someBool READ getBool WRITE setBool)
  public:
    bool someBool;

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

指向此对象的QDialog子类:

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
        }
    }
}

请原谅我所有的My*重命名,但是我需要保护我的实际类/成员名称。

我可以确认MyObjectPtr指向的对象已经被正确读取,因为起始值反映了我在更改它们时预期的值。问题是如何将其与该对象连接起来。我可以从复选框GUI端更改值,但这些值并没有发送到_MyObjectPtr指向的实际对象。


我其实在尝试做类似的事情,但是我采用了稍微不同的方法。我正在创建一个称为QPropertyModel的类,它创建一个一行模型,其中列映射到任意QObject中的属性。然后我使用QDataWidgetMapper将小部件附加到属性上。生成的GUI代码如下:model = new QPropertyModel(pObject); mapper = new QDataWidgetMapper(model); mapper->setModel(model); mapper->addMapping(aTextEdit, model.column("property name")); - Harvey
3个回答

5

为了获取QObject的方法(信号,槽等)的签名,您可以使用元对象(QMetaObject)信息。例如下面的代码(来自Qt文档)提取了对象的所有方法签名:

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

要检查方法是槽还是信号,可以使用QMetaMethod::methodType()函数。对于签名,请使用QMetaMethod::signature()(参见上面的示例)。 QMetaObject 参考 更新:在@HD_Mouse提供有关根据对象属性创建动态GUI的额外信息后,我想出了以下代码,可以解决问题:
添加成员变量,用于存储GUI组件和相应属性索引之间的映射:
class MyDialog : public QDialog
{
    [..]
private:
    /// Mapping between widget and the corresponding property index.
    QMap<QObject *, int> m_propertyMap;
};

当创建 GUI 组件(复选框)时,将其变化信号连接到特殊的插槽上,以处理相应的属性更新。
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;
        }
    }
}

当复选框状态改变时,找到相应的属性(使用映射),并根据复选框状态进行更新:
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));
}

谢谢您的回复。这段代码可以让我获取此QObject中所有方法的列表,我明白了,但我希望能够检索与QMetaProperty相关联的读取和写入函数。当您调用Q_PROPERTY的READ和WRITE部分时,它会被注册,并且在内部,qt在调用setProperty或getProperty时肯定会使用它们,那么为什么我无法访问这些函数以便用于connect?如果我使用上述代码中列出的方法,我需要以某种方式识别我想要的方法,但是我如何只使用QMetaProperty对象来实现呢? - HD_Mouse
@HD_Mouse,但是您不知道方法名称吗?读取和写入函数与QMetaProperty相关联,它们不是相同的方法吗?您不能在所有方法列表中找到它们吗? - vahancho
我在类外这么做,但思路是遍历从QObject::property()返回的QObject的QMetaProperty,然后通过QMetaProperty连接读取和写入槽。读取和写入函数的名称确实在列表中,但是QMetaProperty中没有将其与其槽相关联的方法。 - HD_Mouse
@HD_Mouse,嗯,但为什么QMetaProperty应该指向关联的槽,如果你可以直接更改它呢?也许你可以考虑这个方向? - vahancho
好的,这个对话框所指向的特定对象有许多属性与更多的属性即将到来。我们的目标是设计这个对话框,使得当我们添加属性时它可以自动扩展而不需要硬编码GUI。 - HD_Mouse
显示剩余3条评论

2
我不确定这对你是否有用,但我采用了另一种方法。我编写了GUI并将小部件绑定到基于属性的数据模型上。您可以使用该模型获取属性以动态生成GUI,然后将生成的小部件绑定到该模型。我编写了一个名为QPropertyModel的类,它为具有属性的任何QObject创建单行数据模型。属性与模型中的列相对应。我在gist中发布了这个类,并且这里是它的示例的链接:https://gist.github.com/sr105/7955969
QPropertyModel *model = new QPropertyModel(myQObjectPtr, this);
qDebug() << "model props:" << model->propertyNames();
QDataWidgetMapper *_mapper = model->mapper();
_mapper->addMapping(ui->lblBalance, model->columnForProperty("balance"), "text");
_mapper->addMapping(ui->lineEdit, model->columnForProperty("balance"));
_mapper->toFirst();

0

我也曾经考虑过基于QObject属性的自动UI生成,但目前我不朝这个方向工作,因为通常我需要特定的UI布局才能使其更加用户友好。

虽然我对自动UI生成没有什么可说的,但我一些关于自动UI绑定的想法。代码并不完美且有点硬编码,但主要思路是可见的。我认为像这样的东西也可以用于自动UI生成。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接