适合人群: 已掌握 Qt Quick 基础视觉元素,想使用完整 UI 控件库的开发者
前言
上一篇我们用 Rectangle、Text、MouseArea 手工搭建了 UI 组件。但在实际开发中,按钮、输入框、复选框、滑块这些常见控件不需要从零造——Qt Quick Controls 模块提供了一套完整的、开箱即用的 UI 控件库。
本文系统介绍 Qt Quick Controls 的核心控件、控件解剖结构、内置样式,以及如何用 Layouts 模块管理控件的排列与尺寸。
一、什么是 Qt Quick Controls
QtQuick.Controls 是建立在 Qt Quick 之上的控件模块,提供了:
- 按钮类:
Button、CheckBox、RadioButton、Switch - 输入类:
TextField、TextArea、Slider、SpinBox、ComboBox - 容器类:
GroupBox、Frame、ScrollView、TabBar - 弹窗类:
Dialog、Popup、Menu、Drawer - 导航类:
StackView、SwipeView、PageIndicator - 显示类:
Label、ProgressBar、BusyIndicator
导入方式:
import QtQuick.Controls
二、控件的解剖结构
理解 Qt Quick Controls 的关键是理解每个控件由哪些部分组成。以 Button 为例:
Button
├── background ← 背景(Rectangle、图片等)
├── contentItem ← 内容区域(通常是 Text 或 Icon)
├── indicator ← 指示器(CheckBox 的勾选框等)
└── overlay ← 覆盖层(按下时的涟漪效果等)
这个结构意味着你可以单独替换任意部分来自定义外观,而不需要重写整个控件:
Button {
text: "自定义按钮"
// 只替换背景,保留其他默认行为
background: Rectangle {
radius: 8
color: parent.pressed ? "#2C72C7" : "#4A90E2"
border.width: 0
}
// 只替换文字样式
contentItem: Text {
text: parent.text
font.pixelSize: 15
font.bold: true
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
三、内置样式
Qt Quick Controls 提供了几套内置样式,无需任何代码即可改变所有控件的外观。
可用样式
| 样式名 | 特点 |
|---|---|
Basic | 极简风格,白色背景,适合自定义的起点 |
Fusion | 跨平台桌面风格,类 Qt Widgets 外观 |
Material | Google Material Design 风格 |
Universal | Windows Universal 风格 |
iOS | Apple iOS 风格(需在 iOS 平台) |
macOS | macOS 原生风格 |
Windows | Windows 原生风格 |
设置全局样式
方式一:在 main.cpp 中设置(推荐)
#include <QQuickStyle>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickStyle::setStyle("Material"); // 设置为 Material 风格
QQmlApplicationEngine engine;
// ...
}
方式二:通过环境变量
QT_QUICK_CONTROLS_STYLE=Material ./MyApp
方式三:在 qtquickcontrols2.conf 配置文件中设置
在项目根目录创建 qtquickcontrols2.conf,并在 CMakeLists.txt 中注册为资源:
[Controls]
Style=Material
[Material]
Theme=Light
Accent=Blue
Primary=BlueGrey
Material 样式示例
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
ApplicationWindow {
width: 360; height: 500
visible: true
// Material 样式全局配置
Material.theme: Material.Light
Material.accent: Material.Blue
Material.primary: Material.BlueGrey
Column {
anchors.centerIn: parent
spacing: 16
width: 280
Button {
width: parent.width
text: "普通按钮"
}
Button {
width: parent.width
text: "高亮按钮"
highlighted: true // Material 风格下显示强调色
}
Button {
width: parent.width
text: "扁平按钮"
flat: true
}
}
}
四、按钮类控件
4.1 Button
Button {
text: "提交"
enabled: true // 是否可点击
highlighted: false // 强调样式(Material 风格有明显效果)
flat: false // 扁平样式(无边框背景)
checkable: false // 是否可切换选中状态
icon.source: "images/send.png" // 图标
onClicked: console.log("提交")
onPressAndHold: console.log("长按")
}
4.2 CheckBox — 复选框
Column {
spacing: 8
CheckBox {
id: agreeCheck
text: "我已阅读并同意用户协议"
checked: false
onCheckedChanged: console.log("同意状态:" + checked)
}
CheckBox {
text: "订阅新闻邮件"
checked: true
}
Button {
text: "提交"
enabled: agreeCheck.checked // 绑定:勾选协议后才可提交
}
}
4.3 RadioButton — 单选框
同一 ButtonGroup 中的单选框互斥:
import QtQuick
import QtQuick.Controls
Column {
spacing: 8
Label {
text: "选择性别:"
font.bold: true
}
ButtonGroup {
id: genderGroup
}
RadioButton {
text: "男"
ButtonGroup.group: genderGroup
checked: true
}
RadioButton {
text: "女"
ButtonGroup.group: genderGroup
}
RadioButton {
text: "不愿透露"
ButtonGroup.group: genderGroup
}
Label {
text: "已选:" + genderGroup.checkedButton?.text
color: "#888"
font.pixelSize: 13
}
}
4.4 Switch — 开关
Column {
spacing: 12
Switch {
id: wifiSwitch
text: "Wi-Fi"
checked: true
onCheckedChanged: console.log("Wi-Fi:" + (checked ? "开" : "关"))
}
Switch {
text: "蓝牙"
checked: false
}
Switch {
text: "深色模式"
checked: false
}
}
五、输入类控件
5.1 TextField — 单行输入
Column {
spacing: 12
width: 280
TextField {
id: emailField
width: parent.width
placeholderText: "邮箱地址"
inputMethodHints: Qt.ImhEmailCharactersOnly // 键盘类型提示
validator: RegularExpressionValidator {
regularExpression: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/
}
}
TextField {
width: parent.width
placeholderText: "密码"
echoMode: TextInput.Password // 密码掩码
}
TextField {
width: parent.width
placeholderText: "手机号"
inputMethodHints: Qt.ImhDigitsOnly // 只允许数字键盘
maximumLength: 11
}
}
5.2 TextArea — 多行输入
ScrollView {
width: 300
height: 150
TextArea {
placeholderText: "请输入详细描述..."
wrapMode: TextArea.Wrap
font.pixelSize: 14
}
}
5.3 Slider — 滑块
Column {
spacing: 16
width: 300
// 水平滑块
Row {
spacing: 12
Label {
anchors.verticalCenter: parent.verticalCenter
text: "音量"
width: 40
}
Slider {
id: volumeSlider
width: 200
from: 0; to: 100; value: 70
stepSize: 1
}
Label {
anchors.verticalCenter: parent.verticalCenter
text: Math.round(volumeSlider.value)
width: 30
}
}
// 垂直滑块
Slider {
orientation: Qt.Vertical
height: 120
from: 0; to: 100; value: 50
}
}
5.4 SpinBox — 数字输入框
Row {
spacing: 12
Label {
anchors.verticalCenter: parent.verticalCenter
text: "数量:"
}
SpinBox {
from: 1
to: 99
value: 1
stepSize: 1
editable: true // 允许直接键盘输入
}
}
5.5 ComboBox — 下拉选择框
Column {
spacing: 12
width: 240
ComboBox {
width: parent.width
model: ["北京", "上海", "广州", "深圳", "杭州"]
onCurrentIndexChanged: console.log("选中:" + currentText)
}
// 可编辑的 ComboBox
ComboBox {
width: parent.width
editable: true
model: ListModel {
ListElement { text: "苹果" }
ListElement { text: "香蕉" }
ListElement { text: "橙子" }
}
onAccepted: {
if (find(editText) === -1)
model.append({ text: editText }) // 添加新选项
}
}
}
六、Layouts 布局模块
QtQuick.Layouts 提供了比 anchors 更强大的布局管理,特别适合需要自适应尺寸的 UI。
导入:
import QtQuick.Layouts
6.1 RowLayout — 水平布局
RowLayout {
width: 400
spacing: 8
Button { text: "取消" }
Item { Layout.fillWidth: true } // 弹性空间,把后面的按钮推到右边
Button { text: "确认"; highlighted: true }
}
6.2 ColumnLayout — 垂直布局
ColumnLayout {
width: 300
spacing: 12
Label { text: "用户名" }
TextField {
Layout.fillWidth: true // 填满父布局宽度
placeholderText: "请输入用户名"
}
Label { text: "密码" }
TextField {
Layout.fillWidth: true
echoMode: TextInput.Password
placeholderText: "请输入密码"
}
Button {
Layout.fillWidth: true
Layout.topMargin: 8
text: "登录"
highlighted: true
}
}
6.3 GridLayout — 网格布局
GridLayout {
columns: 2
columnSpacing: 12
rowSpacing: 12
width: 320
Label { text: "姓名:" }
TextField { Layout.fillWidth: true; placeholderText: "请输入姓名" }
Label { text: "手机:" }
TextField { Layout.fillWidth: true; placeholderText: "请输入手机号" }
Label { text: "城市:" }
ComboBox {
Layout.fillWidth: true
model: ["北京", "上海", "广州"]
}
// 跨列的按钮
Button {
Layout.columnSpan: 2
Layout.fillWidth: true
text: "提交"
highlighted: true
}
}
6.4 Layout 附加属性
在子元素上使用 Layout.* 属性控制其在布局中的行为:
RowLayout {
width: 400
Button {
text: "固定宽度"
Layout.preferredWidth: 100 // 期望宽度
Layout.minimumWidth: 80 // 最小宽度
Layout.maximumWidth: 120 // 最大宽度
}
TextField {
Layout.fillWidth: true // 填满剩余空间
Layout.preferredHeight: 40
}
Button {
text: "搜索"
Layout.alignment: Qt.AlignVCenter // 垂直居中对齐
}
}
七、综合示例:用户注册表单
整合本文所有知识点,构建一个完整的注册表单:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ApplicationWindow {
width: 400
height: 580
visible: true
title: "用户注册"
// 整体滚动支持
ScrollView {
anchors.fill: parent
contentWidth: availableWidth
ColumnLayout {
width: parent.width
spacing: 0
// 顶部标题区
Rectangle {
Layout.fillWidth: true
height: 100
color: "#4A90E2"
Column {
anchors.centerIn: parent
spacing: 4
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "创建账号"
font.pixelSize: 22
font.bold: true
color: "white"
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "加入我们,开始你的旅程"
font.pixelSize: 13
color: "#d0e8ff"
}
}
}
// 表单区域
ColumnLayout {
Layout.fillWidth: true
Layout.margins: 24
spacing: 16
// 姓名行
RowLayout {
Layout.fillWidth: true
spacing: 12
ColumnLayout {
Layout.fillWidth: true
spacing: 4
Label { text: "姓"; font.pixelSize: 13; color: "#555" }
TextField {
Layout.fillWidth: true
placeholderText: "姓氏"
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 4
Label { text: "名"; font.pixelSize: 13; color: "#555" }
TextField {
Layout.fillWidth: true
placeholderText: "名字"
}
}
}
// 邮箱
ColumnLayout {
Layout.fillWidth: true
spacing: 4
Label { text: "邮箱地址"; font.pixelSize: 13; color: "#555" }
TextField {
id: emailField
Layout.fillWidth: true
placeholderText: "example@email.com"
inputMethodHints: Qt.ImhEmailCharactersOnly
}
}
// 密码
ColumnLayout {
Layout.fillWidth: true
spacing: 4
Label { text: "密码"; font.pixelSize: 13; color: "#555" }
TextField {
id: passwordField
Layout.fillWidth: true
placeholderText: "至少 8 位字符"
echoMode: TextInput.Password
}
Label {
visible: passwordField.text.length > 0 && passwordField.text.length < 8
text: "密码长度不足 8 位"
font.pixelSize: 12
color: "#E24A4A"
}
}
// 城市选择
ColumnLayout {
Layout.fillWidth: true
spacing: 4
Label { text: "所在城市"; font.pixelSize: 13; color: "#555" }
ComboBox {
Layout.fillWidth: true
model: ["请选择城市", "北京", "上海", "广州", "深圳", "杭州", "成都"]
}
}
// 性别选择
ColumnLayout {
Layout.fillWidth: true
spacing: 4
Label { text: "性别"; font.pixelSize: 13; color: "#555" }
RowLayout {
ButtonGroup { id: genderGroup }
RadioButton {
text: "男"
ButtonGroup.group: genderGroup
checked: true
}
RadioButton {
text: "女"
ButtonGroup.group: genderGroup
}
}
}
// 接收通知
CheckBox {
id: notifyCheck
text: "接收产品更新通知"
checked: true
}
// 同意协议
CheckBox {
id: agreeCheck
text: "我已阅读并同意《用户协议》和《隐私政策》"
font.pixelSize: 13
}
// 注册按钮
Button {
Layout.fillWidth: true
Layout.topMargin: 8
text: "立即注册"
highlighted: true
enabled: agreeCheck.checked &&
emailField.text.length > 0 &&
passwordField.text.length >= 8
onClicked: console.log("注册成功!邮箱:" + emailField.text)
}
// 登录跳转
RowLayout {
Layout.alignment: Qt.AlignHCenter
Label {
text: "已有账号?"
font.pixelSize: 13
color: "#888"
}
Button {
text: "立即登录"
flat: true
font.pixelSize: 13
onClicked: console.log("跳转到登录页")
}
}
}
}
}
}
八、常见问题
Q:Layout.fillWidth 和 anchors.fill 有什么区别?
anchors.fill:用于anchors定位系统,将元素尺寸绑定到父元素Layout.fillWidth:用于Layouts布局系统,让元素占满布局中的剩余宽度
两套系统不能混用——在 RowLayout / ColumnLayout 的直接子元素上,用 Layout.* 属性,不要用 anchors(会产生冲突警告)。
Q:控件的默认尺寸从哪里来?
Qt Quick Controls 的每个控件都有 implicitWidth 和 implicitHeight,由控件内容自动计算。不设置 width/height 时控件使用 implicit 尺寸;放入 Layout 后可以用 Layout.fillWidth 覆盖。
Q:如何统一修改应用内所有按钮的字体大小?
使用 ApplicationWindow 上的 font 属性设置全局字体,所有控件会继承:
ApplicationWindow {
font.pixelSize: 15
font.family: "PingFang SC"
// ...
}
总结
| 控件 / 概念 | 用途 |
|---|---|
Button | 可点击按钮,支持图标、高亮、扁平样式 |
CheckBox | 复选框,独立勾选状态 |
RadioButton | 单选框,配合 ButtonGroup 实现互斥 |
Switch | 开关控件,适合设置页面 |
TextField | 单行输入,支持验证器和键盘类型提示 |
TextArea | 多行输入,配合 ScrollView 使用 |
Slider | 滑块,水平或垂直方向 |
SpinBox | 数字步进框 |
ComboBox | 下拉选择,支持可编辑模式 |
RowLayout | 水平自适应布局 |
ColumnLayout | 垂直自适应布局 |
GridLayout | 网格布局,支持跨行跨列 |
Layout.* 附加属性 | 控制子元素在布局中的尺寸和对齐 |
| 内置样式 | Basic、Material、Fusion 等,全局切换控件外观 |