欢迎光临
我们一直在努力

04-Qt基础-常用控件与布局管理

常用控件与布局管理

这一章讲 Qt 界面开发的"积木":QWidget、QMainWindow、QDialog 三大窗口类,以及 QHBoxLayout/QVBoxLayout/QGridLayout 三大布局。学完就能拼出大部分界面。


一、QWidget:所有控件的基类

1.1 QWidget 是什么

QWidget 是所有界面元素的基类,窗口、按钮、输入框都是它的子类。它既可以作为独立窗口,也可以嵌入到其他控件中。

1.2 常用属性与方法

#include <QWidget>
#include <QDebug>

/**
* @brief 演示 QWidget 常用方法
*/

void demoWidget()
{
QWidget w;

// 窗口标题
w.setWindowTitle("我的窗口");

// 大小
w.resize(400, 300); // 设置大小
w.setMinimumSize(200, 150); // 最小尺寸
w.setMaximumSize(800, 600); // 最大尺寸
w.setFixedSize(400, 300); // 固定大小,不可拉伸

// 位置
w.move(100, 100); // 移动到屏幕坐标 (100, 100)

// 显示与隐藏
w.show(); // 显示(非模态)
// w.hide(); // 隐藏
// w.close(); // 关闭

// 状态查询
qDebug() << "是否可见:" << w.isVisible();
qDebug() << "是否启用:" << w.isEnabled();
qDebug() << "大小:" << w.size();
qDebug() << "位置:" << w.pos();

// 启用/禁用
w.setEnabled(false); // 禁用(变灰,不响应输入)
w.setEnabled(true);

// 设置提示
w.setToolTip("这是一个提示"); // 鼠标悬停显示
}

1.3 窗口标志(WindowFlags)

#include <QWidget>

/**
* @brief 演示窗口标志
*/

void demoWindowFlags()
{
// 普通窗口(默认)
QWidget normal;
normal.setWindowTitle("普通窗口");
normal.show();

// 无边框窗口(自定义标题栏时用)
QWidget borderless;
borderless.setWindowFlags(Qt::FramelessWindowHint);
borderless.show();

// 窗口置顶
QWidget alwaysOnTop;
alwaysOnTop.setWindowFlags(Qt::WindowStaysOnTopHint);
alwaysOnTop.show();

// 对话框风格(带问号按钮)
QWidget dialogStyle;
dialogStyle.setWindowFlags(Qt::Dialog);
dialogStyle.show();

// 组合使用:无边框 + 置顶
QWidget combined;
combined.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
combined.show();
}


二、QMainWindow:主窗口框架

2.1 QMainWindow 的结构

QMainWindow 是带菜单栏、工具栏、状态栏、停靠窗口的主窗口框架,适合做应用程序主界面。

┌─────────────────────────────────────┐
│ 菜单栏 (MenuBar) │
├─────────────────────────────────────┤
│ 工具栏 (ToolBar) │
├──────────┬──────────────────────────┤
│ 停靠窗口 │ 中心部件 (CentralWidget) │
│ (Dock) │ │
├──────────┴──────────────────────────┤
│ 状态栏 (StatusBar) │
└─────────────────────────────────────┘

2.2 完整示例:带菜单工具栏的主窗口

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class QTextEdit;
class QLabel;

/**
* @brief 主窗口,演示 QMainWindow 的各个区域
*/

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = nullptr);

private slots:
void onNewFile(); ///< 新建文件
void onOpenFile(); ///< 打开文件
void onSaveFile(); ///< 保存文件
void onAbout(); ///< 关于对话框

private:
void createMenuBar(); ///< 创建菜单栏
void createToolBar(); ///< 创建工具栏
void createStatusBar(); ///< 创建状态栏

QTextEdit *m_textEdit; ///< 中心文本编辑区
QLabel *m_statusLabel; ///< 状态栏标签
};

#endif // MAINWINDOW_H

