目录
动态创建qml对象
Qt.createComponent
var component = Qt.createComponent()⇒ component.createObject⇒QmlContext是createComponent方法被调用的qml对象的context
qml对象的文件定义:
//DynamicQml.qml
import QtQuick 2.0
Rectangle {
anchors.fill: parent
Image {
id: image
source: "qrc:/bg.png"
}
}
主程序的qml中动态创建这个qml对象:
var component = Qt.createComponent("qrc:/DynamicQml.qml")
//加载本地的qml文件,所以没有检查component加载状态。
var obj = component.createObject()
createObject()可以输入两个参数,第一个参数指定了生成的对象依附于哪个父亲对象,如果父亲对象是界面对象,那么动态创建的对象可以被描绘。当然我们在创建是也可以不指定,在对象创建成功后通过设置对象的parent来指定。第二个参数指定初始化哪些属性,在对象创建后设置属性也可以,但是通过参数的方式设置有更好的执行效率。下面简单演示下如何传递这两个参数。
//DynamicQml.qml
import QtQuick 2.0
Rectangle {
anchors.fill: parent
property string name: "default name"
Image {
id: image
source: "qrc:/bg.png"
}
}
//main.qml
var component = Qt.createComponent("qrc:/DynamicQml.qml")
//通过参数的方式设置parent object 和属性值
//加载本地的qml文件,所以没有检查component加载状态。
var obj = component.createObject(rootItem,{name:"init name"})
//通过简单的赋值方式
obj.parent = rootItem
obj.name = "init name"
Qt.createQmlObject
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}',
parentItem,
"dynamicSnippet1");
createQmlObject()方法可以通过加载qml string来创建qml对象,与createObject()不同,它在调用时必须指定parent item。
Component qml type
//main.qml
Component{
id:dynamicComponent
Rectangle {
anchors.fill: parent
property string name: "default name"
Image {
id: image
source: "qrc:/bg.png"
}
Text {
id: te
text: name
anchors.centerIn: parent
}
}
}
function(){
var obj = dynamicComponent.createObject(container,{name:"init name"})
}
这里通过在main.qml 文件中定义Component的方式声明了组建,然后通过Component id直接调用createObject()。这里调用createObject时参数的规则与第一种动态创建的规则是一样的。
销毁动态创建的对象
动态创建的对象需要在不使用的时候销毁掉,否则会造成内存泄漏的问题。我们可以通过调用obj.destroy()方法来释放对象,这个方法不是同步方法,方法被调用后再后面的某一个时间会进行真正的释放,这个方法被调用时,我们也可以指定延时释放的时间。由于释放操作是异步的,所以在对象内部也可以调用自己的destroy方法。destroy调用的时机包括如下:
- 自己销毁自己=》通过动态qml的根Item调用destroy进行销毁,可以设置delay时间。
- 在动态创建对象的地方销毁=》var obj = component.createObject obj.destroy()
这里有几个问题需要注意:
- 只有动态创建的qml对象才可以使用destroy方法销毁。
- destroy销毁后,动态对象的context就被销毁,绑定的属性和信号都会变成无效的状态
QQmlContext
QQmlContext与动态创建和销毁qml对象有什么关系呢?其实我们在通过上面介绍的方式创建动态对象时,系统都会为每个动态对象绑定一个QQmlContext对象。QQmlContext对象也是按照树形模型管理的,与QObject的管理方式相同。但是QQmlContext树与QObject树是完全不同的两个树。
当我们调用destroy方法释放对象时,系统通过对象的QQmlContext树来查找分支与叶子QQmlContext并将调用它们绑定的QObject 的destroy方法。destroy方法可以沿着QQmlContext树向下传导调用,所以在动态创建qml对象时,我们还需要关系QQmlContext树的建立,进而保证自己的对象不被意外释放而导致问题。
由于我们创建动态qml对象时,系统会自动生成与之关联的QQmlContext对象,所以我们弄清这个对象的父亲QQmlContext对象是谁,以及如何修改父亲QQmlContext对象就可以控制QQmlContext树的构建了。下面针对上面介绍的三种创建方式分别说明。
- Qt.createComponent方法创建对象的父亲QQmlContext是调用这个方法所在的QObject的QQmlContext
- Qt.createQmlObject(qmlString,parentItem,path)方式创建对象的父亲QQmlContext是parentItem的QQmlContext
- Component qml type方法创建对象的父亲QQmlContext是声明Component的QObject的QQmlContext
根据上面罗列的规则我们可以总结如下:
- 不要动态创建的qml对象的qml文件中动态创建其他qml对象,因为这样可以避免动态创建的qml对象的QQmlContext在QQmlContext树中有依赖关系,进而避免根qml对象释放触发子qml对象释放的问题。
- 动态创建qml对象的方法最好定义在主程序的qml文件中,这样qml对象的QQmlContext的父亲QQmlContext就是主window的QQmlContext。这样也避免了动态qml对象释放时影响到其他的动态qml对象。
总结
qml的核心概念就是对象树,通过qml以声明的方式定义了对象树的结构。对象树的关系可以通过设置对象的parent方式而改变。在qml中声明的对象是不能被释放的,只有通过动态创建的qml对象才可以通过调用destroy方法释放。destroy释放的过程是向下传导的,这里的向下传导不是根据对象树结构传导,而是按照QQmlContext的对象树结构向下传导。
通过qml方式声明对象树结构并且没有通过修改parent的方式改变对象树结构,这时对象树与QQmlContext对象树结构是一致的。当我们修改了某些对象的parent或是通过动态创建的方式添加新的qml对象到对象树时,对象树与QQmlContext对象树结构就不同了。所以在使用动态创建qml对象的过程中,我们需要更多地关系QQmlContext对象树的结构变化,进而能游刃有余地管理动态创建的qml对象。
我的公众号已经开通,公众号会同步发布。
欢迎关注我的公众号