Qt 进阶开发:主窗口、对话框、布局与常用控件全解析

Qt 作为跨平台的 C++ 图形界面开发框架,提供了丰富的控件和便捷的布局系统,本文将从 QMainWindow 核心组件、对话框使用、布局管理、常用控件等维度,结合完整可运行的代码示例,详解 Qt 界面开发的核心知识点。

一、QMainWindow 核心组件

QMainWindow 是 Qt 中最常用的主窗口类,内置了菜单栏、工具栏、状态栏、停靠部件和中心控件,是构建复杂桌面应用的基础。

1.1 菜单栏(MenuBar)

菜单栏是窗口顶部的导航组件,支持多级菜单和分割符,示例代码如下:

cpp 复制代码
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 获取/创建菜单栏(QMainWindow 内置,无需手动 new)
    QMenuBar *mb = this->menuBar();
    
    // 添加一级菜单
    QMenu *menuFile = mb->addMenu("文件");
    QMenu *menuEdit = mb->addMenu("编辑");
    
    // 添加菜单项
    QAction* actionNew = menuFile->addAction("新建");
    QAction* actionOpen = menuFile->addAction("打开");
    
    // 添加分割符
    menuFile->addSeparator();
    
    // 添加二级子菜单
    QMenu* menuRecent = menuFile->addMenu("最近打开文件");
    menuRecent->addAction("1.txt");
    menuRecent->addAction("2.log");
    
    // 菜单项点击信号关联(示例)
    connect(actionNew, &QAction::triggered, this, [](){
        qDebug() << "点击了【新建】菜单项";
    });
}

1.2 工具栏(ToolBar)

工具栏是可停靠的快捷操作栏,支持自定义停靠位置、是否浮动/移动,示例代码:

cpp 复制代码
#include <QToolBar>

// 初始化工具栏
void MainWindow::initToolBar()
{
    // 创建工具栏(可创建多个)
    QToolBar* toolbar = this->addToolBar("主工具栏");
    
    // 向工具栏添加菜单项(复用菜单栏的 Action)
    toolbar->addAction(actionNew);
    toolbar->addAction(actionOpen);
    
    // 设置默认停靠位置(左侧)
    this->addToolBar(Qt::LeftToolBarArea, toolbar);
    
    // 限制停靠区域:仅允许左右侧
    toolbar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
    
    // 禁止浮动(默认可浮动)
    toolbar->setFloatable(false);
    
    // 禁止移动(默认可拖动)
    toolbar->setMovable(false);
}

1.3 状态栏(StatusBar)

状态栏位于窗口底部,用于显示临时或常驻信息,支持左右分区显示:

cpp 复制代码
#include <QStatusBar>
#include <QLabel>

// 初始化状态栏
void MainWindow::initStatusBar()
{
    // 获取/创建状态栏(QMainWindow 内置)
    QStatusBar* sb = this->statusBar();
    
    // 左侧常驻信息
    QLabel* labelLeft = new QLabel("就绪", this);
    sb->addWidget(labelLeft);
    
    // 右侧常驻信息
    QLabel* labelRight = new QLabel("当前用户:admin", this);
    sb->addPermanentWidget(labelRight);
    
    // 临时提示(3秒后自动消失)
    sb->showMessage("窗口初始化完成", 3000);
}

1.4 停靠部件与中心控件

停靠部件(QDockWidget)是可浮动/停靠的子窗口,中心控件是主窗口的核心区域(如文本编辑区):

cpp 复制代码
#include <QDockWidget>
#include <QTextEdit>

// 初始化停靠部件和中心控件
void MainWindow::initDockAndCentral()
{
    // 创建停靠部件
    QDockWidget* dw = new QDockWidget("辅助面板", this);
    // 设置停靠位置(底部)
    this->addDockWidget(Qt::BottomDockWidgetArea, dw);
    // 限制停靠方向
    dw->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
    
    // 设置中心控件(QTextEdit 作为核心编辑区)
    QTextEdit* textEdit = new QTextEdit(this);
    textEdit->setPlaceholderText("请输入内容...");
    this->setCentralWidget(textEdit);
}

二、UI 文件与资源文件使用

2.1 UI 文件的使用

