系列介绍

这是系列文章的第五篇,我将带你全面学习Qt提供的各种常用控件。掌握这些控件,你就能搭建出功能完善的桌面应用程序界面。

🎯 本篇你将学到:

  • 按钮类控件的使用

  • 输入类控件的使用

  • 显示类控件的使用

  • 列表和表格控件

  • 容器类控件

  • 实战:构建一个完整的设置对话框

🏆 系列进度:

  • ✅ 第1篇:Qt环境搭建与第一个Hello World

  • ✅ 第2篇:Qt信号槽机制详解

  • ✅ 第3篇:Qt布局管理器完全指南

  • ✅ 第4篇:Qt样式表QSS美化教程

  • 📝 第5篇:Qt常用控件全解析(本篇)

  • 🔜 第6篇:Qt对话框系统

让我们开始吧!


一、控件分类总览

Qt提供了丰富的控件库,按功能可以分为以下几类:

┌─────────────────────────────────────────────────────────────┐
│                        Qt控件体系                            │
├─────────────────────────────────────────────────────────────┤
│  按钮类        │  QPushButton, QToolButton, QRadioButton,   │
│               │  QCheckBox, QCommandLinkButton              │
├─────────────────────────────────────────────────────────────┤
│  输入类        │  QLineEdit, QTextEdit, QPlainTextEdit,     │
│               │  QSpinBox, QDoubleSpinBox, QComboBox,       │
│               │  QSlider, QDial, QDateTimeEdit              │
├─────────────────────────────────────────────────────────────┤
│  显示类        │  QLabel, QProgressBar, QLCDNumber,         │
│               │  QCalendarWidget, QWebEngineView            │
├─────────────────────────────────────────────────────────────┤
│  列表/表格类   │  QListWidget, QTableWidget, QTreeWidget,   │
│               │  QListView, QTableView, QTreeView           │
├─────────────────────────────────────────────────────────────┤
│  容器类        │  QGroupBox, QTabWidget, QStackedWidget,    │
│               │  QScrollArea, QToolBox, QSplitter           │
└─────────────────────────────────────────────────────────────┘

二、按钮类控件

2.1 QPushButton(普通按钮)

最常用的按钮,用于触发操作。

// 基础用法
QPushButton *btn = new QPushButton("确定", this);
btn->setFixedSize(80, 30);

// 设置图标
btn->setIcon(QIcon(":/icons/save.png"));
btn->setIconSize(QSize(20, 20));

// 设置快捷键(Alt+O)
btn->setText("确定(&O)");

// 设置工具提示
btn->setToolTip("点击保存文件");

// 启用/禁用
btn->setEnabled(false);

// 信号
connect(btn, &QPushButton::clicked, this, &MainWindow::onOkClicked);
// 注意:clicked和pressed的区别
// clicked: 按下+释放才触发
// pressed: 按下立即触发
connect(btn, &QPushButton::pressed, this, &MainWindow::onPress);
connect(btn, &QPushButton::released, this, &MainWindow::onRelease);

效果:

┌─────────────────────┐
│  📁 保存(&O)        │  ← 图标 + 文字 + 快捷键提示
└─────────────────────┘
  鼠标悬停时显示:点击保存文件

2.2 QToolButton(工具按钮)

常用于工具栏,可以只显示图标。

QToolButton *toolBtn = new QToolButton(this);

// 只显示图标
toolBtn->setIcon(QIcon(":/icons/edit.png"));
toolBtn->setIconSize(QSize(24, 24));

// 设置样式
toolBtn->setToolButtonStyle(Qt::ToolButtonIconOnly);  // 只显示图标
// toolBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); // 图标+文字
// toolBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);  // 文字在图标下

// 设置弹出菜单
QMenu *menu = new QMenu(toolBtn);
menu->addAction("复制");
menu->addAction("粘贴");
toolBtn->setMenu(menu);
toolBtn->setPopupMode(QToolButton::MenuButtonPopup);

2.3 QRadioButton(单选按钮)

一组单选按钮中,只能选择一个。

// 创建一组单选按钮
QRadioButton *rbMale = new QRadioButton("男", this);
QRadioButton *rbFemale = new QRadioButton("女", this);
QRadioButton *rbOther = new QRadioButton("其他", this);

