第十二章:QT常用控件与布局管理器

说实话,QT的控件体系,是我当年入门时觉得最"爽"的部分。为什么?因为拖拖拽拽就能出界面,比MFC那套舒服太多了。但后来我发现,真正的高手不是靠拖拽,而是靠对控件的深刻理解。

这一章,咱们就来聊聊QT里最常用的几个控件,以及那个让界面"活起来"的布局管理器。嗯,都是干货,我尽量讲得接地气些。

12.1 QWidget:一切控件的"祖宗"

QWidget是所有用户界面对象的基类。说白了,你看到的每个按钮、标签、输入框,本质上都是一个QWidget。它定义了窗口的基本行为:大小、位置、事件处理、样式设置。

我在项目中遇到过一个问题:自定义一个圆形按钮。当时第一反应是继承QPushButton,后来发现直接继承QWidget更灵活。因为QWidget给了你完全的绘制自由。

核心要点:QWidget是所有控件的父类。掌握它,你就掌握了QT界面编程的"根"。

// 创建一个QWidget作为顶层窗口
QWidget *widget = new QWidget;
widget->setWindowTitle("我的第一个窗口");
widget->resize(400, 300);
widget->show();

// 设置背景色
widget->setStyleSheet("background-color: lightblue;");

小技巧:QWidget的setAttribute(Qt::WA_StyledBackground)可以启用样式表背景。我刚开始经常忘记这步,结果样式死活不生效。

12.2 QLabel:不只是"标签"

QLabel,顾名思义就是显示文本或图片的标签。但你想想看,它真的只能当标签用吗?

其实QLabel支持富文本(HTML子集),可以显示超链接、图片、动画。我曾经用它做了一个简单的"关于"对话框,直接塞了一段HTML进去,效果还不错。

QLabel *label = new QLabel("这是<b>粗体</b>和<i>斜体</i>文本");
label->setTextFormat(Qt::RichText);  // 启用富文本

// 显示图片
QLabel *imageLabel = new QLabel;
imageLabel->setPixmap(QPixmap(":/images/logo.png"));
imageLabel->setScaledContents(true);  // 自适应大小

注意:QLabel默认不接收鼠标事件。如果你想让标签可点击,需要设置setOpenExternalLinks(true)或重写mousePressEvent。我曾经踩过这个坑,调试了半天才发现。

12.3 QPushButton:最常用的交互控件

QPushButton,按钮。每个界面都少不了它。但你真的会用吗?

除了点击事件,QPushButton还支持:

  • 快捷键:通过&符号设置,比如"&打开"会显示带下划线的"打开"
  • 图标:setIcon()可以设置图标
  • 菜单:setMenu()可以弹出下拉菜单
  • 默认按钮:按Enter键自动触发
QPushButton *btn = new QPushButton("&保存");
btn->setIcon(QIcon(":/icons/save.png"));
btn->setShortcut(QKeySequence("Ctrl+S"));  // 快捷键

// 连接点击信号
connect(btn, &QPushButton::clicked, [=]() {
    qDebug() << "保存按钮被点击了";
});

我个人习惯把按钮的点击事件统一用Lambda表达式处理,这样代码更紧凑,逻辑也更清晰。

12.4 QLineEdit:单行输入

QLineEdit是单行文本输入框。登录界面、搜索框、参数输入,到处都有它的身影。

常用的功能:

  • 占位文本:setPlaceholderText("请输入用户名")
  • 密码模式:setEchoMode(QLineEdit::Password)
  • 输入验证:setValidator()可以限制输入格式
  • 自动补全:setCompleter()提供下拉建议
QLineEdit *edit = new QLineEdit;
edit->setPlaceholderText("请输入手机号");
edit->setValidator(new QRegExpValidator(QRegExp("\\d{11}"), this));  // 只允许11位数字

// 文本变化信号
connect(edit, &QLineEdit::textChanged, [=](const QString &text) {
    qDebug() << "当前输入:" << text;
});

避坑指南:我曾经在做一个搜索框时,直接用textChanged做实时搜索。结果输入中文时,拼音组合过程也会触发搜索,导致界面卡顿。后来改用editingFinished信号,只在输入完成时触发,问题解决。

12.5 QTextEdit:多行富文本编辑器

QTextEdit支持多行文本、富文本、图片嵌入。它比QLineEdit强大得多,适合做聊天框、日志显示、文档编辑。

核心功能:

  • 纯文本模式:setPlainText()
  • HTML模式:setHtml()
  • 追加文本:append()自动换行
  • 只读模式:setReadOnly(true)
QTextEdit *editor = new QTextEdit;
editor->setHtml("<h1>标题</h1><p>这是<b>粗体</b>内容</p>");

// 追加日志
editor->append("<font color='red'>[错误]</font> 连接超时");