// mainwindow.cpp
#include "mainwindow.h"
#include <QTextEdit>
#include <QLabel>
#include <QMenuBar>
#include <QToolBar>
#include <QStatusBar>
#include <QAction>
#include <QFileDialog>
#include <QMessageBox>
#include <QFile>
#include <QTextStream>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setWindowTitle("我的记事本");
resize(800, 600);

// 1. 设置中心部件(必须)
m_textEdit = new QTextEdit(this);
setCentralWidget(m_textEdit);

// 2. 创建菜单、工具栏、状态栏
createMenuBar();
createToolBar();
createStatusBar();
}

void MainWindow::createMenuBar()
{
// 文件菜单
QMenu *fileMenu = menuBar()->addMenu("文件(&F)");

QAction *newAct = new QAction("新建(&N)", this);
newAct->setShortcut(QKeySequence::New); // Ctrl+N
connect(newAct, &QAction::triggered, this, &MainWindow::onNewFile);
fileMenu->addAction(newAct);

QAction *openAct = new QAction("打开(&O)…", this);
openAct->setShortcut(QKeySequence::Open); // Ctrl+O
connect(openAct, &QAction::triggered, this, &MainWindow::onOpenFile);
fileMenu->addAction(openAct);

QAction *saveAct = new QAction("保存(&S)", this);
saveAct->setShortcut(QKeySequence::Save); // Ctrl+S
connect(saveAct, &QAction::triggered, this, &MainWindow::onSaveFile);
fileMenu->addAction(saveAct);

fileMenu->addSeparator(); // 分隔线

QAction *exitAct = new QAction("退出(&X)", this);
exitAct->setShortcut(QKeySequence::Quit); // Ctrl+Q
connect(exitAct, &QAction::triggered, this, &QWidget::close);
fileMenu->addAction(exitAct);

// 帮助菜单
QMenu *helpMenu = menuBar()->addMenu("帮助(&H)");
QAction *aboutAct = new QAction("关于(&A)", this);
connect(aboutAct, &QAction::triggered, this, &MainWindow::onAbout);
helpMenu->addAction(aboutAct);
}

void MainWindow::createToolBar()
{
QToolBar *toolBar = addToolBar("主工具栏");
toolBar->setMovable(false); // 禁止拖动

// 工具栏可以复用菜单的 QAction
QAction *newAct = new QAction("新建", this);
connect(newAct, &QAction::triggered, this, &MainWindow::onNewFile);
toolBar->addAction(newAct);

QAction *openAct = new QAction("打开", this);
connect(openAct, &QAction::triggered, this, &MainWindow::onOpenFile);
toolBar->addAction(openAct);

QAction *saveAct = new QAction("保存", this);
connect(saveAct, &QAction::triggered, this, &MainWindow::onSaveFile);
toolBar->addAction(saveAct);
}

void MainWindow::createStatusBar()
{
m_statusLabel = new QLabel("就绪", this);
statusBar()->addWidget(m_statusLabel);

// 监听文本变化,更新状态栏字数
connect(m_textEdit, &QTextEdit::textChanged, this, [this]() {
int count = m_textEdit->toPlainText().length();
m_statusLabel->setText(QString("字符数: %1").arg(count));
});
}

void MainWindow::onNewFile()
{
m_textEdit->clear();
m_statusLabel->setText("已新建文件");
}

void MainWindow::onOpenFile()
{
QString fileName = QFileDialog::getOpenFileName(
this, "打开文件", "", "文本文件 (*.txt);;所有文件 (*.*)");
if (fileName.isEmpty()) return;

QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
m_textEdit->setPlainText(in.readAll());
file.close();
statusBar()->showMessage("已打开: " + fileName, 3000);
}
}

void MainWindow::onSaveFile()
{
QString fileName = QFileDialog::getSaveFileName(
this, "保存文件", "untitled.txt", "文本文件 (*.txt)");
if (fileName.isEmpty()) return;

QFile file(fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << m_textEdit->toPlainText();
file.close();
statusBar()->showMessage("已保存: " + fileName, 3000);
}
}

void MainWindow::onAbout()
{
QMessageBox::about(this, "关于", "我的记事本 v1.0\\n基于 Qt 6 开发");
}

