适合人群: 已掌握基础 QML 语法,想系统掌握完整控件库的开发者 > 预计耗时: 90 分钟
前言
Qt Quick Controls 提供了构建完整应用界面所需的全套控件——从最基础的按钮、输入框,到菜单、抽屉、页面导航。本文系统梳理每一类控件的完整用法,并深入讲解样式系统和自定义控件外观。
一、控件分类总览
Qt Quick Controls 的控件按功能分为六大类:
QtQuick.Controls
├── 按钮类 Button · CheckBox · RadioButton · Switch · RoundButton
├── 输入类 TextField · TextArea · Slider · Dial · SpinBox · ComboBox · Tumbler
├── 显示类 Label · ProgressBar · BusyIndicator · DelayButton
├── 容器类 Frame · GroupBox · ScrollView · Pane · Page · TabBar · ToolBar
├── 弹窗类 Dialog · Drawer · Menu · Popup · ToolTip
└── 导航类 StackView · SwipeView · PageIndicator
二、内置样式一览
Qt Quick Controls 内置多套样式,一行代码即可切换全局外观。
Basic 样式(默认,跨平台)
轻量极简,性能最佳,适合作为自定义样式的起点。
Material 样式(Google Material Design)
适合移动端和现代桌面应用,视觉效果丰富。
Fusion 样式(桌面风格)
传统桌面应用外观,与 Qt Widgets 视觉语言一致,适合企业桌面工具。
各平台默认样式
| 操作系统 | 默认样式 |
|---|---|
| Android | Material |
| iOS | iOS Style |
| macOS | macOS Style |
| Windows | Windows Style |
| Linux / 其他 | Fusion |
设置样式的三种方式
方式一:编译时导入(推荐,性能最优)
// 必须在所有其他 QtQuick.Controls 导入之前
import QtQuick.Controls.Material
ApplicationWindow {
Material.theme: Material.Light
Material.accent: Material.Blue
}
方式二:运行时 C++ 设置
#include <QQuickStyle>
QQuickStyle::setStyle("Material");
方式三:配置文件 qtquickcontrols2.conf
[Controls]
Style=Material
[Material]
Theme=Light
Accent=Blue
三、按钮类控件完整用法
3.1 Button 的状态属性
Button {
text: "操作按钮"
// 核心状态属性(只读,反映当前交互状态)
// pressed — 正在按下
// hovered — 鼠标悬停
// checked — 已选中(checkable 时有效)
// enabled — 是否可用
// highlighted — 强调样式(Material 下显示 accent 色)
// flat — 扁平样式(无背景边框)
highlighted: true
flat: false
checkable: true // 允许切换选中状态
icon.source: "images/send.svg"
icon.width: 18
icon.height: 18
}
3.2 DelayButton — 长按确认按钮
需要长按才能触发,适合危险操作(删除、格式化):
DelayButton {
text: "长按删除"
delay: 1500 // 需要按住 1.5 秒
onActivated: console.log("确认删除!")
// 进度条自动显示按压进度
}
3.3 RoundButton — 圆形按钮
RoundButton {
text: "+"
font.pixelSize: 20
highlighted: true
// 或使用图标
icon.source: "images/add.svg"
}
四、输入类控件完整用法
4.1 Dial — 旋钮控件
适合音量、亮度等环形调节:
import QtQuick
import QtQuick.Controls
Column {
spacing: 8
Dial {
id: volumeDial
from: 0; to: 100; value: 50
stepSize: 1
// 旋转模式
inputMode: Dial.Circular // 圆形拖动(默认)
// inputMode: Dial.Horizontal // 水平拖动
// inputMode: Dial.Vertical // 垂直拖动
}
Label {
anchors.horizontalCenter: parent.horizontalCenter
text: "音量:" + Math.round(volumeDial.value)
}
}
4.2 Tumbler — 滚筒选择器
适合时间、日期选择:
Row {
spacing: 0
Tumbler {
id: hourTumbler
model: 24
delegate: Label {
required property int index
text: index.toString().padStart(2, "0")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
font.pixelSize: Math.abs(Tumbler.displacement) < 0.5 ? 18 : 14
}
}
Label {
anchors.verticalCenter: parent.verticalCenter
text: ":"
font.pixelSize: 18
font.bold: true
}
Tumbler {
id: minuteTumbler
model: 60
delegate: Label {
required property int index
text: index.toString().padStart(2, "0")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
font.pixelSize: Math.abs(Tumbler.displacement) < 0.5 ? 18 : 14
}
}
}
五、容器类控件
5.1 Frame 与 GroupBox
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Column {
spacing: 16
width: 300
// Frame:带边框的容器
Frame {
width: parent.width
ColumnLayout {
width: parent.width
Label { text: "账号信息"; font.bold: true }
TextField { Layout.fillWidth: true; placeholderText: "用户名" }
TextField { Layout.fillWidth: true; placeholderText: "邮箱" }
}
}
// GroupBox:带标题的 Frame
GroupBox {
width: parent.width
title: "通知设置"
ColumnLayout {
width: parent.width
CheckBox { text: "邮件通知" }
CheckBox { text: "短信通知" }
CheckBox { text: "推送通知"; checked: true }
}
}
}
5.2 TabBar + StackLayout — 选项卡导航
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Column {
width: 400
TabBar {
id: tabBar
width: parent.width
TabButton { text: "首页" }
TabButton { text: "发现" }
TabButton { text: "消息" }
TabButton { text: "我的" }
}
StackLayout {
width: parent.width
height: 300
currentIndex: tabBar.currentIndex // 与 TabBar 绑定
Rectangle { color: "#E6F1FB"; Label { anchors.centerIn: parent; text: "首页内容" } }
Rectangle { color: "#E1F5EE"; Label { anchors.centerIn: parent; text: "发现内容" } }
Rectangle { color: "#FAEEDA"; Label { anchors.centerIn: parent; text: "消息内容" } }
Rectangle { color: "#FAECE7"; Label { anchors.centerIn: parent; text: "我的内容" } }
}
}
5.3 ToolBar — 工具栏
ApplicationWindow {
width: 500; height: 400
visible: true
header: ToolBar {
RowLayout {
anchors.fill: parent
ToolButton {
icon.source: "images/menu.svg"
onClicked: drawer.open()
}
Label {
text: "应用标题"
font.pixelSize: 16
font.bold: true
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
}
ToolButton {
icon.source: "images/search.svg"
}
ToolButton {
icon.source: "images/more.svg"
}
}
}
}
六、弹窗类控件
6.1 Dialog — 标准对话框
Dialog {
id: confirmDialog
anchors.centerIn: parent
title: "确认操作"
modal: true
width: 280
// 内容区
contentItem: Label {
text: "确定要删除这条记录吗?此操作不可撤销。"
wrapMode: Text.Wrap
padding: 8
}
// 标准按钮
standardButtons: Dialog.Ok | Dialog.Cancel
onAccepted: console.log("用户点击了确定")
onRejected: console.log("用户取消了操作")
}
Button {
text: "删除"
onClicked: confirmDialog.open()
}
6.2 自定义 Dialog 内容
Dialog {
id: inputDialog
anchors.centerIn: parent
title: "重命名"
modal: true
width: 300
contentItem: ColumnLayout {
spacing: 12
width: parent.width
Label { text: "请输入新名称:" }
TextField {
id: nameField
Layout.fillWidth: true
placeholderText: "名称"
focus: true // 对话框打开时自动聚焦
}
}
footer: DialogButtonBox {
Button {
text: "取消"
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
}
Button {
text: "确认"
enabled: nameField.text.length > 0
highlighted: true
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
}
}
onAccepted: console.log("新名称:" + nameField.text)
}
6.3 Drawer — 侧滑抽屉
ApplicationWindow {
id: window
width: 400; height: 600
visible: true
Drawer {
id: drawer
width: 260
height: window.height
edge: Qt.LeftEdge // Qt.RightEdge / Qt.TopEdge / Qt.BottomEdge
// 抽屉内容
ColumnLayout {
anchors.fill: parent
anchors.margins: 0
spacing: 0
// 用户信息头部
Rectangle {
Layout.fillWidth: true
height: 120
color: "#4A90E2"
Column {
anchors.centerIn: parent
spacing: 6
Rectangle {
width: 56; height: 56; radius: 28
color: "white"
anchors.horizontalCenter: parent.horizontalCenter
Label {
anchors.centerIn: parent
text: "用"
font.pixelSize: 22
font.bold: true
color: "#4A90E2"
}
}
Label {
text: "用户名"
color: "white"
font.pixelSize: 14
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
// 菜单列表
Repeater {
model: ["首页", "收藏", "历史记录", "设置", "帮助"]
delegate: ItemDelegate {
required property string modelData
Layout.fillWidth: true
text: modelData
onClicked: {
console.log("导航到:" + modelData)
drawer.close()
}
}
}
Item { Layout.fillHeight: true }
ItemDelegate {
Layout.fillWidth: true
text: "退出登录"
}
}
}
Button {
text: "打开抽屉"
anchors.centerIn: parent
onClicked: drawer.open()
}
}
6.4 Menu — 上下文菜单
Menu {
id: contextMenu
MenuItem {
text: "复制"
shortcut: "Ctrl+C"
onTriggered: console.log("复制")
}
MenuItem {
text: "粘贴"
shortcut: "Ctrl+V"
onTriggered: console.log("粘贴")
}
MenuSeparator {} // 分割线
Menu {
title: "导出为"
MenuItem { text: "PDF" }
MenuItem { text: "PNG" }
MenuItem { text: "SVG" }
}
MenuSeparator {}
MenuItem {
text: "删除"
enabled: false // 禁用状态
}
}
// 右键触发
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: contextMenu.popup() // 在鼠标位置弹出
}
6.5 ToolTip — 悬停提示
Button {
text: "保存"
icon.source: "images/save.svg"
// 方式一:附加属性(最简单)
ToolTip.visible: hovered
ToolTip.text: "保存文件 (Ctrl+S)"
ToolTip.delay: 800 // 悬停 800ms 后显示
// 方式二:独立 ToolTip 组件(可自定义外观)
}
七、导航类控件
7.1 StackView — 页面栈导航
StackView 实现类似移动端的前进/后退页面导航:
// 页面切换时间线:
// push() → 新页面从右侧滑入
// pop() → 当前页面向右滑出
// replace() → 替换当前页面(无返回)
StackView {
id: stackView
anchors.fill: parent
// 初始页面
initialItem: homePage
}
Component {
id: homePage
Rectangle {
color: "#f5f5f5"
Column {
anchors.centerIn: parent
spacing: 12
Label { text: "首页"; font.pixelSize: 24; font.bold: true }
Button {
text: "进入详情页"
onClicked: stackView.push(detailPage, { title: "详情内容" })
}
}
}
}
Component {
id: detailPage
Rectangle {
property string title: ""
color: "#E6F1FB"
Column {
anchors.centerIn: parent
spacing: 12
Label { text: title; font.pixelSize: 20 }
Button {
text: "← 返回"
onClicked: stackView.pop()
}
}
}
}
StackView 页面切换动画流程:
┌─────────────┐ push() ┌─────────────┬─────────────┐
│ 首页 │ ────────▶ │ 首页 │ 详情页 │
│ (当前) │ │ (历史) │ (当前) │
└─────────────┘ └─────────────┴─────────────┘
pop() ┌─────────────┐
────────▶ │ 首页 │
│ (当前) │
└─────────────┘
7.2 SwipeView + PageIndicator — 横划导航
适合引导页、图片轮播、多步骤表单:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Column {
width: 360
spacing: 0
SwipeView {
id: swipeView
width: parent.width
height: 280
// 第一页
Rectangle {
color: "#4A90E2"
Label {
anchors.centerIn: parent
text: "欢迎使用"
font.pixelSize: 24
font.bold: true
color: "white"
}
}
// 第二页
Rectangle {
color: "#1D9E75"
Label {
anchors.centerIn: parent
text: "功能介绍"
font.pixelSize: 24
font.bold: true
color: "white"
}
}
// 第三页
Rectangle {
color: "#E2934A"
Label {
anchors.centerIn: parent
text: "开始使用"
font.pixelSize: 24
font.bold: true
color: "white"
}
}
}
// 页面指示点
PageIndicator {
anchors.horizontalCenter: parent.horizontalCenter
count: swipeView.count
currentIndex: swipeView.currentIndex // 双向绑定
interactive: true // 点击圆点可跳转
}
}
八、自定义控件外观
8.1 替换 background 和 contentItem
每个控件的外观由 background(背景)和 contentItem(内容)组成,单独替换其中任意一个即可改变外观:
// 自定义圆角按钮,保留所有交互行为
Button {
id: btn
text: "自定义按钮"
width: 140; height: 44
background: Rectangle {
radius: btn.height / 2 // 完全圆角
color: btn.pressed ? "#2C72C7" :
btn.hovered ? "#5BA3E8" :
btn.enabled ? "#4A90E2" : "#AAAAAA"
Behavior on color {
ColorAnimation { duration: 120 }
}
border.width: 0
}
contentItem: Text {
text: btn.text
color: "white"
font.pixelSize: 14
font.bold: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
8.2 自定义 ProgressBar
ProgressBar {
id: bar
width: 300
value: 0.65
background: Rectangle {
implicitWidth: 200; implicitHeight: 8
color: "#e0e0e0"
radius: 4
}
contentItem: Item {
implicitWidth: 200; implicitHeight: 8
Rectangle {
width: bar.visualPosition * parent.width
height: parent.height
radius: 4
// 渐变进度条
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: "#4A90E2" }
GradientStop { position: 1.0; color: "#1D9E75" }
}
Behavior on width {
NumberAnimation { duration: 300; easing.type: Easing.OutCubic }
}
}
}
}
8.3 封装统一风格的自定义控件
把自定义样式封装到独立的组件文件,在整个项目复用:
// PrimaryButton.qml
import QtQuick
import QtQuick.Controls
Button {
id: root
height: 44
property color primaryColor: "#4A90E2"
background: Rectangle {
radius: 8
color: root.pressed ? Qt.darker(root.primaryColor, 1.2)
: root.hovered ? Qt.lighter(root.primaryColor, 1.1)
: root.enabled ? root.primaryColor
: "#cccccc"
Behavior on color { ColorAnimation { duration: 100 } }
}
contentItem: Text {
text: root.text
color: root.enabled ? "white" : "#888888"
font.pixelSize: 14
font.bold: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
使用:
PrimaryButton { text: "确认"; width: 120 }
PrimaryButton { text: "危险操作"; width: 120; primaryColor: "#E24A4A" }
PrimaryButton { text: "成功"; width: 120; primaryColor: "#1D9E75" }
九、综合示例:设置页面
整合本文所有控件,构建一个完整的应用设置页面:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ApplicationWindow {
width: 400; height: 650
visible: true
title: "设置"
ScrollView {
anchors.fill: parent
contentWidth: availableWidth
ColumnLayout {
width: parent.width
spacing: 0
// 外观设置
GroupBox {
Layout.fillWidth: true
Layout.margins: 16
title: "外观"
ColumnLayout {
width: parent.width
spacing: 4
RowLayout {
Layout.fillWidth: true
Label { text: "深色模式"; Layout.fillWidth: true }
Switch { id: darkSwitch }
}
RowLayout {
Layout.fillWidth: true
Label { text: "主题色"; Layout.fillWidth: true }
ComboBox {
model: ["蓝色", "绿色", "橙色", "紫色"]
Layout.preferredWidth: 100
}
}
RowLayout {
Layout.fillWidth: true
Label { text: "字体大小"; Layout.fillWidth: true }
Slider {
from: 12; to: 20; value: 15
stepSize: 1
Layout.preferredWidth: 120
}
}
}
}
// 通知设置
GroupBox {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
title: "通知"
ColumnLayout {
width: parent.width
spacing: 4
Repeater {
model: ["接收推送通知", "邮件提醒", "声音提示", "震动反馈"]
delegate: RowLayout {
required property string modelData
required property int index
Layout.fillWidth: true
Label { text: modelData; Layout.fillWidth: true }
Switch { checked: index < 2 }
}
}
}
}
// 存储设置
GroupBox {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
title: "存储与数据"
ColumnLayout {
width: parent.width
spacing: 8
Label {
text: "已用空间:1.2 GB / 5 GB"
font.pixelSize: 13; color: "#666"
}
ProgressBar {
Layout.fillWidth: true
value: 0.24
}
Button {
Layout.fillWidth: true
text: "清理缓存"
onClicked: cacheDialog.open()
}
}
}
// 账号操作
GroupBox {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 16
title: "账号"
ColumnLayout {
width: parent.width
spacing: 8
Button {
Layout.fillWidth: true
text: "修改密码"
}
Button {
Layout.fillWidth: true
text: "退出登录"
onClicked: logoutDialog.open()
}
}
}
}
}
// 清理缓存确认对话框
Dialog {
id: cacheDialog
anchors.centerIn: parent
title: "清理缓存"
modal: true
standardButtons: Dialog.Ok | Dialog.Cancel
contentItem: Label {
text: "确认清理所有缓存数据?"
padding: 8
}
onAccepted: console.log("缓存已清理")
}
// 退出登录确认对话框
Dialog {
id: logoutDialog
anchors.centerIn: parent
title: "退出登录"
modal: true
standardButtons: Dialog.Yes | Dialog.No
contentItem: Label {
text: "确认退出当前账号?"
padding: 8
}
onAccepted: console.log("已退出登录")
}
}
总结
| 控件 | 用途 | 关键属性 |
|---|---|---|
Dial | 旋钮调节 | inputMode、stepSize |
Tumbler | 滚筒选择 | model、visibleItemCount |
TabBar + StackLayout | 选项卡切换 | currentIndex 双向绑定 |
ToolBar | 应用顶部工具栏 | 放在 header 属性 |
Dialog | 模态对话框 | standardButtons、modal |
Drawer | 侧滑抽屉导航 | edge、open() / close() |
Menu | 上下文菜单 | popup()、MenuSeparator |
ToolTip | 悬停提示 | delay、visible: hovered |
StackView | 页面栈前进/后退 | push() / pop() |
SwipeView | 横划页面切换 | 配合 PageIndicator 使用 |
background / contentItem | 自定义控件外观 | 替换任意一个,保留交互行为 |