QT qml中动态组件那些事

目录

动态创建qml对象

Qt.createComponent

Qt.createQmlObject

Component qml type

销毁动态创建的对象

QQmlContext

总结


动态创建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调用的时机包括如下:

  1. 自己销毁自己=》通过动态qml的根Item调用destroy进行销毁,可以设置delay时间。
  2. 在动态创建对象的地方销毁=》var obj = component.createObject obj.destroy()

这里有几个问题需要注意:

  1. 只有动态创建的qml对象才可以使用destroy方法销毁。
  2. destroy销毁后,动态对象的context就被销毁,绑定的属性和信号都会变成无效的状态

QQmlContext

QQmlContext与动态创建和销毁qml对象有什么关系呢?其实我们在通过上面介绍的方式创建动态对象时,系统都会为每个动态对象绑定一个QQmlContext对象。QQmlContext对象也是按照树形模型管理的,与QObject的管理方式相同。但是QQmlContext树与QObject树是完全不同的两个树。

当我们调用destroy方法释放对象时,系统通过对象的QQmlContext树来查找分支与叶子QQmlContext并将调用它们绑定的QObject 的destroy方法。destroy方法可以沿着QQmlContext树向下传导调用,所以在动态创建qml对象时,我们还需要关系QQmlContext树的建立,进而保证自己的对象不被意外释放而导致问题。

由于我们创建动态qml对象时,系统会自动生成与之关联的QQmlContext对象,所以我们弄清这个对象的父亲QQmlContext对象是谁,以及如何修改父亲QQmlContext对象就可以控制QQmlContext树的构建了。下面针对上面介绍的三种创建方式分别说明。

  1. Qt.createComponent方法创建对象的父亲QQmlContext是调用这个方法所在的QObject的QQmlContext
  2. Qt.createQmlObject(qmlString,parentItem,path)方式创建对象的父亲QQmlContext是parentItem的QQmlContext
  3. Component qml type方法创建对象的父亲QQmlContext是声明Component的QObject的QQmlContext

根据上面罗列的规则我们可以总结如下:

  1. 不要动态创建的qml对象的qml文件中动态创建其他qml对象,因为这样可以避免动态创建的qml对象的QQmlContext在QQmlContext树中有依赖关系,进而避免根qml对象释放触发子qml对象释放的问题。
  2. 动态创建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对象。

我的公众号已经开通,公众号会同步发布。
欢迎关注我的公众号

QT qml中动态组件那些事

 

上一篇:Laravel6.* Eloquent


下一篇:Kubernetes K8S之Helm部署ELK日志分析系统