Qt Quick 常用元素:ComboBox(下拉列表) 与 ProgressBar(进度条)
一、ComboBox
ComboBox,即下拉列表框,由一个列表框和一个标签控件(或编辑控件)组成。ComboBox 的下拉列表是使用 Menu 实现的,列表内的每个条目对应一个 Menultem。
-
弹出下拉列表框后,用户选择列表中的一个条目,此时 currentlndex、currentText 属性就会变化,同时 activated 信号也会发射。
-
ComboBox 的 find() 方法用于查找列表中是否存在指定的字符串,对于可编辑的 ComboBox,向列表中添加条目时可以使用此方法来滤重;textAt() 返回指定索引位置的字符串;selectAll() 可以选中可编辑 ComboBox 的编辑框内的所有文本。
-
count 属性返回ComboBox下拉列表内的条目个数。
-
下拉列表的数据从 model 属性来。model 可以是一个简单的字符串列表,如 ["TV”,"CD Player”,"Set Top Box" , ’’Router"];也可以是 ListModel,当使用 ListModel 时,可以通过 textRole 属性指定列表条目对应的 model 内的 role,如果不设定, 则默认使用第一个 role。比如下面的 ComboBox 示例使用的 ListModel,内部数据有 text 和 color 两个角色,如果不设定 ComboBox 的 textRole,则会默认使用 model 的 text;如果设定 textRole: "color",则使用 model 的 color 角色。
ComboBox { editable: true; model: ListModel { ListElement { text: "Banana"; color: "Yellow" } } textRole: "color"; }
-
你可以给 style 属性指定一个 ComboBoxStyle 对象来定制 ComboBox。其实在 ComboBox.qml 中,为 style 指定了 一个动态创建的 ComboBoxStyle 对象,看源码:
style: Qt.createComponent(Settings.style + "/ComboBoxStyle.qml", comboBox)
如果你想了解怎样实现一个功能完备的 ComboBoxStyle,可以循着这个线索找到可参考的源码。为了展示 Qt Quick “一切风格皆可定制” 的特色,我们还是来看看 ComboBoxStyle 到底能干什么。
1.1 ComboBoxStyle
先说一 下ComboBox 顶部显示当前条目的那个控件。当 ComboBox 不可编辑时,它是一个 Button,显示 currentText,响应点击弹出下拉列表框。如果你还记得 ButtonStyle 这个对象,应该能够想到这个 Button 的 background 和 label 可以定制。没错,ComboBoxStyle 恰恰有 background 和 label 两个类型为 Component 的属性,就是用来定制背景和文本的。
当 ComboBox 可编辑时,一个 Textlnput 对象叠加到 Button 上面,Textlnput 左边与 Button 左边对齐,右边留一些位置给那个代表 “下拉” 含义的小图标—这个小图标的宽度由 ComboBoxStyle 的 dropDownButtonWidth 属性来决定。
好啦,现在说到了 ComboBoxStyle 的 background、label、dropDownButtonWidth 三个属性。control 指向 ComboBox 实例,而 renderType —般不用设置。
1.2 ComboBox 综合演示
combobox demo.qml 的内容如下:
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
Rectangle {
width: 480;
height: 360;
color: "#a0b0a0";
Grid {
anchors.fill: parent;
rows: 2;
columns: 2;
rowSpacing: 4;
columnSpacing: 4;
GroupBox {
width: 200;
height: 120;
title: "只读电器";
ComboBox {
anchors.top: parent.top;
anchors.topMargin: 8;
width: parent.width;
model: ["TV" , "CD Player" , "Set Top Box" , "Router"];
}
}
GroupBox {
width: 200;
height: 120;
title: "可编辑水果";
ComboBox {
anchors.top: parent.top;
anchors.topMargin: 8;
width: parent.width;
editable: true;
model: ListModel {
ListElement { text: "Banana"; color: "Yellow" }
}
//textRole: "color";
onAccepted: {
if(count < 4 && find(currentText) === -1){
model.append({text: editText});
currentIndex = find(currentText);
if(count === 4){
editable = false;
}
}
}
}
}
GroupBox {
width: 200;
height: 120;
title: "定制风格";
ComboBox {
anchors.top: parent.top;
anchors.topMargin: 8;
width: parent.width;
//editable: true;
model: ["Google" , "IBM" , "Digia"];
style: ComboBoxStyle {
dropDownButtonWidth: 20;
background: Rectangle {
implicitHeight: 24;
border.width: control.editable ? 0 : 1;
border.color: (control.hovered || control.pressed)? "blue" : "gray";
color: (control.hovered || control.pressed)? "#e0e0e0" : "#c0c0c0";
Canvas {
width: 16;
height: 18;
anchors.right: parent.right;
anchors.rightMargin: 2;
anchors.top: parent.top;
anchors.topMargin: 1;
onPaint: {
var ctx = getContext("2d");
ctx.save();
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(1, 8);
ctx.lineTo(8, 16);
ctx.lineTo(15, 8);
ctx.stroke();
ctx.restore();
}
}
}
label: Text {
anchors.left: parent.left;
anchors.leftMargin: 2;
width: parent.width - 22;
height: parent.height;
verticalAlignment: Text.AlignVCenter;
horizontalAlignment: Text.AlignHCenter;
text: control.currentText;
color: (control.hovered || control.pressed) ? "blue" : "black";
}
}
}
}
GroupBox {
width: 200;
height: 236;
title: "月份输入";
ComboBox {
anchors.top: parent.top;
anchors.topMargin: 8;
width: parent.width;
editable: true;
model: ListModel {
}
validator: IntValidator{ top: 12; bottom: 1; }
onAccepted: {
if(count < 12 && find(currentText) === -1){
model.append({text: editText});
currentIndex = find(currentText);
if(count === 12){
editable = false;
}
}
}
}
}
}
}
下图是使用 qmlscene 记载 combobox_demo.qml 后的效果图。
下图是做了一些操作后的效果图。
我设计了 4 个 ComboBox 对象,把它们分别放在 4 个 GroupBox 里,这样可以利用 GroupBox 的标题和边框,分界清晰。Grid 定位器管理 4 个 GroupBox。
-
名为 “只读电器” 的 ComboBox,使用一个字符串列表作为 model,最为简单。
-
“可编辑水果” 下拉列表框,使用 ListModel,允许编辑,在 onAccepted 信号处理器内插入用户输入的文本,带滤重功能。当下拉列表内的条目数量达到 4 时禁止编辑。
-
名为 “月份输入” 的 ComboBox,使用了 IntValidator,只允许输入 1 至 12 这几个月份。其他的与 “可编辑水果” 类似。
二、ProgressBar
ProgressBar 进度条,用途大家都了解,这里就不赘述了。
- minimumValue 表示最小值;maximumValue 表示最大值;value 是当前值,更新它进度条就会变化。
- orientation 代表方向,默认取值 Qt.Horizontal,水平方向;要想进度条变垂直,设置其为 Qt. Vertical。
- style,又见 style,无处不在的 style,它指向一个 ProgressBarStyle 来定制进度条的外观。 indeterminate 属性比较有意思,当你不知道实际进度时,设置它为 true,ProgressBar 就会变身为Busylndicator 了,请稍等,就不告诉你等多久……它的默认值为 false。
2.1 ProgressBarStyle
ProgressBarStyle 的 control 属性指向 ProgressBar 对象。currentProgress 是一个 0〜1 的值,表示当前进度。background 用来绘制进度条的背景,progress 用来绘制进度。
2.2 进度条综合演示
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
Rectangle {
width: 480;
height: 240;
color: "#a0b0a0";
Row {
anchors.fill: parent;
anchors.margins: 10;
spacing: 8;
ProgressBar {
minimumValue: 0;
maximumValue: 100;
value: 40;
width: 150;
height: 20;
Timer {
interval: 1000;
repeat: true;
running: true;
onTriggered: {
if(parent.value < 99.1){
parent.value += 1;
}else{
stop();
}
}
}
}
ProgressBar {
orientation: Qt.Vertical;
minimumValue: 0;
maximumValue: 1;
value: 0.2;
width: 20;
height: 200;
Timer {
interval: 1000;
repeat: true;
running: true;
onTriggered: {
parent.value = Math.random();
}
}
}
ProgressBar {
minimumValue: 0;
maximumValue: 1;
value: 0.2;
width: 80;
height: 16;
indeterminate: true;
}
Item {
width: 180;
height: parent.height;
ProgressBar {
id: customProgress;
anchors.centerIn: parent;
minimumValue: 0;
maximumValue: 1;
value: 0.2;
width: 160;
height: 20;
style: ProgressBarStyle {
background: Rectangle {
implicitWidth: 200;
implicitHeight: 20;
border.width: 1;
border.color: control.hovered ? "green" : "gray";
color: "lightgray";
}
progress: Rectangle {
color: "#208020";
}
}
}
ProgressBar {
id: percentProgress;
anchors.top: customProgress.bottom;
anchors.topMargin: 4;
anchors.horizontalCenter: parent.horizontalCenter;
minimumValue: 0;
maximumValue: 1;
value: 0.33;
width: 160;
height: 20;
style: ProgressBarStyle {
id: progressStyle;
background: Rectangle {
border.width: 1;
border.color: control.hovered ? "green" : "gray";
color: "lightgray";
}
progress: Rectangle {
color: "#208020";
}
panel: Item {
implicitWidth: 200;
implicitHeight: 20;
Loader {
anchors.fill: parent;
sourceComponent: background;
}
Loader {
id: progressLoader;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.margins: 3;
z: 1;
width: currentProgress * (parent.width - 6);
sourceComponent: progressStyle.progress;
}
Text {
color: "blue";
text: currentProgress * 100 + "%";
z: 2;
anchors.centerIn: parent;
}
}
}
}
}
}
}
设计了 5 个进度条,水平、垂直、忙等待这三个 ProgressBar 对象都很简单。id 为 customProgress 的 ProgressBar 对象只定制了 progress 和 label, 不必说了。
id 为 percentProgress 的 ProgressBar,进度条的填充部分与背景之间留了 3 个像素的空隙, 这是通过设置 progressLoader 的 anchors.margins 实现的。在定制 panel 时,我添加了一个 Text 对象,让它居中显示进度百分比。
下图是执行 “qmlscene progressbar_demo.qml” 命令的效果图。
参考:
《Qt Quick核心编程》第9章