利用
Loader 动态创建与销毁组件
现在我们看看如何动态创建、销毁组件。下面是 dynamic_component.qml
:
import QtQuick 2.0 import QtQuick.Controls 1.1 Rectangle { width: 320; height: 240; color: "#EEEEEE"; id: rootItem; property var colorPickerShow : false; Text { id: coloredText; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; anchors.topMargin: 4; text: "Hello World!"; font.pixelSize: 32; } Button { id: ctrlButton; text: "Show"; anchors.left: parent.left; anchors.leftMargin: 4; anchors.bottom: parent.bottom; anchors.bottomMargin: 4; onClicked:{ if(rootItem.colorPickerShow){ redLoader.source = ""; blueLoader.source = ""; rootItem.colorPickerShow = false; ctrlButton.text = "Show"; }else{ redLoader.source = "ColorPicker.qml"; redLoader.item.colorPicked.connect(onPickedRed); blueLoader.source = "ColorPicker.qml"; blueLoader.item.colorPicked.connect(onPickedBlue); redLoader.focus = true; rootItem.colorPickerShow = true; ctrlButton.text = "Hide"; } } } Loader{ id: redLoader; anchors.left: ctrlButton.right; anchors.leftMargin: 4; anchors.bottom: ctrlButton.bottom; KeyNavigation.right: blueLoader; KeyNavigation.tab: blueLoader; onLoaded:{ if(item != null){ item.color = "red"; item.focus = true; } } onFocusChanged:{ if(item != null){ item.focus = focus; } } } Loader{ id: blueLoader; anchors.left: redLoader.right; anchors.leftMargin: 4; anchors.bottom: redLoader.bottom; KeyNavigation.left: redLoader; KeyNavigation.tab: redLoader; onLoaded:{ if(item != null){ item.color = "blue"; } } onFocusChanged:{ if(item != null){ item.focus = focus; } } } function onPickedBlue(clr){ coloredText.color = clr; if(!blueLoader.focus){ blueLoader.focus = true; redLoader.focus = false; } } function onPickedRed(clr){ coloredText.color = clr; if(!redLoader.focus){ redLoader.focus = true; blueLoader.focus = false; } } }
|
这次我们在界面上放一个按钮,通过按钮来控制颜色选择组件的创建与销毁。启动应用时没有创建颜色选择组件,如图
4 所示:
图 4 动态创建组件初始效果
当你点击 "Show" 按键,代码通过设置 redLoader 和 blueLoader
的 source 来创建颜色选择组件,连接颜色组件的 colorPicked 信号到相应的方法,同时将改变按钮文字,也改变
rootItem 维护的颜色组件是否显示的标志位以便下次再点击按钮可以正常显示。图 5 是颜色选择组件显示后的效果图:
图 5 颜色组件创建后的效果
使用 Loader 控制组件的动态创建与销毁,只是 Qt Quick 提供的动态维护对象的两种方式中的一种。还有一种,是在
JavaScript 中动态创建 QML 对象。
在 JavaScript 中动态创建 QML 对象
ML 支持在 JavaScript 中动态创建对象。这对于延迟对象的创建、缩短应用的启动时间都是有帮助的。同时这种机制也使得我们可以根据用户的输入或者某些事件来动态的将可见元素添加到应用场景中。
在 JavaScript 中,有两种方式可以动态地创建对象:
使用 Qt.createComponent() 动态地创建一个组件对象,然后使用
Component 的 createObject() 方法创建对象
使用 Qt.createQmlObject() 从一个 QML 字符串直接创建一个对象
如果你在一个 qml 文件中定义了一个组件(比如我们的 ColorPicker
),而你想动态地创建它的实例,使用 Qt.createComponent() 是比较好的方式;而如果你的
QML 对象本身是在应用运行时产生的,那 Qt.createQmlObject() 可能是比较好的选择。
从组件文件动态创建 Component
Qt 对象的 createComponent() 方法可以根据 QML
文件动态的创建一个组件。一旦你拥有了组件对象,就可以调用它的 createObject() 方法创建一个组件的实例。下面是我们新的示例,
qml 文件是 qt_create_component.qml :
import QtQuick 2.0 import QtQuick.Controls 1.1 Rectangle { id: rootItem; width: 360; height: 300; property var count: 0; property Component component: null; Text { id: coloredText; text: "Hello World!"; anchors.centerIn: parent; font.pixelSize: 24; } function changeTextColor(clr){ coloredText.color = clr; } function createColorPicker(clr){ if(rootItem.component == null){ rootItem.component = Qt.createComponent("ColorPicker.qml"); } var colorPicker; if(rootItem.component.status == Component.Ready) { colorPicker = rootItem.component.createObject(rootItem, {"color" : clr, "x" : rootItem.count *55, "y" : 10}); colorPicker.colorPicked.connect(rootItem.changeTextColor); } rootItem.count++; } Button { id: add; text: "add"; anchors.left: parent.left; anchors.leftMargin: 4; anchors.bottom: parent.bottom; anchors.bottomMargin: 4; onClicked: { createColorPicker(Qt.rgba(Math.random(), Math.random(), Math.random(), 1)); } } } |
图 6 是示例启动后的界面:
图 6 qt create component
初始效果
图 7 是我点击了 6 次 "add" 按钮后的效果:
图 7 动态创建了颜色选择组件
好啦,现在让我们来看看代码。
我在 qt_create_component.qml 中定义了 createColorPicker()
函数,该函数的参数是颜色值,它根据颜色值来创建一个颜色选择组件实例。首先它判断 rootItem 的 component
属性如果为 null ,就调用 Qt.createComponent() 创建一个 ColorPicker
组件,然后调用 Component.createObject() 创建一个颜色选择组件实例。 createObject()
方法有两个参数,第一个参数用来指定创建出来的 item 的 parent ,第二个参数用来传递初始化参数给待创建的
item ,这些参数以 key - value 的形式保存在一个对象中。我在创建颜色组件实例时,传递颜色、
x 、 y 三个属性给待创建的 item ,于是你看到了,那些色块都在界面顶部。创建了颜色选择组件实例,我调用
colorPicked 信号的 connect() 方法,连接 rootItem 的 changeTextColor
方法,以便用户点击色块时改变 "Hello World!" 文本的颜色。
再来看 "add" 按钮,它的 onClicked 信号处理器,调用 Math
对象的随机函数 random() 和 Qt.rgba() ,随机生成一个 Color 对象,传递给 createColorPicker()
方法来创建指定颜色的颜色选择组件实例。
提一下,对于嵌入在 qml 文档内定义的 Component ,因为 Component 对象是现成的,可以略去
Qt.createComponent() 调用,直接使用 createObject() 方法创建组件实例。
代码就这么简单,解说到此为止。现在让我们看看怎么使用 Qt.createQmlObject() 来创建对象。
从 QML 字符串创建对象
如果你的软件,需要在运行过程中,根据应用的状态适时的生成用于描述对象的
QML 字符串,进而根据这个 QML 字符串创建对象,那么可以使用像下面这样:
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}', parentItem, "dynamicSnippet1"); |
createQmlObject 的第一个参数是要创建对象的 QML 字符串,就像一个 QML 文档一样,你需要导入你用到的所有类型和模块;第二个参数用于指定要创建的对象的父对象;第三个参数,用于给新创建的对象关联一个文件路径,主要用于报告错误。
对于动态创建的对象,该如何销毁呢?
销毁动态创建的对象
有些软件,在不需要一个动态创建的 QML 对象时,仅仅是把它的 visible
属性设置为 false 或者把 opactity 属性设置为 0 ,而不是删除这个对象。如果动态创建的对象很多,无用的对象都这么处理而不直接删除,那会给软件带来比较大的性能问题,比如内存占用增多,运行速度变慢等等。所以呢,动态创建的对象,不再使用时,最好把它删除掉。
我们这里说的动态创建的对象,特指使用 Qt.createComponent()
或 Qt.createQmlObject() 方法创建的对象, 使用 Loader 创建的对象,应当通过将
source 设置为空串或将 sourceComponent 设置为 undefined 触发 Loader
销毁它们。
要删除一个对象,可以调用其 destroy() 方法。 destroy()
方法有一个可选参数,指定延迟多少毫秒再删除这个对象,其默认值为 0 。 destroy() 方法有点儿像
Qt C++ 中 QObject 的 deleteLater() 方法,即便你设定延迟为 0 去调用它,对象也并不会立即删除,QML
引擎会在当前代码块执行结束后的某个合适的时刻删除它们。所以呢,即便你在一个对象内部调用 destroy()
方法也是安全的。
现在让我我们再来一个实例,看看如何销毁对象。新的 qml 文件命名为 delete_dynamic_object.qml
,从 qt_create_component.qml 拷贝而来,作了一点点修改。先看下:
import QtQuick 2.0 import QtQuick.Controls 1.1 Rectangle { id: rootItem; width: 360; height: 300; property var count: 0; property Component component: null; Text { id: coloredText; text: "Hello World!"; anchors.centerIn: parent; font.pixelSize: 24; } function changeTextColor(clr){ coloredText.color = clr; } function createColorPicker(clr){ if(rootItem.component == null){ rootItem.component = Qt.createComponent("ColorPicker.qml"); } var colorPicker; if(rootItem.component.status == Component.Ready) { colorPicker = rootItem.component.createObject(rootItem, {"color" : clr, "x" : rootItem.count *55, "y" : 10}); colorPicker.colorPicked.connect(rootItem.changeTextColor); //[1] add 3 lines to delete some obejcts if(rootItem.count % 2 == 1) { colorPicker.destroy(1000); } } rootItem.count++; } Button { id: add; text: "add"; anchors.left: parent.left; anchors.leftMargin: 4; anchors.bottom: parent.bottom; anchors.bottomMargin: 4; onClicked: { createColorPicker(Qt.rgba(Math.random(), Math.random(), Math.random(), 1)); } } } |
修改的部分我用注释标注出来了:添加了三行代码,新创建的颜色选择组件实例,隔一个删一个,
destroy(1000) 调用指示对象在 1 秒后删除。
图 8 是运行后的效果图:
图 8 删除动态创建的对象
我还制作了一个演示删除动态创建的对象的示例, qml 文件是 delete_dynamic_object2.qml
,我把点击 "add" 按钮创建的对象保存在一个数组中,当你点击 "del"
按钮时,删除最后添加的那个颜色选择组件实例。下面是代码:
import QtQuick 2.0 import QtQuick.Controls 1.1 Rectangle { id: rootItem; width: 360; height: 300; property var count: 0; property Component component: null; property var dynamicObjects: new Array(); Text { id: coloredText; text: "Hello World!"; anchors.centerIn: parent; font.pixelSize: 24; } function changeTextColor(clr){ coloredText.color = clr; } function createColorPicker(clr){ if(rootItem.component == null){ rootItem.component = Qt.createComponent("ColorPicker.qml"); } var colorPicker; if(rootItem.component.status == Component.Ready) { colorPicker = rootItem.component.createObject(rootItem,
{"color" : clr, "x" : rootItem.dynamicObjects.length *55, "y" : 10}); colorPicker.colorPicked.connect(rootItem.changeTextColor); rootItem.dynamicObjects[rootItem.dynamicObjects.length] = colorPicker; console.log("add, rootItem.dynamicObject.length = ", rootItem.dynamicObjects.length); } } Button { id: add; text: "add"; anchors.left: parent.left; anchors.leftMargin: 4; anchors.bottom: parent.bottom; anchors.bottomMargin: 4; onClicked: { createColorPicker(Qt.rgba(Math.random(), Math.random(), Math.random(), 1)); } } Button { id: del; text: "del"; anchors.left: add.right; anchors.leftMargin: 4; anchors.bottom: add.bottom; onClicked: { console.log("rootItem.dynamicObject.length = ", rootItem.dynamicObjects.length); if(rootItem.dynamicObjects.length > 0){ var deleted = rootItem.dynamicObjects.splice(-1, 1); deleted[0].destroy(); } } } } |
你可以自己使用 qmlscene 运行 delete_dynamic_object2.qml 看看效果。
|