4、Qt Widgets基础:QWidget、QLabel、QPushButton、QLineEdit、布局管理器
好,咱们今天来啃一块硬骨头——也是Qt桌面开发的基石。
说白了,你写的任何一个Qt界面,本质上都是在跟QWidget打交道。它就像乐高积木里那块最基础的底板,所有按钮、标签、输入框,都是它的子类。我刚开始学Qt那会儿,总觉得这玩意儿抽象,后来做项目多了才明白——理解了QWidget,你就理解了Qt界面的一半。
4.1 QWidget:一切控件的爹
QWidget是所有用户界面对象的基类。它本身就是一个空白矩形区域,可以接收鼠标、键盘事件,也可以被绘制。
嗯,这里要注意:QWidget可以独立存在,也可以作为其他控件的容器。你想想看,一个对话框、一个主窗口,本质上都是QWidget的派生类。
核心要点:QWidget负责管理子控件的布局、事件分发和绘制。你不需要每次都直接继承QWidget,但必须理解它的属性体系。
我在项目中遇到过一个问题:有个同事直接new了一个QWidget,没设置大小就show出来了。结果窗口小到几乎看不见。这就是典型的「忘了设置几何尺寸」。
// 创建一个QWidget并设置大小
QWidget *widget = new QWidget;
widget->setWindowTitle("我的第一个Widget");
widget->resize(400, 300); // 设置宽400,高300
widget->show();
常用的QWidget属性,我列个表,你记一下:
| 属性 | 类型 | 说明 |
|---|---|---|
| geometry() | QRect | 控件的位置和大小(相对于父控件) |
| size() | QSize | 控件的宽高 |
| pos() | QPoint | 控件左上角坐标 |
| enabled | bool | 是否可用(灰色不可点击) |
| visible | bool | 是否可见 |
| styleSheet | QString | 样式表,类似CSS |
个人习惯:我一般会在构造函数里统一设置geometry,而不是用resize+move分开设置。这样代码更清晰,不容易出错。
4.2 QLabel:显示文本和图片
QLabel,顾名思义,就是个标签。用来显示静态文本、富文本或者图片。它不能交互,但可以响应鼠标事件——如果你重写它的mousePressEvent的话。
// 创建一个标签
QLabel *label = new QLabel("Hello, Qt!", widget);
label->setGeometry(50, 50, 200, 30);
label->setAlignment(Qt::AlignCenter); // 文字居中
// 显示图片
QLabel *imageLabel = new QLabel(widget);
QPixmap pix(":/images/logo.png");
imageLabel->setPixmap(pix);
imageLabel->setScaledContents(true); // 自适应大小
QLabel支持HTML子集。你可以用它显示带颜色的文字、超链接。但注意——它默认不打开外部链接,需要设置setOpenExternalLinks(true)。
避坑指南:我曾经在项目里用QLabel显示用户输入的文本,结果用户输入了HTML标签,导致界面显示异常。记住:如果显示用户输入,一定要用setTextFormat(Qt::PlainText),防止XSS攻击。
4.3 QPushButton:最常用的交互控件
按钮,谁都会用。但你真的用对了吗?
QPushButton可以显示文本、图标,也可以设置快捷键。它最核心的信号是clicked()——点击时触发。
QPushButton *btn = new QPushButton("点击我", widget);
btn->setGeometry(150, 150, 100, 40);
// 连接信号与槽
connect(btn, &QPushButton::clicked, [=]() {
qDebug() << "按钮被点击了!";
});
我个人习惯给按钮设置对象名,方便在样式表中定位:
btn->setObjectName("myButton");
// 样式表
btn->setStyleSheet("QPushButton#myButton { background-color: #4CAF50; color: white; }");
QPushButton还有几个实用模式:
- setDefault(true):按Enter键时触发
- setAutoRepeat(true):长按时连续触发clicked信号
- setMenu():添加下拉菜单
4.4 QLineEdit:单行文本输入
输入框,用户输入信息的地方。QLineEdit支持文本、密码、数字等模式。
QLineEdit *edit = new QLineEdit(widget);
edit->setGeometry(50, 200, 200, 30);
edit->setPlaceholderText("请输入用户名..."); // 占位提示
// 获取输入内容
QString text = edit->text();
// 密码模式
edit->setEchoMode(QLineEdit::Password);
常用的信号:
- textChanged():文本变化时触发(实时)
- editingFinished():编辑完成(按Enter或失去焦点)
- returnPressed():按Enter键时触发
经验之谈:做登录界面时,我习惯用editingFinished()而不是textChanged()。因为用户输入过程中不需要验证,等输入完了再校验,性能更好,体验也更自然。
4.5 布局管理器:让界面自动适应
手动设置坐标和大小?那是新手干的事。真正的项目里,我们靠布局管理器来搞定。
布局管理器会自动计算子控件的位置和大小,窗口缩放时自动调整。Qt提供了三种基本布局:
| 布局类 | 排列方式 | 适用场景 |
|---|---|---|
| QHBoxLayout | 水平排列 | 按钮组、工具栏 |
| QVBoxLayout | 垂直排列 | 表单、列表 |
| QGridLayout | 网格排列 | 计算器、表格 |
来看一个实际例子:
// 创建一个窗口
QWidget *window = new QWidget;
window->setWindowTitle("布局示例");
window->resize(300, 200);
// 创建控件
QLabel *label = new QLabel("姓名:");
QLineEdit *edit = new QLineEdit;
QPushButton *btn = new QPushButton("提交");
// 水平布局:标签 + 输入框
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addWidget(label);
hLayout->addWidget(edit);
// 垂直布局:上面 + 按钮
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(hLayout); // 嵌套布局
mainLayout->addWidget(btn);
window->setLayout(mainLayout);
window->show();
布局管理器有几个重要特性:
- 伸缩因子(stretch):控制控件占据空间的比例
- 边距(margin):布局边缘的空白
- 间距(spacing):控件之间的间隔
// 设置伸缩因子:输入框占2份,按钮占1份
hLayout->addWidget(edit, 2);
hLayout->addWidget(btn, 1);
// 设置边距和间距
mainLayout->setContentsMargins(10, 10, 10, 10); // 左、上、右、下
mainLayout->setSpacing(8);
避坑指南:我曾经犯过一个错误——给一个已经设置了布局的窗口又手动调用了setGeometry()。结果布局管理器失效了,窗口缩放时控件纹丝不动。记住:布局管理和手动设置坐标是互斥的,选一个就别用另一个。
4.6 综合实战:一个简单的登录界面
把上面学的串起来,写一个登录界面:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget *window = new QWidget;
window->setWindowTitle("登录");
window->resize(300, 150);
// 用户名
QLabel *userLabel = new QLabel("用户名:");
QLineEdit *userEdit = new QLineEdit;
userEdit->setPlaceholderText("请输入用户名");
QHBoxLayout *userLayout = new QHBoxLayout;
userLayout->addWidget(userLabel);
userLayout->addWidget(userEdit);
// 密码
QLabel *pwdLabel = new QLabel("密 码:");
QLineEdit *pwdEdit = new QLineEdit;
pwdEdit->setPlaceholderText("请输入密码");
pwdEdit->setEchoMode(QLineEdit::Password);
QHBoxLayout *pwdLayout = new QHBoxLayout;
pwdLayout->addWidget(pwdLabel);
pwdLayout->addWidget(pwdEdit);
// 按钮
QPushButton *loginBtn = new QPushButton("登录");
QPushButton *cancelBtn = new QPushButton("取消");
QHBoxLayout *btnLayout = new QHBoxLayout;
btnLayout->addStretch(); // 弹簧,把按钮推到右边
btnLayout->addWidget(loginBtn);
btnLayout->addWidget(cancelBtn);
// 主布局
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(userLayout);
mainLayout->addLayout(pwdLayout);
mainLayout->addLayout(btnLayout);
mainLayout->addStretch();
window->setLayout(mainLayout);
window->show();
return app.exec();
}
这个例子虽然简单,但包含了我们这节课讲的所有核心内容。你想想看——QWidget作为容器,QLabel显示提示,QLineEdit接收输入,QPushButton触发操作,布局管理器自动排列。这就是Qt Widgets开发的基本套路。
嗯,最后说一句:布局管理器是Qt的精华。刚开始可能觉得麻烦,但用习惯了,你会发现它比手动布局高效得多。尤其是做多语言适配或者窗口缩放时,布局管理器能帮你省下大量时间。
下一节,我们会深入信号与槽机制——这是Qt最强大的特性,没有之一。到时候见。