适合人群: 已掌握 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
导入方式:
arduino
import QtQuick.Controls
二、控件的解剖结构
理解 Qt Quick Controls 的关键是理解每个控件由哪些部分组成。以 Button 为例:
css
Button
├── background ← 背景(Rectangle、图片等)
├── contentItem ← 内容区域(通常是 Text 或 Icon)
├── indicator ← 指示器(CheckBox 的勾选框等)
└── overlay ← 覆盖层(按下时的涟漪效果等)
这个结构意味着你可以单独替换任意部分来自定义外观,而不需要重写整个控件:
less
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 中设置(推荐)
arduino
#include <QQuickStyle>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickStyle::setStyle("Material"); // 设置为 Material 风格
QQmlApplicationEngine engine;
// ...
}
方式二:通过环境变量
ini
QT_QUICK_CONTROLS_STYLE=Material ./MyApp
方式三:在 qtquickcontrols2.conf 配置文件中设置
在项目根目录创建 qtquickcontrols2.conf,并在 CMakeLists.txt 中注册为资源:
ini
[Controls]
Style=Material
[Material]
Theme=Light
Accent=Blue
Primary=BlueGrey
Material 样式示例
less
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
arduino
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 --- 复选框
arduino
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 中的单选框互斥:
vbnet
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 --- 开关
arduino
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 --- 单行输入
less
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 --- 多行输入
less
ScrollView {
width: 300
height: 150
TextArea {
placeholderText: "请输入详细描述..."
wrapMode: TextArea.Wrap
font.pixelSize: 14
}
}
5.3 Slider --- 滑块
yaml
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 --- 数字输入框
yaml
Row {
spacing: 12
Label {
anchors.verticalCenter: parent.verticalCenter
text: "数量:"
}
SpinBox {
from: 1
to: 99
value: 1
stepSize: 1
editable: true // 允许直接键盘输入
}
}
5.5 ComboBox --- 下拉选择框
arduino
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。
导入:
arduino
import QtQuick.Layouts
6.1 RowLayout --- 水平布局
css
RowLayout {
width: 400
spacing: 8
Button { text: "取消" }
Item { Layout.fillWidth: true } // 弹性空间,把后面的按钮推到右边
Button { text: "确认"; highlighted: true }
}
6.2 ColumnLayout --- 垂直布局
yaml
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 --- 网格布局
css
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.* 属性控制其在布局中的行为:
arduino
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 // 垂直居中对齐
}
}
七、综合示例:用户注册表单
整合本文所有知识点,构建一个完整的注册表单:
css
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 属性设置全局字体,所有控件会继承:
css
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 等,全局切换控件外观 |