前言
在上一篇文章中介绍了如何注册一个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
}
关于单例类型导入先介绍到这里。