在《Qt Quick 事件处理之信号与槽》一文中介绍自定义信号时,举了一个简单的例子,定义了一个颜色选择组件,当用户在组建内点击鼠标时,该组件会发出一个携带颜色值的信号,当时我使用 Connections 对象连接到组件的 colorPicked 信号,改变文本的颜色。 当时用到的 Component 、 Loader 两个特性,一直没来得及介绍,可能很多人都还在雾里看花呢。这次呢,我们就来仔仔细细地把他们讲清楚。
版权所有 foruok ,转载请注明出处:http://blog.csdn.net/foruok 。
Components(组件)
Component 是由 Qt 框架或开发者封装好的、只暴露了必要接口的 QML 类型,可以重复利用。一个 QML 组件就像一个黑盒子,它通过属性、信号、函数和外部世界交互。
一个 Component 即可以定义在独立的 qml 文件中,也可以嵌入到其它的 qml 文档中来定义。通常我们可以根据这个原则来选择将一个 Component 定义在哪里:如果一个 Component 比较小且只在某个 qml 文档中使用或者一个 Component 从逻辑上看从属于某个 qml 文档,那就可以采用嵌入的方式来定义该 Component 。你也可以与 C++ 的嵌套类对比来理解。
嵌入式定义组件
《Qt Quick 事件处理之信号与槽》一文中使用到 Component 的示例 QML 代码如下:
import QtQuick 2.0import QtQuick.Controls 1.1Rectangle { width: 320; height: 240; color: "#C0C0C0"; Text { id: coloredText; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; anchors.topMargin: 4; text: "Hello World!"; font.pixelSize: 32; } Component { id: colorComponent; Rectangle { id: colorPicker; width: 50; height: 30; signal colorPicked(color clr); MouseArea { anchors.fill: parent onPressed: colorPicker.colorPicked(colorPicker.color); } } } Loader{ id: redLoader; anchors.left: parent.left; anchors.leftMargin: 4; anchors.bottom: parent.bottom; anchors.bottomMargin: 4; sourceComponent: colorComponent; onLoaded:{ item.color = "red"; } } Loader{ id: blueLoader; anchors.left: redLoader.right; anchors.leftMargin: 4; anchors.bottom: parent.bottom; anchors.bottomMargin: 4; sourceComponent: colorComponent; onLoaded:{ item.color = "blue"; } } Connections { target: redLoader.item; onColorPicked:{ coloredText.color = clr; } } Connections { target: blueLoader.item; onColorPicked:{ coloredText.color = clr; } }}
其中,颜色选择组件的定义代码如下:
Component { id: colorComponent; Rectangle { id: colorPicker; width: 50; height: 30; signal colorPicked(color clr); MouseArea { anchors.fill: parent onPressed: colorPicker.colorPicked(colorPicker.color); } } }
如你所见,要在一个 QML 文档中嵌入 Component 的定义,需要使用 Component 对象。
定义一个 Component 与定义一个 QML 文档类似, Component 只能包含一个顶层 item ,而且在这个 item 之外不能定义任何数据,除了 id 。比如上面的代码中,顶层 item 是 Rectangle 对象,在 Rectangle 之外我定义了 id 属性,其值为 colorComponent 。而顶层 item 之内,则可以包含更多的子元素来协同工作,最终形成一个具有特定功能的组件。
Component 通常用来给一个 view 提供图形化组件,比如 ListView::delegate 属性就需要一个 Component 来指定如何显示列表的每一个项,又比如 ButtonStyle::background 属性也需要一个 Component 来指定如何绘制 Button 的背景。
Component 不是 Item 的派生类,而是从 QQmlComponent 继承而来,虽然它通过自己的顶层 item 为其它的 view 提供可视化组件,但它本身是不可见元素。你可以这么理解:你定义的组件是一个新的类型,它必须被实例化以后才可能显示。而要实例化一个嵌入在 qml 文档中定义的组件,则可以通过 Loader 。后面我们详细讲述 Loader ,这里先按下不表,我们要来看如何在一个文件中定义组件了。
在单独文件中定义组件
很多时候我们把一个 Component 单独定义在一个 qml 文档中,比如 Qt Quick 提供的 BusyIndicator 控件,其实就是在 BusyIndicator.qml 中定义的一个组件。下面是 BusyIndicator.qml 文件的内容:
Control { id: indicator property bool running: true Accessible.role: Accessible.Indicator Accessible.name: "busy" style: Qt.createComponent(Settings.style + "/BusyIndicatorStyle.qml", indicator)}
我在《 Qt Quick 简单教程》一文中的显示网络图片实例中,使用了 BusyIndicator 来提示用户图片正在加载中需要等候,你可以跳转到那篇文章学习 BusyIndicator 的用法。
BusyIndicator 组件的代码非常简单,只是给 Control 元素(Qt Quick 定义的私有元素,用作其它控件的基类,如 ComboBox 、 BusyIndicator 等)增加了一个属性、设置了几个属性的值,仅此而已。
不知你是否注意到了, BusyIndicator.qml 文件中的顶层 item 是 Control ,而我们使用时却是以 BusyIndicator 为组件名(类名)。这是我们定义 Component 时要遵守的一个约定:组件名字必须和 qml 文件名一致。好嘛,和 Java 一样啦,类名就是文件名。还有一点,组件名字的第一个字母必须是大写。对于在文件中定义一个组件,就这么简单了,再没有其它的特殊要求。 Qt Quick 提供的多数基本元素和特性,你都可以在定义组件时使用。
一旦你在文件中定义了一个组件,就可以像使用标准 Qt Quick 元素一样使用你的组件。比如我们给颜色选择组件起个名字叫 ColorPicker ,对应的 qml 文件为 ColorPicker.qml ,那么你就可以在其它 QML 文档中使用 ColorPicker {...} 来定义 ColorPicker 的实例。
好啦,现在让我们来看看在单独文件中定义的 ColorPicker 组件:
import QtQuick 2.0import QtQuick.Controls 1.1Rectangle { id: colorPicker; width: 50; height: 30; signal colorPicked(color clr); function configureBorder(){ colorPicker.border.width = colorPicker.focus ? 2 : 0; colorPicker.border.color = colorPicker.focus ? "#90D750" : "#808080"; } MouseArea { anchors.fill: parent onClicked: { colorPicker.colorPicked(colorPicker.color); mouse.accepted = true; colorPicker.focus = true; } } Keys.onReturnPressed: { colorPicker.colorPicked(colorPicker.color); event.accepted = true; } Keys.onSpacePressed: { colorPicker.colorPicked(colorPicker.color); event.accepted = true; } onFocusChanged: { configureBorder(); } Component.onCompleted: { configureBorder(); }}
请注意上面的代码,它和嵌入式定义有明显不同: Component 对象不见咧!对,就是酱紫滴:在单独文件内定义组件,不需要 Component 对象,只有在其它 QML 文档中嵌入式定义组件时才需要 Component 对象。另外,为了能够让多个 ColorPicker 组件可以正常的显示焦点框,我还使用了 onClicked 信号处理器,新增了 onFocusChanged 信号处理器,在它们的实现中调用 configureBorder() 函数来重新设置边框的宽度和颜色,新增 onReturnPressed 和 onSpacePressed 以便响应回车和空格两个按键。
你可以使用 Item 或其派生类作为组件的根 item 。 ColorPicker 组件使用 Rectangle 作为根 Item 。现在让我们看看如实在其它文件中使用新定义的 ColorPicker 组件。我修改了上节的示例,新的 qml 文件被我命名为 component_file.qml ,内容如下:
import QtQuick 2.0import QtQuick.Controls 1.1Rectangle { width: 320; height: 240; color: "#EEEEEE"; Text { id: coloredText; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; anchors.topMargin: 4; text: "Hello World!"; font.