// 默认选中
rbMale->setChecked(true);

// 获取选中状态
if (rbMale->isChecked()) {
    qDebug() << "选择了男";
}

// 信号:选中状态改变时
connect(rbMale, &QRadioButton::toggled, this, [](bool checked) {
    qDebug() << "男选中状态:" << checked;
});

重要: 同一父窗口下的单选按钮会自动互斥。如果需要分组,使用QGroupBox:

QGroupBox *group1 = new QGroupBox("性别");
QVBoxLayout *layout1 = new QVBoxLayout(group1);
layout1->addWidget(rbMale);
layout1->addWidget(rbFemale);

QGroupBox *group2 = new QGroupBox("学历");
QVBoxLayout *layout2 = new QVBoxLayout(group2);
layout2->addWidget(new QRadioButton("本科"));
layout2->addWidget(new QRadioButton("硕士"));
layout2->addWidget(new QRadioButton("博士"));
// 这两组单选按钮互不影响

2.4 QCheckBox(复选框)

可以多选,有三种状态(选中、未选中、半选)。

QCheckBox *cbAgree = new QCheckBox("我同意用户协议", this);
QCheckBox *cbRemember = new QCheckBox("记住密码", this);

// 设置三态模式(用于树形结构中的部分选中)
QCheckBox *cbChildren = new QCheckBox("全选", this);
cbChildren->setTristate(true);  // 启用三态

// 获取状态
if (cbAgree->isChecked()) {
    qDebug() << "已同意";
}

// 信号
connect(cbAgree, &QCheckBox::stateChanged, this, [](int state) {
    // Qt::Checked     = 2  已选中
    // Qt::Unchecked   = 0  未选中
    // Qt::PartiallyChecked = 1  部分选中
    if (state == Qt::Checked) {
        qDebug() << "已勾选";
    }
});

三、输入类控件

3.1 QLineEdit(单行输入框)

QLineEdit *edit = new QLineEdit(this);

// 设置占位文本
edit->setPlaceholderText("请输入用户名");

// 设置文本
edit->setText("默认文字");

// 获取文本
QString text = edit->text();

// 设置输入限制
edit->setMaxLength(20);                    // 最大长度
edit->setEchoMode(QLineEdit::Password);    // 密码模式
// QLineEdit::Normal     正常显示
// QLineEdit::Password   显示为●
// QLineEdit::NoEcho     不显示任何字符

// 设置输入掩码
edit->setInputMask("999-9999-9999");       // 手机号格式

// 设置验证器
QIntValidator *validator = new QIntValidator(0, 100, this);
edit->setValidator(validator);              // 只允许输入0-100的数字

// 清空按钮(Qt 5.2+)
edit->setClearButtonEnabled(true);

// 信号
connect(edit, &QLineEdit::textChanged, this, [](const QString &text) {
    qDebug() << "文本改变:" << text;
});
connect(edit, &QLineEdit::returnPressed, this, []() {
    qDebug() << "按下回车键";
});

EchoMode效果对比:

┌─────────────────────┐     ┌─────────────────────┐
│ 张三                │     │ ●●●●                │
│ Normal              │     │ Password            │
└─────────────────────┘     └─────────────────────┘

┌─────────────────────┐     ┌─────────────────────┐
│ (空白)              │     │ 123-4567-8901       │
│ NoEcho              │     │ InputMask           │
└─────────────────────┘     └─────────────────────┘

3.2 QTextEdit(多行富文本输入框)

QTextEdit *textEdit = new QTextEdit(this);

// 设置文本
textEdit->setPlainText("普通文本");     // 纯文本
textEdit->setHtml("<b>粗体</b>文本");   // HTML格式

// 获取文本
QString plainText = textEdit->toPlainText();
QString html = textEdit->toHtml();

// 设置字体
QFont font("微软雅黑", 12);
textEdit->setFont(font);

// 设置只读
textEdit->setReadOnly(true);

// 自动换行
textEdit->setLineWrapMode(QTextEdit::WidgetWidth);

// 添加内容
textEdit->append("追加一行文字");

