(10)功能实现:Qt实战项目之新建项目对话框

上一章节在掌握了信号与槽这一 Qt 核心机制后,接下来就让我们动手落地第一个实战功能 ------ 为菜单"新建项目" 实现业务逻辑。

本节课的实战目标

本次课程我们要实现新建项目的功能,具体如下:

  • 点击新建项目,打开一个新建项目的对话框;
  • 新建项目对话框中需要用户输入项目名称、选择项目的存放位置,并允许用户进行确定和取消;
  • 对新建项目中的标签、输入框、按钮等按照图例进行布局;

为了实现 上面的"新建项目" 这个功能,我们需要引入一个新的、非常常用的界面组件类 ------ QDialog。接下来,我们就将结合信号与槽,一起学习如何使用 QDialog 来创建一个对话框,并完成 "新建项目" 的整个逻辑流程。

QDialog 是什么?

你可以把 QDialog 理解为一个专门用于与用户进行短期交互的窗口。它通常不会像主窗口(QMainWindow)那样承载整个应用的主要功能和菜单工具栏,而是在需要时弹出,完成特定任务后就关闭。

下图是QDialog的继承关系,理解继承关系有助于你掌握 QDialog 的功能来源和在 Qt 控件体系中的位置。

让我们逐层解析:
QObject:

  • 位置: 继承链的最顶端。
  • 核心功能: QObject 是 Qt 中所有支持信号与槽(Signals & Slots)机制的类的基类。它也提供了对象树管理、事件处理、动态属性等核心功能。
  • 对 QDialog 的意义: 正因为继承自 QObject,QDialog 及其子类才能使用信号与槽进行通信,例如,当用户点击对话框上的 "确定" 按钮时,发出一个信号。

QWidget:

  • 位置: 直接父类。
  • 核心功能: QWidget 是 Qt 中所有用户界面对象(控件)的基类。一个 QWidget 可以理解为一个 "空白画布",它可以显示在屏幕上,接收鼠标、键盘事件,并可以作为其他控件的容器。它提供了窗口的基本特性,如大小、位置、显示 / 隐藏、样式表支持等。
  • 对 QDialog 的意义: 继承自 QWidget 意味着 QDialog 本质上是一个可以独立存在的窗口控件。它拥有 QWidget 的所有能力,比如可以设置背景色、可以安装事件过滤器、可以包含其他 QWidget(如 QPushButton, QLabel)作为其子控件。

QDialog:

  • 位置: 我们讨论的类本身。
  • 核心功能: QDialog 在 QWidget 的基础上进行了专门化,以更好地服务于 "对话框" 这一特定场景。它并没有从零开始,而是继承了 QWidget 的所有通用功能,然后添加了自己的特性,例如:
  • 内置的模态行为支持 (exec())。
  • 与父窗口的关系管理,使其可以作为子对话框弹出。
  • 对标准按钮和对话框结果(accept() / reject())的标准化处理。
  • 更适合对话框的默认窗口标志和样式。

自定义对话框类

我们在工程项目中添加一个自定义类,命名为:NewProjectDlg继承自QDialog。

添加类以后,需要加上Q_OBJECT宏,因为我们要用的Qt的信号与槽的机制,就必须加上这个宏,然后完成构造函数和析构函数的定义:

cpp 复制代码
#pragma once
#include <QDialog>
class NewProjectDlg :
    public QDialog
{
    Q_OBJECT

public:
	NewProjectDlg(QWidget* parent = nullptr);
	~NewProjectDlg();
};

接着我们来重新写一下"新建项目"这个菜单的槽函数,如下:

cpp 复制代码
void SubCfgTool::createProject()
{
	NewProjectDlg* newProDlg = new NewProjectDlg(this);
	int ret = newProDlg->exec();// 构建模态对话框
	//qDebug() << " project has created!";
}

这里说明一下,对话框的构建分为两类,模态对话框和非模态对话框:

  • 模态对话框: 这是最常见的类型。当它弹出时,会阻止用户与父窗口或应用程序的其他部分进行交互,直到用户关闭这个对话框。例如,你在 Word 中选择 "保存" 时弹出的保存对话框就是模态的。QDialog::exec() 是显示模态对话框的标准方式。
  • 非模态对话框: 弹出后,用户仍然可以与应用程序的其他窗口进行交互。例如,一些应用中的 "查找" 窗口。QDialog::show() 可以用来显示非模态对话框,但通常需要配合 setModal(false) 和事件循环管理。

测试运行一下效果,点击新建项目如果弹出一个空白的对话框,说明这一步成功。

初始化控件及布局

1、设置标题名称、对话框的大小

在自定义对话框的构造函数中设置对话框的标题和大小,如下:

cpp 复制代码
NewProjectDlg::NewProjectDlg(QWidget* parent /*= nullptr*/)
{
	setWindowTitle(QStringLiteral("新建项目"));
	setFixedSize(400, 150);
}

注意:如果中文的标题名称不显示,记得将cpp文件设置高级保存选项,更改编码为:Unicode(UTF-8 带签名)-代码页 65001,具体的操作前面的章节已经介绍过,可以翻阅。

2、添加控件及布局

先定义控件,涉及的控件有QLable、QLineEdit、QPushbutton,引入头文件

#include <QLabel>

#include <QLineEdit>

#include <QPushButton>

并开始控件的定义:

