Qwidget

类 用途 常用操作或信号
QLabel 显示文字、图片 setText()、setPixmap()
QPushButton 按钮 clicked()
QLineEdit 单行输入框 text()、textChanged()
QTextEdit 多行文本编辑 setPlainText()、toPlainText()
QCheckBox 勾选开关 isChecked()、toggled(bool)
QComboBox 下拉选择 addItem()、currentIndexChanged()
QSlider 拖动调节数值 setRange()、valueChanged(int)
QSpinBox 输入整数 setRange()、value()
QStackedWidget 多个页面切换,同一时间显示一个 setCurrentIndex()
QListWidget 简单列表 addItem()、currentRowChanged()
QDialog 设置、确认等对话框 open()、accept()、reject()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 引入应用、窗口、控件和布局所需的类型
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QCheckBox>
#include <QVBoxLayout>

int main(int argc, char *argv[])
{
// 创建 GUI 应用,负责事件分发等基础工作
QApplication app(argc, argv);

// 没有父控件,因此这里作为顶层窗口
QWidget window;
window.setWindowTitle("登录界面教学");
window.resize(360, 260);

// 子控件指定 window 为父对象,窗口销毁时会清理它们
auto *title = new QLabel("欢迎登录", &window);
auto *account = new QLineEdit(&window);
auto *password = new QLineEdit(&window);
auto *remember = new QCheckBox("记住账号", &window);
auto *login = new QPushButton("登录", &window);
auto *message = new QLabel(&window);

// 设置输入提示与密码显示方式
account->setPlaceholderText("请输入账号");
password->setPlaceholderText("请输入密码");
password->setEchoMode(QLineEdit::Password);

// 安装纵向布局,控件按添加顺序从上到下排列
auto *layout = new QVBoxLayout(&window);
layout->setContentsMargins(20, 20, 20, 20);
layout->setSpacing(12);
layout->addWidget(title);
layout->addWidget(account);
layout->addWidget(password);
layout->addWidget(remember);
layout->addWidget(login);
layout->addWidget(message);

// 按钮发出 clicked 信号时,执行这段处理逻辑
// window 是连接的上下文对象,销毁后连接会自动断开
QObject::connect(login, &QPushButton::clicked, &window,
[account, password, remember, message]() {
// 从输入框读取当前文本
const QString name = account->text().trimmed();

// 这里只做输入检查,不代表服务器认证成功
if (name.isEmpty() || password->text().isEmpty()) {
message->setText("请填写账号和密码");
return;
}

// 读取复选框状态,并展示演示结果
message->setText(
QString("账号:%1;记住账号:%2")
.arg(name)
.arg(remember->isChecked() ? "是" : "否"));
});

// 显示窗口,进入事件循环,等待点击、输入、重绘等事件
window.show();
return app.exec();
}

自定义控件

自绘控件继承自 QWidget,最核心的三个概念:

名字 职责
update() 告诉 Qt:“我的显示内容变了,请安排重画”
paintEvent() Qt 需要这个控件重画时调用的函数
QPainter 真正执行画矩形、文字、图片、路径等操作的工具
  • rect() 是控件自己的局部区域,左上角通常是 (0,0)。
  • 窗口被遮挡后重新露出、控件尺寸变化,也可能触发绘制,因此 paintEvent() 要能根据已保存的状态恢复画面。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    // 防止头文件被重复包含
    #pragma once

    // 引入窗口、绘图及数值限制工具
    #include <QWidget>
    #include <QPainter>
    #include <algorithm>

    // 教学控件:显示 0~1 之间的电平值
    class SimpleLevelBar : public QWidget
    {
    public:
    explicit SimpleLevelBar(QWidget *parent = nullptr)
    : QWidget(parent)
    {
    // 横向允许伸展,纵向倾向保持推荐高度
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    setMinimumSize(40, 16);
    }

    // 向布局提供推荐尺寸,不是强制锁死尺寸
    QSize sizeHint() const override
    {
    return QSize(240, 24);
    }

    // 外部通过这个函数更新控件的数据
    void setLevel(float level)
    {
    // 将输入限制在 0~1,避免绘制超出范围
    level = std::clamp(level, 0.0f, 1.0f);

    // 数值没有变化时,不额外请求重绘
    if (m_level == level) {
    return;
    }

    m_level = level;

    // 安排稍后的绘制,不直接调用 paintEvent
    update();
    }

    protected:
    void paintEvent(QPaintEvent *) override
    {
    // 在当前控件表面绘制
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    // 留出边距,确定色条的整体区域
    const QRectF track = QRectF(rect()).adjusted(2, 2, -2, -2);

    // 先绘制灰色背景
    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor("#D9DEE7"));
    painter.drawRoundedRect(track, 4, 4);

    // 电平越大,填充区域越宽
    QRectF fill = track;
    fill.setWidth(track.width() * m_level);

    // 高电平显示红色,其余显示绿色
    painter.setBrush(
    m_level > 0.8f ? QColor("#E85D5D") : QColor("#42B883"));

    if (m_level > 0.0f) {
    painter.drawRoundedRect(fill, 4, 4);
    }
    }

    private:
    // 保存当前显示值,初始没有电平
    float m_level = 0.0f;
    };