// 清空
textEdit->clear();

3.3 QPlainTextEdit(多行纯文本输入框)

比QTextEdit更轻量,适合显示大量纯文本。

QPlainTextEdit *plainEdit = new QPlainTextEdit(this);

// 设置最大行数(用于日志显示)
plainEdit->setMaximumBlockCount(1000);  // 最多保存1000行

// 自动滚动到底部
plainEdit->moveCursor(QTextCursor::End);

3.4 QSpinBox 和 QDoubleSpinBox(数字输入框)

// 整数输入
QSpinBox *spinBox = new QSpinBox(this);
spinBox->setRange(1, 100);      // 范围1-100
spinBox->setValue(50);          // 默认值
spinBox->setSingleStep(5);      // 步长
spinBox->setSuffix(" 个");       // 后缀
spinBox->setPrefix("数量: ");    // 前缀

// 小数输入
QDoubleSpinBox *doubleSpin = new QDoubleSpinBox(this);
doubleSpin->setRange(0.0, 100.0);
doubleSpin->setDecimals(2);      // 小数位数
doubleSpin->setValue(25.5);
doubleSpin->setSingleStep(0.5);

// 信号
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), 
        this, [](int value) {
    qDebug() << "值改变:" << value;
});

效果:

┌─────────────────────────┐
│ 数量: 50 个      ▲      │
│              ▼          │
└─────────────────────────┘

3.5 QComboBox(下拉选择框)

QComboBox *combo = new QComboBox(this);

// 添加选项
combo->addItem("选项1");
combo->addItem("选项2");
combo->addItems({"选项3", "选项4", "选项5"});

// 添加带图标的选项
combo->addItem(QIcon(":/icons/star.png"), "收藏");

// 设置当前选中
combo->setCurrentIndex(0);      // 按索引
combo->setCurrentText("选项2");  // 按文本

// 获取选中
int index = combo->currentIndex();
QString text = combo->currentText();

// 可编辑(允许用户输入)
combo->setEditable(true);

// 自动补全
combo->setEditable(true);
QCompleter *completer = new QCompleter(combo->model(), combo);
completer->setFilterMode(Qt::MatchContains);
combo->setCompleter(completer);

// 信号
connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged),
        this, [](int index) {
    qDebug() << "切换到索引:" << index;
});

3.6 QSlider(滑动条)

QSlider *slider = new QSlider(Qt::Horizontal, this);  // 水平
// QSlider *slider = new QSlider(Qt::Vertical, this); // 垂直

slider->setRange(0, 100);
slider->setValue(50);
slider->setTickPosition(QSlider::TicksBelow);    // 刻度位置
slider->setTickInterval(10);                      // 刻度间隔

// 信号
connect(slider, &QSlider::valueChanged, this, [](int value) {
    qDebug() << "滑动到:" << value;
});
connect(slider, &QSlider::sliderPressed, this, []() {
    qDebug() << "开始滑动";
});
connect(slider, &QSlider::sliderReleased, this, []() {
    qDebug() << "滑动结束";
});

效果:

水平滑动条:
├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
  0  10  20  30  40  50  60  70  80  90 100
  ●━━━━━━━━━━━━━━━━━━━━━○
      当前值:65

3.7 QDateTimeEdit(日期时间选择)

// 日期选择
QDateEdit *dateEdit = new QDateEdit(this);
dateEdit->setDate(QDate::currentDate());
dateEdit->setCalendarPopup(true);  // 弹出日历

// 时间选择
QTimeEdit *timeEdit = new QTimeEdit(this);
timeEdit->setTime(QTime::currentTime());

// 日期时间选择
QDateTimeEdit *dtEdit = new QDateTimeEdit(this);
dtEdit->setDateTime(QDateTime::currentDateTime());
dtEdit->setDisplayFormat("yyyy-MM-dd HH:mm:ss");  // 显示格式
dtEdit->setMinimumDateTime(QDateTime::currentDateTime());
dtEdit->setMaximumDateTime(QDateTime::currentDateTime().addDays(30));

四、显示类控件

4.1 QLabel(标签)

QLabel *label = new QLabel(this);

// 显示文本
label->setText("Hello World");