2.3 QAction:菜单和工具栏的统一抽象

QAction 是 Qt 的一个重要设计:一个动作可以同时出现在菜单和工具栏中,共享文本、图标、快捷键、信号。

// 最佳实践:创建一次 QAction,菜单和工具栏都用它
QAction *saveAct = new QAction(QIcon(":/icons/save.png"), "保存", this);
saveAct->setShortcut(QKeySequence::Save);
saveAct->setStatusTip("保存当前文件");
connect(saveAct, &QAction::triggered, this, &MainWindow::onSaveFile);

fileMenu->addAction(saveAct); // 加到菜单
toolBar->addAction(saveAct); // 加到工具栏(同一个 action)


三、QDialog:对话框

3.1 模态与非模态

#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>

/**
* @brief 自定义对话框
*/

class MyDialog : public QDialog
{
Q_OBJECT

public:
explicit MyDialog(QWidget *parent = nullptr)
: QDialog(parent)
{
setWindowTitle("自定义对话框");
auto *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("这是一个对话框", this));

auto *okBtn = new QPushButton("确定", this);
auto *cancelBtn = new QPushButton("取消", this);
layout->addWidget(okBtn);
layout->addWidget(cancelBtn);

// 点击确定时,对话框返回 QDialog::Accepted
connect(okBtn, &QPushButton::clicked, this, &QDialog::accept);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
}
};

// 使用对话框
void demoDialog()
{
MyDialog dlg;
// 模态显示:阻塞主窗口,直到对话框关闭
if (dlg.exec() == QDialog::Accepted) {
qDebug() << "用户点击了确定";
} else {
qDebug() << "用户点击了取消";
}

// 非模态显示:不阻塞,和主窗口同时操作
// MyDialog *dlg2 = new MyDialog(this);
// dlg2->show();
// dlg2->setAttribute(Qt::WA_DeleteOnClose); // 关闭时自动释放
}

3.2 标准对话框(开箱即用)

Qt 提供了一系列常用标准对话框,不用自己写。

#include <QMessageBox>
#include <QInputDialog>
#include <QFileDialog>
#include <QColorDialog>
#include <QFontDialog>
#include <QDebug>

/**
* @brief 演示各种标准对话框
*/

void demoStandardDialogs(QWidget *parent)
{
// 1. 消息框
QMessageBox::information(parent, "提示", "操作成功");
QMessageBox::warning(parent, "警告", "文件可能已损坏");
QMessageBox::critical(parent, "错误", "无法连接服务器");

// 确认对话框(有返回值)
auto reply = QMessageBox::question(
parent, "确认", "确定要删除吗?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
qDebug() << "用户确认删除";
}

// 2. 输入对话框
bool ok;
QString text = QInputDialog::getText(
parent, "输入", "请输入姓名:", QLineEdit::Normal, "", &ok);
if (ok && !text.isEmpty()) {
qDebug() << "输入的姓名:" << text;
}

int value = QInputDialog::getInt(parent, "输入", "请输入数量:", 1, 1, 100, 1, &ok);

// 3. 文件对话框
QString fileName = QFileDialog::getOpenFileName(
parent, "打开文件", "", "图片 (*.png *.jpg);;所有文件 (*)");

QString saveName = QFileDialog::getSaveFileName(
parent, "保存文件", "output.txt", "文本 (*.txt)");

QString dir = QFileDialog::getExistingDirectory(parent, "选择目录");

// 4. 颜色对话框
QColor color = QColorDialog::getColor(Qt::white, parent, "选择颜色");
if (color.isValid()) {
qDebug() << "选中颜色:" << color.name();
}

// 5. 字体对话框
bool ok2;
QFont font = QFontDialog::getFont(&ok2, QFont("Arial", 12), parent);
if (ok2) {
qDebug() << "选中字体:" << font.family() << font.pointSize();
}
}


四、布局管理(Layout)

布局管理器自动管理控件的位置和大小,窗口缩放时控件自动适配。永远不要手动写 setGeometry 定位控件,用布局。

4.1 三大基础布局

