Qt:使用指针和前向声明的Q_PROPERTY用于QtScript访问

5

问题

我正在使用Q_OBJECT和Q_PROPERTY制作一个项目,以便从脚本中访问一些对象。我有两个问题:

  1. 如何使使用前置声明的类可脚本化
  2. 如何返回指针属性

解释

1. 为什么需要前置声明?

类B获取了A的前置声明,因为由于模板,A需要在头文件中包含完整的B类型。B只需要不完整类型(A*),因此前置声明是有效的。

2. 为什么要返回指针?

我们不能返回副本,因为我们需要访问脚本中的实际对象。我们不能返回引用,因为Qt不允许槽函数返回引用——它们的类型将被忽略,它们只会返回void*。

代码

可以在pastebin上下载完整代码或者下载ZIP压缩包或者最小示例的ZIP压缩包进行测试/玩耍:我需要分开文件来处理前置声明和MOC。我添加了一个Makefile以进行测试。Make依赖项:g++,moc,Qt。

重要部分

class A; // forward declaration necessary, see explanation above

class B : public QObject {
    Q_OBJECT
    Q_PROPERTY(A a READ GetA) // <-- ERROR HERE
    // ...

public slots:
    A* GetA() { 
        return mA; 
    }

private:
    A* mA;
    // ...
}

脚本中的错误行:
print(bObj.GetA().GetName());

编译错误

当我注释掉上面的 Q_PROPERTY 时,这个错误就消失了。

tmp/B.moc.hpp:95:51: error: invalid use of incomplete type ‘struct A’
tmp/../B.hpp:10:7: error: forward declaration of ‘struct A’

脚本异常

当在脚本中不包含 Q_PROPERTY 并将 GetA() 方法作为槽从脚本中调用时,会出现以下异常:

Line 7: "TypeError: cannot call GetA(): unknown return type `A*' 
        (register the type with qScriptRegisterMetaType())"

当使用qRegisterMetaType<A*>("A*")注册A*时,这将变成:

Line 7: "TypeError: Result of expression 'bObj.GetA().GetName' 
        [undefined] is not a function." 

这表明GetA()函数并不返回A对象,或者以某种方式返回指针,但脚本无法解引用它。 GetA()实际上返回一个QVariant(A*),是否可以以某种方式使用它?

问题:

  1. 我能否从不完整的类型中创建Q_PROPERTY,或者如何避免前向声明?
  2. 我能否在槽函数中返回引用(也许有一些技巧,比如包装指针并“覆盖”脚本operator.的类,如果存在类似的东西)
  3. 我能否以某种方式将QVariant(A*)解引用为QtScript中的A类型?
1个回答

6
  1. Your property type is A, not A*, that's why you get very reasonable error.
  2. You should use QScriptValue. Look this code. It works Ok:

    class A; // forward declaration necessary, see explanation above
    
    class B : public QObject
    {
        Q_OBJECT
    
        // Using QScriptValue, made from A instead of A to allow script work correctly with an object
        Q_PROPERTY(QScriptValue a READ GetA) 
    
    public slots:
        QScriptValue GetA() {
            //making QScriptValue from A. Type conversion in C style only due to limitation of incomplete type
            //In real app it's beter to put defenition of this slot after A's defenition
            return static_cast<QScriptEngine*>(parent())->newQObject((QObject*)mA);
        }
    
    private:
        A* mA;
        // ...
    public:
        //I decided my object will be a child of scriptEngine, but you can take a pointer to it in some other way
        B(QScriptEngine * parent);
    };
    
    class A: public QObject
    {
        Q_OBJECT
    public slots:
        QString GetName() const {return "a name";}
    public:
        A(QScriptEngine*parent):QObject(parent){}
    
    };
    
    B::B(QScriptEngine *parent):QObject(parent), mA(new A(parent)){}
    

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