// 显示HTML
label->setText("<b>粗体</b> <font color=red>红色</font>");

// 显示图片
QPixmap pixmap(":/images/logo.png");
label->setPixmap(pixmap);
label->setScaledContents(true);  // 缩放图片适应标签

// 显示动画
QMovie *movie = new QMovie(":/images/loading.gif");
label->setMovie(movie);
movie->start();

// 设置对齐
label->setAlignment(Qt::AlignCenter);
// Qt::AlignLeft | Qt::AlignCenter | Qt::AlignRight
// Qt::AlignTop | Qt::AlignBottom

// 自动换行
label->setWordWrap(true);

// 设置工具提示
label->setToolTip("这是一段提示");

// 设置伙伴(快捷键关联)
QLabel *nameLabel = new QLabel("用户名(&N):");
QLineEdit *nameEdit = new QLineEdit;
nameLabel->setBuddy(nameEdit);  // Alt+N 聚焦到输入框

4.2 QProgressBar(进度条)

QProgressBar *progressBar = new QProgressBar(this);

// 设置范围
progressBar->setRange(0, 100);
progressBar->setValue(0);

// 设置样式(百分比/无文字)
progressBar->setFormat("%p%");    // 显示百分比(默认)
progressBar->setFormat("%v / %m"); // 显示当前值/最大值
progressBar->setFormat("");        // 不显示文字

// 重置方向
progressBar->setOrientation(Qt::Vertical);  // 垂直方向

// 设置倒序
progressBar->setInvertedAppearance(true);

// 更新进度
for (int i = 0; i <= 100; i++) {
    progressBar->setValue(i);
    QThread::msleep(10);
}

// 不确定进度(不知道具体进度时使用)
progressBar->setRange(0, 0);  // 显示忙碌动画

4.3 QCalendarWidget(日历控件)

QCalendarWidget *calendar = new QCalendarWidget(this);

// 设置当前日期
calendar->setSelectedDate(QDate::currentDate());

// 设置日期范围
calendar->setMinimumDate(QDate(2024, 1, 1));
calendar->setMaximumDate(QDate(2025, 12, 31));

// 设置首个星期几
calendar->setFirstDayOfWeek(Qt::Monday);  // 星期一为第一天

// 设置网格可见
calendar->setGridVisible(true);

// 设置日期格式
calendar->setDateEditEnabled(true);

// 信号
connect(calendar, &QCalendarWidget::selectionChanged, this, [calendar]() {
    QDate date = calendar->selectedDate();
    qDebug() << "选中日期:" << date.toString("yyyy-MM-dd");
});

connect(calendar, &QCalendarWidget::clicked, this, [](const QDate &date) {
    qDebug() << "点击日期:" << date.toString();
});

五、列表/表格类控件

5.1 QListWidget(列表控件)

QListWidget *listWidget = new QListWidget(this);

// 添加项目
listWidget->addItem("项目1");
listWidget->addItem(new QListWidgetItem("项目2"));

// 添加带图标的项目
QListWidgetItem *item = new QListWidgetItem("项目3");
item->setIcon(QIcon(":/icons/file.png"));
listWidget->addItem(item);

// 批量添加
listWidget->addItems({"项目4", "项目5", "项目6"});

// 设置选择模式
listWidget->setSelectionMode(QAbstractItemView::SingleSelection);     // 单选
// listWidget->setSelectionMode(QAbstractItemView::MultiSelection);   // 多选
// listWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); // Ctrl多选

// 设置排序
listWidget->setSortingEnabled(true);

// 获取选中项
QListWidgetItem *current = listWidget->currentItem();
QString text = current->text();

// 获取所有选中项
QList<QListWidgetItem*> selected = listWidget->selectedItems();

// 删除项目
delete listWidget->takeItem(row);  // 删除指定行
listWidget->clear();                // 清空所有

// 信号
connect(listWidget, &QListWidget::currentItemChanged, 
        this, [](QListWidgetItem *current, QListWidgetItem *previous) {
    qDebug() << "从" << (previous ? previous->text() : "无") 
             << "切换到" << current->text();
});