cpp 复制代码
	QLabel* labelName = new QLabel(QStringLiteral("项目名称:"));
	QLabel* labelPath = new QLabel(QStringLiteral("项目位置:"));

	lineEdtProName = new QLineEdit;
	lineEdtProDirPath = new QLineEdit;

	QPushButton* pushBtnChoosePath = new QPushButton(QStringLiteral("选择"));
	QPushButton* pushBtnOk = new QPushButton(QStringLiteral("确定"));
	QPushButton* pushBtnCancel = new QPushButton(QStringLiteral("取消"));

如何布局这些控件的位置,以达到我们想要的效果呢。这里就需要用到布局管理器,常用的布局管理器有:水平布局管理器(QHBoxLayout)、垂直布局管理器(QVBoxLayout)、网格布局管理器(QGridLayout),如下:

在本节课的实战场景中,我们会用到这三个布局管理器,引入一下头文件:

#include <QGridLayout>

#include <QHBoxLayout>

#include <QVBoxLayout>

其中QGridLayout 是 Qt 中最灵活、最常用的布局管理器之一,核心思想是将界面分割成 "行 × 列" 的网格单元格,每个控件可以占用一个或多个单元格,就像 Excel 表格一样精准排列组件。

我们先来实现一下,内部的网络布局的部分,代码如下:

cpp 复制代码
    QGridLayout *layout1 = new QGridLayout;
    layout1->addWidget(labelName, 0, 0, 1, 2);
    layout1->addWidget(lineEdtProName, 0, 2, 1, 4);
    layout1->addWidget(labelPath, 1, 0, 1, 2);
    layout1->addWidget(lineEdtProDirPath, 1, 2, 1, 3);
    layout1->addWidget(pushBtnChoosePath, 1, 5, 1, 1);

接下来我们实现按钮部分的水平布局,如下:

我们需要在按钮左侧加入一个可伸缩的区域,如下:

cpp 复制代码
	QHBoxLayout* layout2 = new QHBoxLayout();
	layout2->addStretch(1);
	layout2->addWidget(pushBtnOk);
	layout2->addWidget(pushBtnCancel);

最后再完成总体的垂直方向的布局,如下:

cpp 复制代码
    QVBoxLayout* layout = new QVBoxLayout();
    layout->addLayout(layout1);
    layout->addLayout(layout2);

    setLayout(layout);

运行代码查看效果。 发现界面的样式不太美观,我们在Main函数中加入一行代码:
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); //优化界面显示的清晰度

3、添加控件的响应逻辑

  • 选择目录

定义一个选择目录的槽函数:

cpp 复制代码
public slots:
    void selectProjectDirPath();

将槽函数绑定到选择目录按钮的点击事件:

cpp 复制代码
connect(pushBtnChoosePath, &QPushButton::clicked, this, &NewProjectDlg::selectProjectDirPath);

selectProjectDirPath()槽函数的实现,需要用到QFileDialog类,这个类是Qt 框架中用于实现文件系统对话框的核心类,支持用户选择文件、目录或保存路径,最后将选择的目录添加到文本框中显示,如下:

cpp 复制代码
void NewProjectDlg::selectProjectDirPath()
{
	QString strDirPath = QFileDialog::getExistingDirectory(this, QStringLiteral("选择存放路径"), "/");
	if (strDirPath.isEmpty())
	{
		return;
	}
	else
	{
		strDirPath += "/";
		lineEdtProDirPath->setText(strDirPath);
	}
}
  • 确认、取消

参考Qt官方文档,Qt已经为我们提供了两个槽函数,并声明为虚函数。虚函数允许子类进行重写。

  • accept()(返回QDialog::Accepted),表示确定
  • reject()(返回QDialog::Rejected),表示取消

我们的确定、取消按钮,分别绑定这两个槽函数:

cpp 复制代码
	connect(pushBtnOk, &QPushButton::clicked, this, &NewProjectDlg::accept);
	connect(pushBtnCancel, &QPushButton::clicked, this, &NewProjectDlg::reject);

重写一下accept函数,即点击了确定之后执行的内容,其中如何创建工程的核心逻辑结合不同的项目自行定义,我们下一节课讲解本案例的核心实现逻辑。注意最终需要调用QDialog::accept();,因为重写了父类的accept方法,如果不调用父类的这个accept方法,返回值将不能正常返回QDialog::Accepted

cpp 复制代码
void NewProjectDlg::accept()
{
	//TODO: 创建工程的核心逻辑

	QDialog::accept();
}

运行测试一下效果,本章节的对话框创建就算完成了,本章的代码会放入评论区,欢迎大家下载。

相关推荐
MediaTea32 分钟前
Python 编程B17:文件(二)
开发语言·python
w***954935 分钟前
在21世纪的我用C语言探寻世界本质——字符函数和字符串函数(2)
c语言·开发语言
ironinfo36 分钟前
C#性能优化随记
开发语言·性能优化·c#
无限进步_38 分钟前
基于顺序表的通讯录系统设计与实现
c语言·开发语言·数据结构·c++·后端·算法·visual studio
e***193541 分钟前
QoS质量配置
开发语言·智能路由器·php
宠..1 小时前
使用纯代码设计界面
开发语言·c++·qt
froginwe111 小时前
SQL ALTER 语句详解
开发语言
ALex_zry1 小时前
内核开发者的视角:C与Rust在系统编程中的哲学与实践
c语言·开发语言·rust
u***45161 小时前
Windows安装Rust环境(详细教程)
开发语言·windows·rust