如果要在 ListView 的每一项里加入自定义样式,比如增加一个 Label 和一个 Button,应该怎么实现?

  1. 少量列表项,可以给每项放一个自定义 QWidget:创建列表项 → 创建一个包含 QLabel+QPushButton 的行控件 → 将行控件放到列表项里。
  2. 列表项很多时,应该使用 Delegate:第一种方法每行都创建真实 QWidget、Label、Button。几千行时,对象数量、内存和布局开销都会增加。
    更适合大量数据的做法是继承 QStyledItemDelegate:
    • paint():画文字和按钮外观。
    • sizeHint():提供列表项大小。
    • editorEvent():处理点击,判断是否点击了按钮区域。

“继承并实现一个自定义的 QStyledItemDelegate。需要重写 paint 方法,在这个方法中使用 QPainter 来绘制,包括文本和按钮样式。为了响应交互,比如按钮的点击,还需要重写 editorEvent 方法来处理鼠标事件,判断点击位置是否在您绘制的按钮区域内,并发出自定义的信号。虽然也可以使用 QListView::setIndexWidget 为每一项直接设置一个真实的 QWidget,但这种方法会为每个列表项都创建一个完整的窗口部件实例,当列表项非常多时,会增加性能消耗”

布局方式

布局类 排列方式 适用场景
QVBoxLayout 从上到下 登录表单、纵向工具栏
QHBoxLayout 从左到右 一排按钮、输入框+发送按钮
QGridLayout 行列网格,可以跨行跨列 多列设置项、计算器键盘
QFormLayout 标签+输入控件组成的表单 登录页面、编码参数设置
QStackedLayout 多个页面叠放,默认显示一个 登录方式切换、多步骤页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 假设以下代码位于一个 QWidget 子类的方法中
auto *layout = new QVBoxLayout(this);

// 按添加顺序从上到下排列 (纵向布局)
layout->addWidget(new QLineEdit(this));
layout->addWidget(new QLineEdit(this));
layout->addWidget(new QPushButton("登录", this));

// 创建一行布局,稍后加入外层布局
auto *row = new QHBoxLayout;

// 输入框允许占用更多空间,按钮保持适合文字的宽度 (横向布局)
row->addWidget(codeEdit, 1);
row->addWidget(getCodeButton, 0);

// 将这一整行加入已有的纵向布局
mainLayout->addLayout(row);

辅助容器

QSplitter:允许用户拖动分隔线;QScrollArea:内容太多时提供滚动条。

1
2
3
4
5
6
7
// 创建左右分隔容器
auto *splitter = new QSplitter(Qt::Horizontal, this);
splitter->addWidget(layerPanel);
splitter->addWidget(previewPanel);

// 分隔容器本身也是控件,可以加入布局
mainLayout->addWidget(splitter);
1
2
3
4
5
6
7
8
9
10
11
12
// 内容面板用布局排列大量设置项
auto *content = new QWidget;
auto *form = new QFormLayout(content);
form->addRow("名称:", new QLineEdit(content));
form->addRow("地址:", new QLineEdit(content));

// 滚动容器接管内容面板,允许内容宽度随可用空间调整
auto *scroll = new QScrollArea(this);
scroll->setWidgetResizable(true);
scroll->setWidget(content);

mainLayout->addWidget(scroll);

QML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import QtQuick
import QtQuick.Controls

Rectangle {
id: panel

// 定义面板的尺寸和外观。
width: parent.width / 2 // 建立关系:宽度始终跟随父元素变化。
height: 160
color: "#202020"
radius: 8

// 自定义状态,供其他属性绑定。
property bool recording: false

Button {
anchors.centerIn: parent

// 属性绑定:recording 改变时,文字自动更新。
text: panel.recording ? "停止录制" : "开始录制"

// 事件处理:点击时修改状态。
onClicked: {
panel.recording = !panel.recording;
}
}
}
语法 含义
Rectangle {} 创建一个矩形对象
id: panel 在当前组件中引用这个对象
color: "#202020" 设置属性
property bool recording 声明自己的属性
text: panel.recording ? ... 建立依赖其他属性的绑定
onClicked: {} 响应点击信号
anchors.centerIn: parent 在父元素中居中

常见的控件:

类型 用途 在播放器中的例子
Item 无外观的基础容器 组织视频、浮层
Rectangle 背景、边框、圆角 卡片、面板、高亮边框
Text / Label 显示文字 房间标题、FPS
Image 显示图片 头像、表情
MouseArea 接收鼠标输入 直播间右键、拖动
Button 标准按钮 设置操作
TextField 单行输入 输入直播间 ID
Slider 拖动数值 音量、时间轴
CheckBox / Switch 开关状态 启用某项功能
RowLayout / ColumnLayout 自动排列并分配空间 控制栏、设置面板
ListView 滚动列表 播放列表
Repeater 根据数据生成元素 少量重复控件
Menu / MenuItem 弹出菜单和操作项 直播间右键菜单
Instantiator 根据模型创建对象 动态画质、线路菜单
Behavior / NumberAnimation 属性动画 悬浮颜色、弹幕移动
ScrollView 提供滚动容器 设置页面使用
Flickable 提供可拖拽、可惯性滚动的内容区域 是常见滚动基础组件
ScrollBar 可拖动的滚动条 项目封装为 StyledScrollBar
ScrollIndicator 主要显示滚动位置的指示器 与可操作滚动条用途不同
MenuSeparator 菜单里的分隔线 直播间右键菜单使用
ToolSeparator 工具栏里的分隔线 Qt 提供的标准组件
SplitView 带可拖动分隔条的分栏容器 主视频布局没有用它

布局方式

布局 用途
RowLayout 播放控制栏、设置项的标签和输入框横排
ColumnLayout 设置项纵排、登录框内容
GridLayout 布局预设的网格排列
StackLayout 切换“直播”“录制”设置页