connect(listWidget, &QListWidget::itemDoubleClicked, 
        this, [](QListWidgetItem *item) {
    qDebug() << "双击:" << item->text();
});

5.2 QTableWidget(表格控件)

QTableWidget *table = new QTableWidget(this);

// 设置行列数
table->setRowCount(5);
table->setColumnCount(3);

// 设置表头
QStringList headers = {"姓名", "年龄", "城市"};
table->setHorizontalHeaderLabels(headers);

// 设置内容
table->setItem(0, 0, new QTableWidgetItem("张三"));
table->setItem(0, 1, new QTableWidgetItem("25"));
table->setItem(0, 2, new QTableWidgetItem("北京"));

table->setItem(1, 0, new QTableWidgetItem("李四"));
table->setItem(1, 1, new QTableWidgetItem("30"));
table->setItem(1, 2, new QTableWidgetItem("上海"));

// 设置对齐方式
QTableWidgetItem *item = table->item(0, 1);
item->setTextAlignment(Qt::AlignCenter);

// 设置不可编辑
item->setFlags(item->flags() & ~Qt::ItemIsEditable);

// 设置列宽
table->setColumnWidth(0, 100);
table->resizeColumnToContents(1);     // 自适应内容
table->horizontalHeader()->setStretchLastSection(true); // 最后一列拉伸

// 设置行高
table->setRowHeight(0, 40);

// 选择模式
table->setSelectionBehavior(QAbstractItemView::SelectRows);  // 选择整行
table->setSelectionMode(QAbstractItemView::SingleSelection); // 单选

// 获取选中行
int row = table->currentRow();
QString name = table->item(row, 0)->text();

// 信号
connect(table, &QTableWidget::cellClicked, this, [](int row, int col) {
    qDebug() << "点击单元格:" << row << col;
});

connect(table, &QTableWidget::itemChanged, this, [](QTableWidgetItem *item) {
    qDebug() << "内容改变:" << item->text();
});

5.3 QTreeWidget(树形控件)

QTreeWidget *tree = new QTreeWidget(this);

// 设置表头
tree->setHeaderLabel("文件列表");

// 创建顶层项目
QTreeWidgetItem *rootItem = new QTreeWidgetItem(tree);
rootItem->setText(0, "我的电脑");
rootItem->setIcon(0, QIcon(":/icons/computer.png"));

// 添加子项目
QTreeWidgetItem *cItem = new QTreeWidgetItem(rootItem);
cItem->setText(0, "C盘");

QTreeWidgetItem *dItem = new QTreeWidgetItem(rootItem);
dItem->setText(0, "D盘");

// 添加孙子项目
QTreeWidgetItem *docItem = new QTreeWidgetItem(cItem);
docItem->setText(0, "Documents");

QTreeWidgetItem *downloadItem = new QTreeWidgetItem(cItem);
downloadItem->setText(0, "Downloads");

// 展开所有项目
tree->expandAll();

// 设置项目可展开(显示箭头)
cItem->setExpanded(true);

// 信号
connect(tree, &QTreeWidget::itemClicked, this, [](QTreeWidgetItem *item, int col) {
    qDebug() << "点击:" << item->text(col);
});

connect(tree, &QTreeWidget::itemDoubleClicked, this, [](QTreeWidgetItem *item, int col) {
    qDebug() << "双击打开:" << item->text(col);
});

树形结构效果:

▼ 📁 我的电脑
  ├─ 📁 C盘
  │   ├─ 📁 Documents
  │   └─ 📁 Downloads
  └─ 📁 D盘

六、容器类控件

6.1 QGroupBox(分组框)

QGroupBox *groupBox = new QGroupBox("用户信息", this);

QVBoxLayout *layout = new QVBoxLayout(groupBox);
layout->addWidget(new QLabel("姓名:"));
layout->addWidget(new QLineEdit);
layout->addWidget(new QLabel("年龄:"));
layout->addWidget(new QSpinBox);

// 设置复选框(可选启用/禁用分组内容)
groupBox->setCheckable(true);
groupBox->setChecked(true);
// 取消勾选时,分组内所有控件被禁用

6.2 QTabWidget(标签页)

QTabWidget *tabWidget = new QTabWidget(this);

