使用QML调用虚拟C ++函数

时间:2015-01-06 03:42:59

标签: c++ qml

我通过反复试验发现,如果我想从QML调用C ++类的虚函数,我必须在类层次结构中将它们全部声明为Q_INVOKABLE,否则我会收到类型错误:

  

TypeError:对象X(0xXX)的属性“X”不是函数

我无法找到任何与此相关的文档,有人可以指点我吗?如果我想注册派生类类型并在QML中实例化它会怎么样?是否存在与将虚拟函数声明为Q_INVOKABLE相关的性能问题?目前,我正在做的是(原型)。

class B : public QObject
{
public:
   virtual void foo();
}

class D : public B
{
public:
   Q_INVOKABLE virtual void foo();
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    Base * derived = new D;

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

   engine.rootContext()->setContextProperty("derived", d);

    return app.exec();
}

1 个答案:

答案 0 :(得分:2)

您可以在Q_INVOKABLE部分声明您的功能,而不是使用slots

class D : public B
{
    Q_OBJECT
public slots:
    virtual void foo();
}

请查看此链接以获取详细说明:

http://developer.nokia.com/community/wiki/Calling_Qt_class_methods_from_QML

另外,不要忘记课程中的Q_OBJECT宏。

<强>更新

Q_INVOKABLEslots关键字基本上都做同样的事情。他们&#34;注册&#34;你使用Qt元系统的功能。不同之处在于,正如您在评论中指出的那样,使用Q_INVOKABLE可以返回值。

在虚函数方面 - 它们透明地工作,就像在常规C ++中一样。

在您的示例中,由于您将Base*指针传递给QML,因此需要启用Qt元系统&#34;对于此类,添加Q_OBJECT宏及其foo函数需要在Q_INVOKABLE注册。

您不必在派生类中执行此操作。所以你的代码是:

class B : public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE virtual void foo();
}

class D : public B
{
public:
    virtual void foo();
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    Base * derived = new D;

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    engine.rootContext()->setContextProperty("derived", d);

    return app.exec();
}

以下是我从上述链接文章修改的完整示例。它使用Q_INVOKABLEslotsvirtual函数。

example.pro

QT += declarative

HEADERS += stringhelper.h
SOURCES += main.cpp

stringhelper.h

#ifndef STRINGHELPER_H
#define STRINGHELPER_H

#include <QObject>
#include <QString>

// outputs text, ignoring reverse
class StringHelper_Base : public QObject
{
    Q_OBJECT
public:
    StringHelper_Base(QObject *parent = 0): QObject(parent) { }

    Q_INVOKABLE virtual QString echo(const QString &text) const {
        return text;
    }

public slots:
    virtual void toggleEcho(bool reverse) { (void)reverse; }
};

// outputs text with reversing
class StringHelper : public StringHelper_Base
{
public:
    StringHelper(QObject *parent = 0): StringHelper_Base(parent), reverse(false) { }

    QString echo(const QString &text) const {
        if(reverse == false) { return text; }

        QString reversed;
        for(QString::const_iterator it = text.begin(); it != text.end(); it++) {
            reversed.push_front(*it);
        }

        return reversed;
    }

    void toggleEcho(bool reverse) { this->reverse = reverse; }

protected:
    bool reverse;
};

#endif // STRINGHELPER_H

的main.cpp

#include <QApplication>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include "stringhelper.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //StringHelper_Base* stringHelper = new StringHelper_Base();
    StringHelper_Base* stringHelper = new StringHelper();

    QDeclarativeView view;
    view.setResizeMode(QDeclarativeView::SizeRootObjectToView);
    view.rootContext()->setContextProperty("foo", stringHelper);
    view.setSource(QUrl("./ui.qml"));

    view.setGeometry(100, 100, 800, 480);
    view.show();

    return a.exec();
}

ui.qml

import QtQuick 1.1

Rectangle {
    id: rect

    property string text: "Using Qt class to echo this"

    function updateUI() {
        foo.toggleEcho(button.pressed); // calling StringHelper::toggleEcho
        text.text = foo.echo(rect.text) // calling StringHelper::echo
    }

    anchors.fill: parent
    color: "black"

    Component.onCompleted: updateUI()

    Text {
        id: text
        anchors.centerIn: parent
        color: "white"
    }

    Rectangle {
        id: button

        property bool pressed: false

        width: 100; height: 40
        anchors.right: parent.right; anchors.rightMargin: 20
        anchors.bottom: parent.bottom; anchors.bottomMargin: 20
        radius: 6
        color: pressed ? "gray" : "white"

        Text {
            anchors.centerIn: parent
            text: "Reverse"
        }

        MouseArea {
            anchors.fill: parent
            onClicked: { button.pressed = !button.pressed; updateUI() }
        }
    }
}