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最强大的特性,没有之一。到时候见。