// 创建页面
QWidget *page1 = new QWidget;
QVBoxLayout *layout1 = new QVBoxLayout(page1);
layout1->addWidget(new QLabel("这是第一个标签页"));
layout1->addWidget(new QPushButton("按钮1"));
tabWidget->addTab(page1, "页面1");

// 第二个页面
QWidget *page2 = new QWidget;
QVBoxLayout *layout2 = new QVBoxLayout(page2);
layout2->addWidget(new QLabel("这是第二个标签页"));
layout2->addWidget(new QPushButton("按钮2"));
tabWidget->addTab(page2, "页面2");

// 添加带图标的标签页
tabWidget->addTab(new QWidget, QIcon(":/icons/settings.png"), "设置");

// 设置标签位置
tabWidget->setTabPosition(QTabWidget::North);  // 顶部(默认)
// QTabWidget::South    底部
// QTabWidget::West     左侧
// QTabWidget::East     右侧

// 设置标签形状
tabWidget->setTabShape(QTabWidget::Rounded);  // 圆角(默认)
// QTabWidget::Triangular  三角形

// 设置是否可关闭
tabWidget->setTabsClosable(true);
connect(tabWidget, &QTabWidget::tabCloseRequested, this, [tabWidget](int index) {
    tabWidget->removeTab(index);
});

// 信号
connect(tabWidget, &QTabWidget::currentChanged, this, [](int index) {
    qDebug() << "切换到标签页:" << index;
});

6.3 QStackedWidget(堆叠窗口)

一次只显示一个页面,适合做向导界面。

QStackedWidget *stacked = new QStackedWidget(this);

// 创建多个页面
QWidget *page1 = new QWidget;
page1->setStyleSheet("background-color: red;");

QWidget *page2 = new QWidget;
page2->setStyleSheet("background-color: green;");

QWidget *page3 = new QWidget;
page3->setStyleSheet("background-color: blue;");

stacked->addWidget(page1);
stacked->addWidget(page2);
stacked->addWidget(page3);

// 切换页面
stacked->setCurrentIndex(0);  // 按索引
stacked->setCurrentWidget(page2);  // 按控件

// 导航按钮
QPushButton *prevBtn = new QPushButton("上一步");
QPushButton *nextBtn = new QPushButton("下一步");

connect(prevBtn, &QPushButton::clicked, [stacked]() {
    int index = stacked->currentIndex();
    if (index > 0) stacked->setCurrentIndex(index - 1);
});

connect(nextBtn, &QPushButton::clicked, [stacked]() {
    int index = stacked->currentIndex();
    if (index < stacked->count() - 1) stacked->setCurrentIndex(index + 1);
});

6.4 QScrollArea(滚动区域)

QScrollArea *scrollArea = new QScrollArea(this);

// 创建一个很大的内容控件
QWidget *content = new QWidget;
QVBoxLayout *layout = new QVBoxLayout(content);
for (int i = 0; i < 50; i++) {
    layout->addWidget(new QLabel(QString("第%1行").arg(i)));
}
content->setMinimumHeight(2000);  // 内容高度超过窗口

// 设置滚动区域
scrollArea->setWidget(content);
scrollArea->setWidgetResizable(true);  // 是否自适应
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);

6.5 QSplitter(可拖动分割条)

// 水平分割
QSplitter *hSplitter = new QSplitter(Qt::Horizontal, this);
hSplitter->addWidget(new QTextEdit);
hSplitter->addWidget(new QTextEdit);
hSplitter->setSizes({300, 500});  // 设置初始大小

// 垂直分割
QSplitter *vSplitter = new QSplitter(Qt::Vertical, this);
vSplitter->addWidget(new QListWidget);
vSplitter->addWidget(new QTextEdit);

// 嵌套分割
QSplitter *mainSplitter = new QSplitter(Qt::Horizontal, this);
mainSplitter->addWidget(new QListWidget);  // 左侧
mainSplitter->addWidget(vSplitter);        // 右侧(垂直分割)

效果:

┌───────┬─────────────────────────────┐
│       │                             │
│ 列表  │         内容区域             │
│       │                             │
│       ├─────────────────────────────┤
│       │         底部区域             │
└───────┴─────────────────────────────┘
   ↑              ↑
