使用 QML 调用虚拟 C++ 函数
Calling virtual C++ functions with QML
我通过反复试验发现,如果我想从 QML 调用 C++ class 的虚函数,我必须在 class 层次结构中将它们全部声明为 Q_INVOKABLE 否则我会收到类型错误:
TypeError: Property 'X' of object X(0xXX) is not a function
我真的找不到任何与此相关的文档,有人可以指点我吗?如果我想注册一个派生的 class 类型并在 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();
}
您可以在 slots
部分声明您的函数,而不是使用 Q_INVOKABLE
:
class D : public B
{
Q_OBJECT
public slots:
virtual void foo();
}
查看此 link 了解详细说明:
http://developer.nokia.com/community/wiki/Calling_Qt_class_methods_from_QML
此外,不要忘记 classes 中的 Q_OBJECT 宏。
更新
Q_INVOKABLE
和 slots
关键字基本上做同样的事情。他们 "register" 您使用 Qt 元系统的功能。不同之处在于,正如您在评论中指出的那样,使用 Q_INVOKABLE
您可以 return 值。
就虚函数而言——它们透明地工作,就像它们在常规 C++ 中所做的那样。
在您的示例中,由于您将 Base*
指针传递给 QML,Qt 元系统需要通过添加 Q_OBJECT
宏来为 class "enabled"并且它的 foo
函数需要用 Q_INVOKABLE
.
注册
您不必在派生 class 中执行此操作。所以你的代码将是:
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();
}
以下是我根据上述 linked 文章修改的完整示例。它使用 Q_INVOKABLE
、slots
和 virtual
函数。
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() }
}
}
}
我通过反复试验发现,如果我想从 QML 调用 C++ class 的虚函数,我必须在 class 层次结构中将它们全部声明为 Q_INVOKABLE 否则我会收到类型错误:
TypeError: Property 'X' of object X(0xXX) is not a function
我真的找不到任何与此相关的文档,有人可以指点我吗?如果我想注册一个派生的 class 类型并在 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();
}
您可以在 slots
部分声明您的函数,而不是使用 Q_INVOKABLE
:
class D : public B
{
Q_OBJECT
public slots:
virtual void foo();
}
查看此 link 了解详细说明:
http://developer.nokia.com/community/wiki/Calling_Qt_class_methods_from_QML
此外,不要忘记 classes 中的 Q_OBJECT 宏。
更新
Q_INVOKABLE
和 slots
关键字基本上做同样的事情。他们 "register" 您使用 Qt 元系统的功能。不同之处在于,正如您在评论中指出的那样,使用 Q_INVOKABLE
您可以 return 值。
就虚函数而言——它们透明地工作,就像它们在常规 C++ 中所做的那样。
在您的示例中,由于您将 Base*
指针传递给 QML,Qt 元系统需要通过添加 Q_OBJECT
宏来为 class "enabled"并且它的 foo
函数需要用 Q_INVOKABLE
.
您不必在派生 class 中执行此操作。所以你的代码将是:
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();
}
以下是我根据上述 linked 文章修改的完整示例。它使用 Q_INVOKABLE
、slots
和 virtual
函数。
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() }
}
}
}