我记得有一次做日志查看器,用QTextEdit配合append(),每秒追加几百条日志都不卡。但要注意,如果日志量太大,建议限制最大行数,否则内存会爆。

12.6 QComboBox:下拉选择框

QComboBox就是下拉列表。省空间,适合选项较多的场景。

常用操作:

  • 添加项:addItem("选项1")
  • 当前项:currentText() / currentIndex()
  • 可编辑:setEditable(true)允许用户输入
  • 信号:currentIndexChanged(int) 或 currentTextChanged(QString)
QComboBox *combo = new QComboBox;
combo->addItem("北京");
combo->addItem("上海");
combo->addItem("广州");
combo->setCurrentIndex(0);  // 默认选中北京

connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged),
        [=](int index) {
    qDebug() << "选中了:" << combo->currentText();
});

注意:QComboBox的currentIndexChanged信号有重载(int和QString),用QOverload指定版本,否则编译会报错。我刚开始用的时候被这个坑过。

12.7 布局管理器:让界面"自适应"

布局管理器是QT界面编程的"灵魂"。没有它,你的窗口一拉伸,控件就乱成一团。

QT提供了三种核心布局:

布局类 特点 适用场景
QHBoxLayout 水平排列 按钮组、工具栏
QVBoxLayout 垂直排列 表单、列表
QGridLayout 网格排列 表格、计算器键盘
// 水平布局
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addWidget(new QPushButton("左"));
hLayout->addWidget(new QPushButton("中"));
hLayout->addWidget(new QPushButton("右"));

// 垂直布局
QVBoxLayout *vLayout = new QVBoxLayout;
vLayout->addWidget(new QLabel("用户名:"));
vLayout->addWidget(new QLineEdit);
vLayout->addWidget(new QPushButton("登录"));

// 网格布局
QGridLayout *grid = new QGridLayout;
grid->addWidget(new QPushButton("1"), 0, 0);
grid->addWidget(new QPushButton("2"), 0, 1);
grid->addWidget(new QPushButton("3"), 0, 2);
grid->addWidget(new QPushButton("4"), 1, 0);

个人经验:布局嵌套时,建议先用QWidget作为容器,再设置布局。这样结构清晰,也方便后续修改。我见过有人把所有控件直接塞到顶层布局里,结果想加个分组都无从下手。

12.8 知识体系总览

下面这张图,是我梳理的本章知识结构。你看一眼,心里就有数了。

QT常用控件与布局管理器 QT控件体系 QWidget(基类) QLabel(标签) QPushButton(按钮) QLineEdit(单行输入) QTextEdit(多行编辑) QComboBox(下拉框) QHBoxLayout(水平) QVBoxLayout(垂直) QGridLayout(网格) 布局嵌套 + 信号槽 = 灵活界面

你看,控件是"零件",布局管理器是"框架"。零件再好,没有框架也拼不出好界面。反过来,框架再牛,零件不行也白搭。两者相辅相成。

12.9 实战小例子:登录界面

光说不练假把式。咱们用刚学的知识,拼一个简单的登录界面。

// 创建窗口
QWidget *loginWindow = new QWidget;
loginWindow->setWindowTitle("登录");
loginWindow->resize(300, 200);

// 创建控件
QLabel *userLabel = new QLabel("用户名:");
QLineEdit *userEdit = new QLineEdit;
userEdit->setPlaceholderText("请输入用户名");

QLabel *pwdLabel = new QLabel("密码:");
QLineEdit *pwdEdit = new QLineEdit;
pwdEdit->setEchoMode(QLineEdit::Password);
pwdEdit->setPlaceholderText("请输入密码");

QPushButton *loginBtn = new QPushButton("登录");
QPushButton *cancelBtn = new QPushButton("取消");

// 布局
QGridLayout *grid = new QGridLayout;
grid->addWidget(userLabel, 0, 0);
grid->addWidget(userEdit, 0, 1);
grid->addWidget(pwdLabel, 1, 0);
grid->addWidget(pwdEdit, 1, 1);

QHBoxLayout *btnLayout = new QHBoxLayout;
btnLayout->addStretch();
btnLayout->addWidget(loginBtn);
btnLayout->addWidget(cancelBtn);

QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(grid);
mainLayout->addLayout(btnLayout);

loginWindow->setLayout(mainLayout);
loginWindow->show();

这个例子虽然简单,但包含了QLabel、QLineEdit、QPushButton三种控件,以及QGridLayout、QHBoxLayout、QVBoxLayout三种布局的嵌套使用。你想想看,是不是很典型?

核心总结:QT控件编程,记住三句话——选对控件、用好布局、连对信号。做到这三点,界面开发就稳了。

公众号:蓝海资料掘金营,微信deep3321