布局类排列方式典型用途
QHBoxLayout 水平排列 按钮行、工具栏
QVBoxLayout 垂直排列 表单、列表
QGridLayout 网格排列 计算器键盘、表格布局

4.2 QHBoxLayout(水平布局)

#include <QWidget>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>

/**
* @brief 演示水平布局
*/

QWidget *createHBoxDemo()
{
QWidget *widget = new QWidget;
QHBoxLayout *layout = new QHBoxLayout(widget);

layout->addWidget(new QPushButton("按钮1", widget));
layout->addWidget(new QPushButton("按钮2", widget));
layout->addWidget(new QLabel("标签", widget));
layout->addWidget(new QPushButton("按钮3", widget));

layout->addStretch(); // 添加弹性空间,把前面的控件挤到左边

return widget;
}

4.3 QVBoxLayout(垂直布局)

#include <QWidget>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QPushButton>

/**
* @brief 演示垂直布局(登录表单)
*/

QWidget *createLoginForm()
{
QWidget *widget = new QWidget;
QVBoxLayout *layout = new QVBoxLayout(widget);

layout->addWidget(new QLineEdit("用户名", widget));
layout->addWidget(new QLineEdit("密码", widget));

layout->addStretch(); // 弹性空间

QPushButton *loginBtn = new QPushButton("登录", widget);
layout->addWidget(loginBtn);

// 设置间距和边距
layout->setSpacing(10); // 控件间距
layout->setContentsMargins(20, 20, 20, 20); // 左、上、右、下边距

return widget;
}

4.4 QGridLayout(网格布局)

#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>

/**
* @brief 演示网格布局(计算器键盘)
*/

QWidget *createCalculatorKeypad()
{
QWidget *widget = new QWidget;
QGridLayout *layout = new QGridLayout(widget);

// 显示栏(跨4列)
QLineEdit *display = new QLineEdit("0", widget);
display->setAlignment(Qt::AlignRight);
display->setReadOnly(true);
layout->addWidget(display, 0, 0, 1, 4); // 行0, 列0, 占1行4列

// 按钮网格
QStringList buttons = {
"C", "±", "%", "÷",
"7", "8", "9", "×",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", ".", "="
};

int row = 1, col = 0;
for (const QString &text : buttons) {
QPushButton *btn = new QPushButton(text, widget);
if (text == "0") {
layout->addWidget(btn, row, col, 1, 2); // 0 占2列
col += 2;
} else {
layout->addWidget(btn, row, col);
++col;
}
if (col >= 4) { col = 0; ++row; }
}

layout->setSpacing(5);
return widget;
}

4.5 布局嵌套(复杂界面)

实际界面通常是布局嵌套布局。

#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QTextEdit>

/**
* @brief 演示布局嵌套(用户信息表单)
*/

QWidget *createComplexForm()
{
QWidget *widget = new QWidget;
QVBoxLayout *mainLayout = new QVBoxLayout(widget); // 最外层垂直

// 第一部分:基本信息(网格布局)
QGridLayout *infoLayout = new QGridLayout;
infoLayout->addWidget(new QLabel("姓名:", widget), 0, 0);
infoLayout->addWidget(new QLineEdit(widget), 0, 1);
infoLayout->addWidget(new QLabel("邮箱:", widget), 1, 0);
infoLayout->addWidget(new QLineEdit(widget), 1, 1);
infoLayout->addWidget(new QLabel("电话:", widget), 2, 0);
infoLayout->addWidget(new QLineEdit(widget), 2, 1);
mainLayout->addLayout(infoLayout); // 把布局加入布局

// 第二部分:备注(文本框)
mainLayout->addWidget(new QLabel("备注:", widget));
mainLayout->addWidget(new QTextEdit(widget));

// 第三部分:按钮行(水平布局,右对齐)
QHBoxLayout *btnLayout = new QHBoxLayout;
btnLayout->addStretch(); // 弹性空间把按钮挤到右边
btnLayout->addWidget(new QPushButton("取消", widget));
btnLayout->addWidget(new QPushButton("确定", widget));
mainLayout->addLayout(btnLayout);

mainLayout->setSpacing(10);
mainLayout->setContentsMargins(20, 20, 20, 20);
return widget;
}

