【3】对话框 QDialog + 布局管理器
六、对话框 QDialog
6.1 基本概念
对话框是 GUI 程序中不可或缺的组成部分。很多不能或者不适合放入主窗口的功能组件都必须放在对话框中设置。对话框通常会是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互。
Qt 中使用 QDialog 类实现对话框。就像主窗口一样,通常会设计一个类继承 QDialog。QDialog(及其子类,以及所有 Qt::Dialog 类型的类)的对于其 parent 指针都有额外的解释:如果 parent 为 NULL,则该对话框会作为一个顶层窗口,否则则作为其父组件的子对话框(此时,其默认出现的位置是 parent 的中心)。顶层窗口与非顶层窗口的区别在于,顶层窗口在任务栏会有自己的位置,而非顶层窗口则会共享其父组件的位置。
对话框分为模态对话框和非模态对话框。
-
模态对话框,会阻塞同一应用程序中其它窗口的输入,不可以对其他窗口进行操作,只能对当前窗口操作。模态对话框很常见,比如“打开文件”功能。你可以尝试一下记事本的打开文件,当打开文件对话框出现时,不能对除此对话框之外的窗口部分进行操作的。
-
非模态对话框与此相反,可以对其他窗口进行操作。例如查找对话框,可以在显示着查找对话框的同时,继续对记事本的内容进行编辑。
【示例】
使用 ui 界面给【菜单栏】创建【菜单】,添加点击事件,即点击菜单弹出对话框
【模式对话框示例】
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//点击新建菜单项 弹出对话框
connect(ui->actionNew,&QAction::triggered,[=](){
//模态创建 阻塞
QDialog dlg(this);
dlg.resize(200,100);
dlg.exec(); // 🍓 模态显示函数
qDebug() <<"hello windows"; // 🍓 对话框关闭后才会输出
});
}
MainWindow::~MainWindow()
{
delete ui;
}
【非模式对话框示例】
//点击新建菜单项 弹出对话框
connect(ui->actionNew,&QAction::triggered,[=](){
//非模态创建 不阻塞 【对话框不关闭,也可以操作其他窗口】
QDialog *dlg = new QDialog(this);
dlg->resize(200,100);
dlg->show(); // 🍓 显示函数改变
dlg->setAttribute(Qt::W A_DeleteOnClose); // 🍓🍓 该语句【关闭时释放资源】放置反复点击新建,造成内存溢出
qDebug() <<"hello windows"; // 🍓 直接输出,不会阻塞
}
);
6.2 标准对话框
所谓标准对话框,是 Qt 内置的一系列对话框,用于简化开发。事实上,有很多对话框都是通用的,比如打开文件、设置颜色、打印设置等。这些对话框在所有程序中几乎相同,因此没有必要在每一个程序中都自己实现这么一个对话框。
Qt 的内置对话框大致分为以下几类:
-
QColorDialog
: 选择颜色; -
QFileDialog
: 选择文件或者目录; -
QFontDialog
: 选择字体; -
QInputDialog
: 允许用户输入一个值,并将其值返回; -
QMessageBox
: 模态对话框,用于显示信息、询问问题等; -
QPageSetupDialog
: 为打印机提供纸张相关的选项; -
QPrintDialog
: 打印机配置; -
QPrintPreviewDialog
:打印预览; -
QProgressDialog
: 显示操作过程。
6.3 消息对话框
QMessageBox用于显示消息提示,头文件 #include <QMessageBox>
。
【Static Public Members】
StandardButton critical(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
StandardButton information(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
StandardButton question(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = StandardButtons( Yes | No ), StandardButton defaultButton = NoButton)
StandardButton warning(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
* 第一个参数:对话框的父窗口
* 第二个参数:对话框的标题
* 第三个参数:对话框显示内容
* 第四个参数:按钮类型,指明显示的按钮种类
* 第五个参数:默认指定的按钮
【示例 1】
【示例 2】
第四个参数,修改显示的按钮。如其默认按钮显示为 OK
。
QMessageBox::critical(this,"critical","错误",QMessageBox::Open);
按钮修改为 Open
。
第四个参数的值有:
QMessageBox::Ok | QMessageBox::Open | QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Close |
QMessageBox::Discard | QMessageBox::Apply | QMessageBox::Reset | QMessageBox::Help | QMessageBox::Yes |
QMessageBox::SaveAll | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Abort |
QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::NoButton | QMessageBox::RestoreDefaults |
【示例 3】
提问对话框
connect(ui->actionNew,&QAction::triggered,[=](){
//提问对话框
//参数1 父亲 参数2 标题 参数3 提示内容 参数4 按键类型 参数5 默认关联回车按键
if (QMessageBox::Save == QMessageBox::question(this,"ques","提问",QMessageBox::Save|QMessageBox::Cancel,QMessageBox::Cancel))
{
qDebug() << "选择的是保存";
}
else
{
qDebug() << "选择的是取消";
}
});
6.4 其他对话框
【颜色对话框】
#include <QColorDialog>
//点击新建菜单项 弹出颜色对话框
connect(ui->actionNew,&QAction::triggered,[=](){
//颜色对话框
QColor color = QColorDialog::getColor(QColor(255,0,0)); //默认颜色为红色
//输出选中的颜色信息
qDebug() << "r = " << color.red() << " g = " << color.green() << " b = " << color.blue() ;
});
【字体对话框】
//点击新建菜单项 弹出字体对话框
connect(ui->actionNew,&QAction::triggered,[=](){
bool flag;
QFont font = QFontDialog::getFont(&flag,QFont("华文彩云",36));
qDebug() << "字体:" << font.family().toUtf8().data() << " 字号 "<< font.pointSize() << " 是否加粗"<< font.bold() << " 是否倾斜"<<font.italic();
});
【文件对话框】
//点击新建菜单项 弹出文件对话框
connect(ui->actionNew,&QAction::triggered,[=](){
//文件对话框
QString str = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\Guo\\Desktop");
qDebug() << str;
});
对弹出的文件对话框,过滤可见文件,只过滤文件,文件夹不过滤(统一显示)
// 显示的文件对话框只有文件夹与txt文件
connect(ui->actionNew,&QAction::triggered,[=](){
//文件对话框
QString str = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\Guo\\Desktop","(*.txt)");
qDebug() << str;
});
6.5 自定义消息框
Qt 支持模态对话框和非模态对话框。
模态与非模态的实现:
- 使用
QDialog::exec()
实现应用程序级别的模态对话框 - 使用
QDialog::open()
实现窗口级别的模态对话框 - 使用
QDialog::show()
实现非模态对话框。
6.5.1 模态对话框
Qt 有两种级别的模态对话框:
-
应用程序级别的模态
- 当该种模态的对话框出现时,用户必须首先对对话框进行交互,直到关闭对话框,然后才能访问程序中其他的窗口。(阻塞所有窗口)
-
窗口级别的模态
- 该模态仅仅阻塞与对话框关联的窗口,但是依然允许用户与程序中其它窗口交互。窗口级别的模态尤其适用于多窗口模式。
一般默认是应用程序级别的模态。
在下面的示例中,调用了 exec()
将对话框显示出来,因此这就是一个模态对话框。当对话框出现时,不能与主窗口进行任何交互,直到关闭该对话框。
QDialog dialog;
dialog.setWindowTitle(tr("Hello, dialog!"));
dialog.exec();
6.5.2 非模态对话框
将 exec()
修改为 show()
,看看非模态对话框:
QDialog dialog(this);
dialog.setWindowTitle(tr("Hello, dialog!"));
dialog.show();
对话框一闪而过!因为,show()函数不会阻塞当前线程,对话框会显示出来,然后函数立即返回,代码继续执行。注意,dialog 是建立在栈上的,show()函数返回,MainWindow::open()函数结束,dialog 超出作用域被析构,因此对话框消失了。
QDialog *dialog = new QDialog;
dialog->setWindowTitle(tr("Hello, dialog!"));
dialog->show();
上面的代码是有问题的:dialog 存在内存泄露!dialog 使用 new 在堆上分配空间,却一直没有 delete。解决方案很简单:将 MainWindow 的指针赋给 dialog 即可,系统会自动回收。
但是,这样做仍然会存在问题:如果对话框不是在一个界面类中出现呢?由于 QWidget 的 parent 必须是 QWidget 指针,那就限制不能将一个普通的 C++ 类指针传给 Qt 对话框。另外,如果对内存占用有严格限制的话,当将主窗口作为 parent 时,主窗口不关闭,对话框就不会被销毁,所以会一直占用内存。在这种情景下,可以设置 dialog 的WindowAttribute:
QDialog *dialog = new QDialog;
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowTitle(tr("Hello, dialog!"));
dialog->show();
setAttribute()函数设置对话框关闭时,自动销毁对话框。
七、布局管理器
所谓 GUI 界面,归根结底,就是一堆组件的叠加。在设计界面创建一个窗口,把按钮放上面,把图标放上面,这样就成了一个界面。在放置时,组件的位置尤其重要。必须要指定组件放在哪里,以便窗口能够按照需要的方式进行渲染。这就涉及到组件定位的机制。
Qt 提供了两种组件定位机制:绝对定位和布局定位。
-
绝对定位就是一种最原始的定位方法:给出这个组件的坐标和长宽值。
- 这样,Qt 就知道该把组件放在哪里以及如何设置组件的大小。但是这样做带来的一个问题是,如果用户改变了窗口大小,比如点击最大化按钮或者使用鼠标拖动窗口边缘,采用绝对定位的组件是不会有任何响应的。可以禁止用户改变窗口大小。但这并不通用。
-
布局定位:只要把组件放入某一种布局,布局由专门的布局管理器进行管理。当需要调整大小或者位置的时候,Qt 使用对应的布局管理器进行调整。布局定位完美的解决了使用绝对定位的缺陷。Qt 提供的布局中以下三种是最常用的:
-
QHBoxLayout
:按照水平方向从左到右布局; -
QVBoxLayout
:按照竖直方向从上到下布局; -
QGridLayout
:在一个网格中进行布局,类似于 HTML 的 table;
-
【系统提供的布局控件】
【利用 widget 做布局】
第二种布局方式是利用 Containers 中里的 widget 来做布局