可拖动分割线   可拖动分割线

七、实战:完整的设置对话框

class SettingsDialog : public QDialog
{
    Q_OBJECT

public:
    SettingsDialog(QWidget *parent = nullptr) : QDialog(parent)
    {
        setWindowTitle("设置");
        setModal(true);
        resize(500, 400);
        
        setupUI();
        loadSettings();
    }

private:
    void setupUI()
    {
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        
        // 创建标签页
        QTabWidget *tabWidget = new QTabWidget;
        
        // ========== 常规设置 ==========
        QWidget *generalWidget = new QWidget;
        QFormLayout *generalLayout = new QFormLayout(generalWidget);
        
        // 语言选择
        m_langCombo = new QComboBox;
        m_langCombo->addItems({"简体中文", "English"});
        generalLayout->addRow("语言:", m_langCombo);
        
        // 启动时检查更新
        m_checkUpdate = new QCheckBox("启动时检查更新");
        generalLayout->addRow("", m_checkUpdate);
        
        // 主题选择
        m_themeCombo = new QComboBox;
        m_themeCombo->addItems({"深色主题", "浅色主题"});
        generalLayout->addRow("主题:", m_themeCombo);
        
        // ========== PDF设置 ==========
        QWidget *pdfWidget = new QWidget;
        QFormLayout *pdfLayout = new QFormLayout(pdfWidget);
        
        // 默认压缩质量
        m_compressQuality = new QSlider(Qt::Horizontal);
        m_compressQuality->setRange(1, 100);
        m_compressQuality->setValue(70);
        m_compressQuality->setTickPosition(QSlider::TicksBelow);
        m_compressQuality->setTickInterval(10);
        
        m_qualityLabel = new QLabel("70%");
        QHBoxLayout *qualityLayout = new QHBoxLayout;
        qualityLayout->addWidget(m_compressQuality);
        qualityLayout->addWidget(m_qualityLabel);
        
        pdfLayout->addRow("默认压缩质量:", qualityLayout);
        
        // 输出目录
        m_outputDir = new QLineEdit;
        m_outputDir->setReadOnly(true);
        QPushButton *browseBtn = new QPushButton("浏览");
        QHBoxLayout *dirLayout = new QHBoxLayout;
        dirLayout->addWidget(m_outputDir);
        dirLayout->addWidget(browseBtn);
        pdfLayout->addRow("默认输出目录:", dirLayout);
        
        // ========== 高级设置 ==========
        QWidget *advancedWidget = new QWidget;
        QVBoxLayout *advancedLayout = new QVBoxLayout(advancedWidget);
        
        // 日志级别
        m_logLevel = new QComboBox;
        m_logLevel->addItems({"调试", "信息", "警告", "错误"});
        
        QFormLayout *formLayout = new QFormLayout;
        formLayout->addRow("日志级别:", m_logLevel);
        advancedLayout->addLayout(formLayout);
        
        // 功能开关
        m_enableAutoSave = new QCheckBox("启用自动保存");
        m_enableBackup = new QCheckBox("输出文件时备份原文件");
        advancedLayout->addWidget(m_enableAutoSave);
        advancedLayout->addWidget(m_enableBackup);
        advancedLayout->addStretch();
        
        // 添加标签页
        tabWidget->addTab(generalWidget, "常规");
        tabWidget->addTab(pdfWidget, "PDF");
        tabWidget->addTab(advancedWidget, "高级");
        
        // ========== 按钮 ==========
        QDialogButtonBox *buttonBox = new QDialogButtonBox(
            QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply
        );
        
        // 连接信号槽
        connect(m_compressQuality, &QSlider::valueChanged, this, [this](int value) {
            m_qualityLabel->setText(QString("%1%").arg(value));
        });
        
        connect(browseBtn, &QPushButton::clicked, this, [this]() {
            QString dir = QFileDialog::getExistingDirectory(this, "选择输出目录");
            if (!dir.isEmpty()) {
                m_outputDir->setText(dir);
            }
        });
        
        connect(buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::saveAndClose);
        connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
        connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked,
                this, &SettingsDialog::applySettings);
        