Qt Designer 生成的 .ui 文件会被 qmake 转换为 C++ 代码,通过 ui 指针直接访问控件:

  1. 创建项目时勾选「Generate form」,自动生成 mainwindow.ui

  2. 在代码中通过 ui->控件对象名 访问控件(需包含 ui_mainwindow.h);

  3. setupUi(this) 函数自动完成控件的创建和布局。

示例:通过 UI 文件访问菜单项

cpp 复制代码
// 假设 ui 文件中创建了名为 actionSave 的菜单项
connect(ui->actionSave, &QAction::triggered, this, [](){
    qDebug() << "点击了UI文件中的【保存】菜单项";
});

2.2 资源文件(qrc)

资源文件用于将图片、配置文件等嵌入程序,避免绝对路径依赖:

  1. 添加资源文件:右键项目 → Add New → Qt → Qt Resource File;

  2. 编辑资源文件,添加前缀(如 /icon)和文件;

  3. 通过 :/前缀/文件名 访问资源。

示例:设置菜单项图标

cpp 复制代码
#include <QIcon>

// 方式1:绝对路径(不推荐,移植性差)
ui->actionNew->setIcon(QIcon("D:/Qt/Image/Luffy.png"));

// 方式2:资源文件(推荐,嵌入程序)
ui->actionNew->setIcon(QIcon(":/icon/Image/Luffy.png"));

三、对话框(QDialog)

对话框是临时窗口,分为模态 (阻塞其他窗口)和非模态(不阻塞),Qt 还提供了标准化对话框(如消息框、文件选择框)。

3.1 自定义对话框

模态对话框
cpp 复制代码
#include <QDialog>
#include <QPushButton>

// 点击按钮弹出模态对话框
connect(ui->btnModal, &QPushButton::clicked, this, [](){
    QDialog dlg(this);
    dlg.setWindowTitle("模态对话框");
    dlg.resize(300, 200);
    // exec() 阻塞,直到对话框关闭
    dlg.exec();
    qDebug() << "模态对话框已关闭";
});
非模态对话框

非模态对话框需用 new 创建(避免栈销毁),并设置自动释放属性:

cpp 复制代码
connect(ui->btnNonModal, &QPushButton::clicked, this, [=](){
    QDialog* dlg = new QDialog(this);
    dlg->setWindowTitle("非模态对话框");
    dlg->resize(300, 200);
    // 关闭时自动释放内存(避免内存泄漏)
    dlg->setAttribute(Qt::WA_DeleteOnClose);
    // show() 非阻塞,立即返回
    dlg->show();
});

3.2 标准对话框

Qt 封装了常用的标准化对话框,无需自定义,直接调用静态函数即可:

消息对话框(QMessageBox)

支持 4 种类型:信息、警告、错误、提问(可自定义按钮)。

cpp 复制代码
#include <QMessageBox>

// 错误提示
void MainWindow::on_btnCritical_clicked()
{
    QMessageBox::critical(this, "错误", "文件读取失败!");
}

// 警告提示
void MainWindow::on_btnWarning_clicked()
{
    QMessageBox::warning(this, "警告", "数据未保存,是否继续?");
}

// 信息提示
void MainWindow::on_btnInfo_clicked()
{
    QMessageBox::information(this, "信息", "操作成功完成!");
}

