Qt 从C ++定义QML类型(二)

前言

上一篇文章中介绍了如何注册一个C++的可实例化的对象类型供 QML 中使用,那么今天继续之前的文章介绍。

正文

注册不可实例化类型

有时候QObject派生类可能需要在QML类型系统中注册,但不能作为可实例化类型。例如,如果C ++类是这种情况:

1.是一个不应该实例化的接口类型
2.是不需要暴露给QML的基类类型
3.声明了一些应该可以从QML访问的枚举,但除此之外不应该是可实例化的
4.通过单例实例提供给QML的类型,不应该从QML实例化

在Qt的QML模块提供用于登记非实例类型的几种方法:

  • qmlRegisterType()(不带参数)注册一个C ++类型,该类型不可实例化,不能从QML引用。这使得引擎可以强制从QML实例化的任何继承类型。
  • qmlRegisterInterface()注册具有特定QML类型名称的Qt接口类型。该类型不是从QML实例化的,但可以通过其类型名称引用。
  • qmlRegisterUncreatableType()注册一个不可实例化的命名C ++类型,但可以识别为QML类型系统的一个类型。如果类型的枚举或附加属性可以从QML访问,但是类型本身不应该是可实例化的,那么这个方法可以用到。
  • qmlRegisterSingletonType()注册一个可以从QML导入的单例类型。

注意,使用QML类型系统注册的所有C ++类型都必须是QObject派生的,即使是不可实例化类。

用单例类型注册单例对象

单例类型让属性、信号和方法能够暴露在名称空间中,而不需要客户端手动实例化对象实例。特别是QObject单例类型是提供功能或全局属性值的一种高效方便的方法。

请注意,单例类型没有关联的QQmlContext,因为它们在引擎中的所有上下文之间共享。QObject单例类型实例由QQmlEngine构建并拥有,并且在引擎销毁时将被销毁。

一个QObject单例类型可以以类似于任何其他QObject或实例化类型的方式进行交互,除了只存在一个(引擎构造和拥有的)实例,并且它必须通过类型名称而不是id引用。可以绑定QObject单例类型的Q_PROPERTY ,并且可以在信号处理程序表达式中使用QObject模块API的Q_INVOKABLE函数。这使得singleton类型成为实现样式或主题的理想方式,并且它们也可以用来代替存储全局状态或提供全局功能。

一旦注册,一个QObject单例类型可以被导入和使用,就像任何其他暴露给QML的QObject实例一样。
如下示例:
假如这里的Theme已经注册到MyThemeModule 1.0系统空间。并且定义了 color 属性,那么在 QML 中可以直接进行引用。

import MyThemeModule 1.0 as Theme
Rectangle {
    color: Theme.color // binding.
}

注意: QML中注册类型的枚举值应该以大写字母开头。

OK,接下来我们看看qmlRegisterSingletonType 注册一个可以从 QML 导入的单例对象。

qmlRegisterSingletonType函数说明

qmlRegisterSingletonType一共有三个重构函数,先来看看函数的声明。

int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue(* ) ( QQmlEngine *, QJSEngine * ) callback)
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(* ) ( QQmlEngine *, QJSEngine * ) callback)
int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
1.int qmlRegisterSingletonType(const char uri, int versionMajor, int versionMinor, const char *typeName, QJSValue( ) ( QQmlEngine , QJSEngine ) callback)

此函数可用于在特定的uri和typeName中注册singleton类型提供者回调,其版本在versionMajor和versionMinor中指定。
单例类型可以是QObject或QJSValue。这个函数应该用来注册一个单例类型的提供者函数,它返回一个QJSValue作为单例类型。

注: 如果更改,QJSValue单例类型属性不会触发绑定。

来看看示例:

//首先,定义单例类型提供者回调函数
static QJSValue example_qjsvalue_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)

    static int seedValue = 5;
    QJSValue example = scriptEngine->newObject();
    example.setProperty("someProperty", seedValue++);
    qDebug() << __FUNCTION__ << seedValue;
    return example;
}


int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    //第二,通过在初始化函数中调用该函数,用QML注册单例类型提供程序
    qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", example_qjsvalue_singletontype_provider);

    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

这里在注册的时候也可以写成 Lambda 函数:如下:

qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue {
    Q_UNUSED(engine)

    static int seedValue = 5;
    QJSValue example = scriptEngine->newObject();
    example.setProperty("someProperty", seedValue++);
    return example;
});

在 QML 中调用:

import QtQuick 2.0
import Qt.example.qjsvalueApi 1.0 as ExampleApi
Item {
    id: root
    property int someValue: ExampleApi.MyApi.someProperty

    Component.onCompleted: {
        console.log("someValue = ",someValue)
    }
}

程序输出:

example_qjsvalue_singletontype_provider 6
qml: someValue =  5

注意,这里的someValue再次调用还是保持之前的值,也就是说不会重复进入到回调函数中。经过多次测试均是如此。

个人觉得上面这种用法在实际的场景中应该使用得不多,那么接下来看看第二个。

2.int qmlRegisterSingletonType(const char uri, int versionMajor, int versionMinor, const char *typeName, QObject (* ) ( QQmlEngine , QJSEngine ) callback)

和上面那个注册函数不同的是,这里是返回一个 QObject 对象类型的回调。

一个QObject单例类型可以通过它所注册的类型名称被引用,并且这个类型名称可以用作Connections类型中的目标,或者用作任何其他类型的id。一个例外是QObject单例类型属性不能被别名(因为单例类型名称不能识别与其他任何项目相同的组件中的对象)。

注:一个QObject的从单类型提供者返回单类型实例是由QML引擎所有,除非对象有明确的QQmlEngine :: CppOwnership标志设置。

来看看示例:

//定义一个继承于 QObject 的功能类

class Student : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY sigNameChanged)

public:
    explicit Student(QObject *parent = nullptr):
        QObject(parent)
    {
    }
    ~Student(){}


    void setName(const QString & name){
        if(name != m_name){
            m_name = name;
            emit sigNameChanged(m_name);
        }
    }
    QString getName() const {return m_name;}

    Q_INVOKABLE QString doSomething(){
        qDebug(__FUNCTION__);
        setName("xiaoming");
        return m_name;
    }

signals:
    void sigNameChanged(QString name);


private:
    QString m_name = "zhangsan";

};

//第二,定义单例类型提供者函数(回调)
static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)
    qDebug()<<__FUNCTION__;
    Student *student = new Student();
    return student;
}

调用函数注册:

qmlRegisterSingletonType<Student>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider);

在 QML 中使用:

import QtQuick 2.0
import Qt.example.qobjectSingleton 1.0

Item {
    id: root
    property string someValue: MyApi.name

    Component.onCompleted: {
        console.log("someValue = ",someValue)
        someValue = MyApi.doSomething()
        console.log("someValue = ",someValue)
    }
}

运行代码,输出如下:

example_qobject_singletontype_provider
qml: someValue =  zhangsan
doSomething
qml: someValue =  xiaoming

注意,由于singleton类型没有关联的QQmlContext对象,因此在注册为单例类型实现的QObject衍生类型的函数内,QML上下文和引擎信息不可用。

3.int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)

该函数可用于在从uri导入的库中注册名为qmlName的单例类型,其版本号由versionMajor和versionMinor组成。该类型由位于url的QML文件定义。该网址必须是绝对网址。

另外,该类型的QML文件在其导入语句中必须包含编译指令Singleton语句。

单例类型可以通过它所注册的类型名称来引用,并且此类型名称可以用作Connections类型中的目标,或者用作任何其他类型的id。单例类型属性不能被别名。
看看示例:

//首先,定义提供该功能的QML单例类型。
pragma Singleton
import QtQuick 2.0
Item {
    property int testProp1: 125
}
//其次,通过在初始化函数中调用此函数来注册QML单例类型。
qmlRegisterSingletonType(QUrl("file:///absolute/path/SingletonType.qml"), "Qt.example.qobjectSingleton", 1, 0, "RegisteredSingleton");

为了在QML中使用已注册的单例类型,必须导入单例类型。

import QtQuick 2.0
import Qt.example.qobjectSingleton 1.0
Item {
    id: root
    property int someValue: RegisteredSingleton.testProp1
}

关于单例类型导入先介绍到这里。

猜你喜欢

转载自blog.csdn.net/luoyayun361/article/details/80544906
今日推荐