        // 组装布局
        mainLayout->addWidget(tabWidget);
        mainLayout->addWidget(buttonBox);
    }
    
    void loadSettings()
    {
        QSettings settings("MyCompany", "PDFToolbox");
        m_langCombo->setCurrentIndex(settings.value("language", 0).toInt());
        m_checkUpdate->setChecked(settings.value("checkUpdate", true).toBool());
        m_themeCombo->setCurrentIndex(settings.value("theme", 0).toInt());
        m_compressQuality->setValue(settings.value("compressQuality", 70).toInt());
        m_outputDir->setText(settings.value("outputDir", QDir::homePath()).toString());
        m_logLevel->setCurrentIndex(settings.value("logLevel", 1).toInt());
        m_enableAutoSave->setChecked(settings.value("autoSave", true).toBool());
        m_enableBackup->setChecked(settings.value("backup", true).toBool());
    }
    
    void saveSettings()
    {
        QSettings settings("MyCompany", "PDFToolbox");
        settings.setValue("language", m_langCombo->currentIndex());
        settings.setValue("checkUpdate", m_checkUpdate->isChecked());
        settings.setValue("theme", m_themeCombo->currentIndex());
        settings.setValue("compressQuality", m_compressQuality->value());
        settings.setValue("outputDir", m_outputDir->text());
        settings.setValue("logLevel", m_logLevel->currentIndex());
        settings.setValue("autoSave", m_enableAutoSave->isChecked());
        settings.setValue("backup", m_enableBackup->isChecked());
    }
    
    void applySettings()
    {
        saveSettings();
        // 发出信号通知主窗口应用新设置
        emit settingsApplied();
    }
    
    void saveAndClose()
    {
        saveSettings();
        accept();
    }
    
signals:
    void settingsApplied();

private:
    QComboBox *m_langCombo;
    QCheckBox *m_checkUpdate;
    QComboBox *m_themeCombo;
    QSlider *m_compressQuality;
    QLabel *m_qualityLabel;
    QLineEdit *m_outputDir;
    QComboBox *m_logLevel;
    QCheckBox *m_enableAutoSave;
    QCheckBox *m_enableBackup;
};

八、控件选择指南

需求场景 推荐控件 说明
触发操作 QPushButton 最常用
工具栏按钮 QToolButton 可只显示图标
二选一 QRadioButton 互斥
多选 QCheckBox 可独立选择
单行输入 QLineEdit 用户名、密码等
多行输入 QTextEdit/QPlainTextEdit 备注、日志
数字输入 QSpinBox/QDoubleSpinBox 年龄、价格
下拉选择 QComboBox 性别、城市
范围选择 QSlider 音量、亮度
日期选择 QDateTimeEdit 生日、截止日期
列表展示 QListWidget 简单列表
表格展示 QTableWidget 多列数据
树形展示 QTreeWidget 目录结构
页面切换 QTabWidget 标签页
向导界面 QStackedWidget 安装向导

九、本课小结

通过本篇学习,你掌握了:

✅ 按钮类控件:QPushButton、QRadioButton、QCheckBox
✅ 输入类控件:QLineEdit、QTextEdit、QSpinBox、QComboBox
✅ 显示类控件:QLabel、QProgressBar、QCalendarWidget
✅ 列表/表格类控件:QListWidget、QTableWidget、QTreeWidget
✅ 容器类控件:QGroupBox、QTabWidget、QSplitter
✅ 实战:完整的设置对话框

下一课预告:

第6篇「Qt对话框系统」——我们将学习Qt中各种对话框的使用,包括消息框、文件对话框、输入对话框,以及如何创建自定义对话框!


系列进度

状态 篇数 文章标题
✅ 已完成 第1篇 Qt环境搭建与第一个Hello World
✅ 已完成 第2篇 Qt信号槽机制详解
✅ 已完成 第3篇 Qt布局管理器完全指南
✅ 已完成 第4篇 Qt样式表QSS美化教程
✅ 已完成 第5篇 Qt常用控件全解析
🔜 待发布 第6篇 Qt对话框系统

如果觉得有用,欢迎点赞、收藏、关注

下期见!👋

更多推荐