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章


posted @ 2019-11-28 14:26  fengMisaka  阅读(5382)  评论(0编辑  收藏  举报