// 提问对话框(带按钮选择)
void MainWindow::on_btnQuestion_clicked()
{
    int ret = QMessageBox::question(this, "确认", "是否删除选中文件?",
                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
    switch(ret) {
        case QMessageBox::Yes:
            qDebug() << "点击了【是】";
            break;
        case QMessageBox::No:
            qDebug() << "点击了【否】";
            break;
        case QMessageBox::Cancel:
            qDebug() << "点击了【取消】";
            break;
    }
}
文件选择对话框(QFileDialog)

用于打开/保存文件,支持文件过滤:

cpp 复制代码
#include <QFileDialog>

void MainWindow::on_btnOpenFile_clicked()
{
    // 打开文件对话框:(父窗口, 标题, 默认路径, 文件过滤器)
    QString filePath = QFileDialog::getOpenFileName(this, "选择文件", "D:/",
                          "图片文件 (*.png *.jpg);;文本文件 (*.txt);;所有文件 (*.*)");
    if (!filePath.isEmpty()) {
        qDebug() << "选中的文件:" << filePath;
    }
}

四、布局管理

Qt 布局分为静态布局 (固定位置)和动态布局(自适应窗口大小),推荐使用动态布局提升用户体验。

4.1 核心布局类型

|-------------|------------------|
| 布局类 | 作用 |
| QHBoxLayout | 水平布局(控件横向排列) |
| QVBoxLayout | 垂直布局(控件纵向排列) |
| QGridLayout | 栅格布局(行列对齐) |
| QFormLayout | 表单布局(标签+输入框成对排列) |

4.2 布局使用示例(栅格布局)

cpp 复制代码
#include <QGridLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QWidget>

void MainWindow::initLayout()
{
    // 创建容器承载布局(中心控件)
    QWidget* centralWidget = new QWidget(this);
    this->setCentralWidget(centralWidget);
    
    // 创建栅格布局
    QGridLayout* gridLayout = new QGridLayout(centralWidget);
    
    // 添加控件:(控件, 行, 列, 行跨度, 列跨度)
    QLabel* labName = new QLabel("用户名:", this);
    QLineEdit* edtName = new QLineEdit(this);
    QLabel* labPwd = new QLabel("密码:", this);
    QLineEdit* edtPwd = new QLineEdit(this);
    edtPwd->setEchoMode(QLineEdit::Password);
    QPushButton* btnLogin = new QPushButton("登录", this);
    QPushButton* btnCancel = new QPushButton("取消", this);
    
    gridLayout->addWidget(labName, 0, 0);
    gridLayout->addWidget(edtName, 0, 1, 1, 2); // 跨2列
    gridLayout->addWidget(labPwd, 1, 0);
    gridLayout->addWidget(edtPwd, 1, 1, 1, 2);
    gridLayout->addWidget(btnLogin, 2, 1);
    gridLayout->addWidget(btnCancel, 2, 2);
    
    // 设置布局间距
    gridLayout->setMargin(20); // 布局与容器的间距
    gridLayout->setSpacing(10); // 控件之间的间距
}

4.3 布局优化技巧

  1. 弹簧(QSpacerItem) :用于控件居中/留白,如 gridLayout->addItem(new QSpacerItem(0,0, QSizePolicy::Expanding, QSizePolicy::Expanding), 2, 0);

  2. 大小策略 :通过 setSizePolicy 设置控件自适应规则(如固定大小、拉伸);

  3. 固定窗口大小setFixedSize(800, 600)setMinimumSize/setMaximumSize

五、常用控件实战

5.1 单选按钮(QRadioButton)

单选按钮默认互斥,需用 QGroupBox 隔离不同组:

cpp 复制代码
#include <QRadioButton>
#include <QGroupBox>

void MainWindow::initRadioButton()
{
    QGroupBox* groupBox = new QGroupBox("性别", this);
    QHBoxLayout* hLayout = new QHBoxLayout(groupBox);
    
    QRadioButton* rdoMale = new QRadioButton("男", this);
    QRadioButton* rdoFemale = new QRadioButton("女", this);
    rdoMale->setChecked(true); // 默认选中
    
    hLayout->addWidget(rdoMale);
    hLayout->addWidget(rdoFemale);
    
    // 信号关联
    connect(rdoMale, &QRadioButton::clicked, [](){
        qDebug() << "选中:男";
    });
}

5.2 复选框(QCheckBox)

支持三态(选中/未选中/半选),通过 stateChanged 信号监听状态:

cpp 复制代码
#include <QCheckBox>

void MainWindow::initCheckBox()
{
    QCheckBox* cbxRead = new QCheckBox("阅读权限", this);
    QCheckBox* cbxWrite = new QCheckBox("写入权限", this);
    QCheckBox* cbxAdmin = new QCheckBox("管理员权限", this);
    cbxAdmin->setTristate(true); // 启用三态
    
    connect(cbxRead, &QCheckBox::stateChanged, [](int state){
        if (state == Qt::Checked) {
            qDebug() << "阅读权限:已选中";
        } else {
            qDebug() << "阅读权限:未选中";
        }
    });
}

5.3 列表控件(QListWidget)

用于显示单列列表,支持添加单个/多个项:

cpp 复制代码
#include <QListWidget>
#include <QListWidgetItem>

void MainWindow::initListWidget()
{
    QListWidget* listWidget = new QListWidget(this);
    
    // 方式1:添加单个项(居中对齐)
    QListWidgetItem* item1 = new QListWidgetItem("床前明月光");
    item1->setTextAlignment(Qt::AlignHCenter);
    listWidget->addItem(item1);
    
    // 方式2:批量添加项
    QStringList poemList;
    poemList << "疑是地上霜" << "举头望明月" << "低头思故乡";
    listWidget->addItems(poemList);
    
    // 选中项监听
    connect(listWidget, &QListWidget::itemClicked, [](QListWidgetItem* item){
        qDebug() << "选中:" << item->text();
    });
}

5.4 树形控件(QTreeWidget)

用于显示层级结构数据,支持多级节点:

cpp 复制代码
#include <QTreeWidget>
#include <QTreeWidgetItem>

void MainWindow::initTreeWidget()
{
    QTreeWidget* treeWidget = new QTreeWidget(this);
    // 设置表头(多列)
    treeWidget->setHeaderLabels(QStringList() << "英雄分类" << "简介");
    
    // 根节点
    QTreeWidgetItem* rootStrength = new QTreeWidgetItem(QStringList() << "力量型");
    QTreeWidgetItem* rootAgility = new QTreeWidgetItem(QStringList() << "敏捷型");
    treeWidget->addTopLevelItem(rootStrength);
    treeWidget->addTopLevelItem(rootAgility);
    
    // 子节点
    QTreeWidgetItem* hero1 = new QTreeWidgetItem(QStringList() << "刚背猪" << "前排坦克,范围输出");
    QTreeWidgetItem* hero2 = new QTreeWidgetItem(QStringList() << "月骑" << "中排物理输出");
    rootStrength->addChild(hero1);
    rootAgility->addChild(hero2);
    
    // 展开所有节点
    treeWidget->expandAll();
}

5.5 表格控件(QTableWidget)

用于显示多行多列的表格数据:

cpp 复制代码
#include <QTableWidget>
#include <QTableWidgetItem>

void MainWindow::initTableWidget()
{
    QTableWidget* tableWidget = new QTableWidget(this);
    
    // 1. 设置行列数
    tableWidget->setRowCount(5);
    tableWidget->setColumnCount(3);
    
    // 2. 设置表头
    tableWidget->setHorizontalHeaderLabels(QStringList() << "英雄" << "性别" << "年龄");
    
    // 3. 填充数据
    QStringList heroNames = {"亚瑟", "赵云", "妲己", "安琪拉", "孙悟空"};
    QStringList heroGender = {"男", "男", "女", "女", "男"};
    QStringList heroAge = {"123", "321", "18", "20", "500"};
    
    for (int row = 0; row < 5; row++) {
        tableWidget->setItem(row, 0, new QTableWidgetItem(heroNames[row]));
        tableWidget->setItem(row, 1, new QTableWidgetItem(heroGender[row]));
        tableWidget->setItem(row, 2, new QTableWidgetItem(heroAge[row]));
    }
    
    // 自动调整列宽
    tableWidget->horizontalHeader()->setStretchLastSection(true);
}

5.6 堆叠控件(QStackedWidget)

用于页面切换(如 Tab 页效果),通过 setCurrentIndex 切换页面:

cpp 复制代码
#include <QStackedWidget>
#include <QPushButton>

void MainWindow::initStackedWidget()
{
    QStackedWidget* stackedWidget = new QStackedWidget(this);
    
    // 创建页面
    QWidget* page1 = new QWidget(this);
    QWidget* page2 = new QWidget(this);
    page1->setStyleSheet("background:#f0f0f0;");
    page2->setStyleSheet("background:#e0e0e0;");
    
    // 添加页面
    stackedWidget->addWidget(page1);
    stackedWidget->addWidget(page2);
    
    // 切换按钮
    QPushButton* btnPage1 = new QPushButton("页面1", this);
    QPushButton* btnPage2 = new QPushButton("页面2", this);
    
    connect(btnPage1, &QPushButton::clicked, [=](){
        stackedWidget->setCurrentIndex(0); // 切换到第1页
    });
    connect(btnPage2, &QPushButton::clicked, [=](){
        stackedWidget->setCurrentIndex(1); // 切换到第2页
    });
}

5.7 图片/动画显示(QLabel)

QLabel 不仅能显示文本,还能显示静态图片和动态 GIF:

cpp 复制代码
#include <QLabel>
#include <QPixmap>
#include <QMovie>

void MainWindow::initLabel()
{
    // 1. 显示静态图片
    QLabel* labImage = new QLabel(this);
    labImage->setPixmap(QPixmap(":/icon/Image/Luffy.png").scaled(100, 100, Qt::KeepAspectRatio));
    
    // 2. 显示动态 GIF
    QLabel* labGif = new QLabel(this);
    QMovie* movie = new QMovie(":/icon/Image/loading.gif");
    labGif->setMovie(movie);
    movie->start(); // 播放动画
}

六、自定义控件

当基础控件无法满足需求时,可通过「组合基础控件」创建自定义控件:

6.1 创建自定义控件类

  1. 右键项目 → Add New → Qt → Qt Designer Form Class → 选择 Widget 模板;

  2. 在 UI 文件中拖拽基础控件,编写内部逻辑(如信号槽);

  3. 对外提供接口(如设置值、获取值的函数)。

6.2 使用自定义控件

  1. 在主窗口 UI 文件中拖拽一个 QWidget

  2. 右键该 Widget → 提升为 → 输入自定义控件类名 → 添加 → 提升;

  3. 在代码中通过 ui->自定义控件对象名 调用接口。

示例:自定义控件头文件(MyWidget.h)

cpp 复制代码
#include <QLabel>
#include <QPixmap>
#include <QMovie>

void MainWindow::initLabel()
{
    // 1. 显示静态图片
    QLabel* labImage = new QLabel(this);
    labImage->setPixmap(QPixmap(":/icon/Image/Luffy.png").scaled(100, 100, Qt::KeepAspectRatio));
    
    // 2. 显示动态 GIF
    QLabel* labGif = new QLabel(this);
    QMovie* movie = new QMovie(":/icon/Image/loading.gif");
    labGif->setMovie(movie);
    movie->start(); // 播放动画
}

七、总结

本文覆盖了 Qt 界面开发的核心知识点:QMainWindow 组件、UI/资源文件、对话框、布局管理、常用控件及自定义控件。Qt 的控件体系高度封装,结合信号槽机制,能快速实现跨平台的桌面应用。实际开发中,建议优先使用 Qt Designer 可视化布局,配合动态布局和自定义控件,提升开发效率和界面适配性。

完整代码仓库 :可将上述代码整合到一个 MainWindow 类中,编译前需确保.pro 文件包含 QT += core gui widgets,并正确配置资源文件路径。

相关推荐
小短腿的代码世界2 小时前
Qt时间日期处理与QTimer高级应用:从毫秒级精度到跨平台定时器的完整架构解析
开发语言·qt·架构
小短腿的代码世界3 小时前
QGC飞控参数系统架构深度解析:从XML到飞控寄存器的参数同步引擎
qt·microsoft·ui
小短腿的代码世界3 小时前
QGC固件升级与引导加载架构深度解析:从Bootloader握手到固件校验的完整流程
qt·性能优化·架构
buhuizhiyuci4 小时前
【QT-百日筑基篇】打完完怪,开始学炼丹, 前往藏书阁寻找对应材料的信息,并前往去寻找对应材料-QT信号和槽
开发语言·qt
小短腿的代码世界4 小时前
QtitanRibbon深度解析:从微软Office UI到Qt跨平台Ribbon框架的完整架构实现
qt·microsoft·ui
郝学胜-神的一滴4 小时前
CMake 010 :一步到位链接静态库
开发语言·c++·qt·程序人生·系统架构·cmake
Shadow(⊙o⊙)4 小时前
qt中自定义槽函数 内部继承逻辑、GUI+CLI协同1.0
开发语言·前端·c++·qt
代钦塔拉4 小时前
第一篇:字符编码全解:从ASCII/GBK/Unicode到UTF-8
开发语言·qt
syagain_zsx5 小时前
Qt初识,快速上手
开发语言·qt