4.6 伸缩因子(Stretch Factor)

QHBoxLayout *layout = new QHBoxLayout(widget);

QPushButton *btn1 = new QPushButton("短", widget);
QPushButton *btn2 = new QPushButton("长按钮", widget);

// 第二个参数是 stretch,值越大占的空间越多
layout->addWidget(btn1, 1); // 占 1 份
layout->addWidget(btn2, 2); // 占 2 份(宽度是 btn1 的两倍)

// 也可以用 setStretch
// layout->setStretch(0, 1);
// layout->setStretch(1, 2);

4.7 控件大小策略(SizePolicy)

QPushButton *btn = new QPushButton("按钮", widget);

// 水平方向:固定大小;垂直方向:可扩展
btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);

// 常用策略:
// QSizePolicy::Fixed 固定大小(取 sizeHint)
// QSizePolicy::Minimum 至少 sizeHint,可以更大
// QSizePolicy::Maximum 最多 sizeHint,可以更小
// QSizePolicy::Preferred 推荐 sizeHint,可以更大或更小(默认)
// QSizePolicy::Expanding 尽可能多占空间
// QSizePolicy::Ignored 忽略 sizeHint,尽可能多占

4.8 QFormLayout(表单专用布局)

虽然路线图没列,但做表单非常好用,补充一下。

#include <QFormLayout>
#include <QLineEdit>
#include <QSpinBox>

QFormLayout *form = new QFormLayout(widget);
form->addRow("姓名:", new QLineEdit(widget));
form->addRow("年龄:", new QSpinBox(widget));
form->addRow("邮箱:", new QLineEdit(widget));
// 自动对齐标签和输入框,比手写 QGridLayout 方便


五、常用控件速查

控件用途关键信号
QLabel 显示文本/图片 linkActivated
QPushButton 按钮 clicked
QLineEdit 单行输入 textChanged, editingFinished, returnPressed
QTextEdit 多行富文本 textChanged
QPlainTextEdit 多行纯文本(性能好) textChanged
QCheckBox 复选框 stateChanged, toggled
QRadioButton 单选按钮 toggled
QComboBox 下拉框 currentIndexChanged, currentTextChanged
QSpinBox 数字输入 valueChanged
QSlider 滑块 valueChanged
QProgressBar 进度条
QListWidget 列表 itemClicked, currentItemChanged
QTableWidget 表格 cellClicked, itemChanged
QTabWidget 标签页 currentChanged
QGroupBox 分组框
QScrollArea 滚动区域
QSplitter 可拖动分割 splitterMoved
QStatusBar 状态栏
QMenuBar 菜单栏
QToolBar 工具栏

六、核心要点总结

  • QWidget 是所有控件基类,既能当窗口也能嵌入其他控件。
  • QMainWindow 自带菜单栏/工具栏/状态栏/停靠区,做应用主窗口用它,必须设置 centralWidget。
  • QDialog 用于弹窗,exec() 模态阻塞,show() 非模态;标准对话框(QMessageBox 等)开箱即用。
  • QAction 统一菜单和工具栏,创建一次多处复用,共享图标、快捷键、信号。
  • 布局是必须的:永远用 QHBoxLayout/QVBoxLayout/QGridLayout,不要手动 setGeometry。
  • 布局嵌套:复杂界面 = 外层垂直布局 + 内层水平/网格布局,层层组合。
  • addStretch():添加弹性空间,把控件挤到一侧(右对齐、底部对齐常用)。
  • setContentsMargins 设置边距,setSpacing 设置控件间距。
  • SizePolicy 控制控件在布局中的拉伸行为,Expanding 尽可能多占空间。
  • 标准对话框:消息、输入、文件、颜色、字体都有现成的,不用自己写。

  • 下一章:资源系统与 QSS 样式

    赞(0)
    未经允许不得转载:171主机测评 » 04-Qt基础-常用控件与布局管理
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址