一、对话框
1、基本概念
对话框通常会是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互。
Qt 中使用QDialog类实现对话框。就像主窗口一样,我们通常会设计一个类继承QDialog。QDialog(及其子类,以及所有Qt::Dialog类型的类)的对于其 parent 指针都有额外的解释:如果 parent 为 NULL,则该对话框会作为一个顶层窗口,否则则作为其父组件的子对话框(此时,其默认出现的位置是 parent 的中心)。 顶层窗口与非顶层窗口的区别在于,顶层窗口在任务栏会有自己的位置,而非顶层窗口则会共享其父组件的位置。
对话框分为模态对话框和非模态对话框。
- 模态对话框:就是会阻塞同一应用程序中其它窗口的输入。不关闭当前窗口无法对其他窗口进行操作。
- 非模态对话框:打开之后还可以对其他窗口进行操作。
2、标准对话框
所谓标准对话框,是 Qt 内置的一系列对话框,用于简化开发。事实上,有很多对话框都是通用的,比如打开文件、设置颜色、打印设置等。这些对话框在所有程序中几乎相同,因此没有必要在每一个程序中都自己实现这么一个对话框。
Qt 的内置对话框大致分为以下几类:
- QColorDialog: 选择颜色;
- QFileDialog: 选择文件或者目录;
- QFontDialog: 选择字体;
- QInputDialog: 允许用户输入一个值,并将其值返回;
- QMessageBox: 模态对话框,用于显示信息、询问问题等;
- QPageSetupDialog: 为打印机提供纸张相关的选项;
- QPrintDialog: 打印机配置;
- QPrintPreviewDialog:打印预览;
- QProgressDialog: 显示操作过程。
1)消息对话框QMessageBox示例:
QMessageBox用于显示消息提示。我们一般会使用其提供的几个 static 函数:
- 显示关于对话框。
这是一个最简单的对话框,其标题是 title,内容是 text,父窗口是 parent。对话框只有一个 OK 按钮。
void about(QWidget * parent, const QString & title, const QString & text)
- 显示关于 Qt 对话框。该对话框用于显示有关 Qt 的信息。
void aboutQt(QWidget * parent, const QString & title = QString())
- 显示严重错误对话框。
这个对话框将显示一个红色的错误符号。我们可以通过 buttons 参数指明其显示的按钮。默认情况下只有一个 Ok 按钮,我们可以使用StandardButtons类型指定多种按钮。
QMessageBox::StandardButton critical(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton = NoButton)
int critical(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButton button0, QMessageBox::StandardButton button1)
//错误对话框示例
QMessageBox::critical(this,"critical","错误");
- 显示普通信息对话框。
这个对话框提供一个普通信息图标。
QMessageBox::StandardButton information(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton = NoButton)
QMessageBox::StandardButton information(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButton button0, QMessageBox::StandardButton button1 = NoButton)
//信息对话框示例
QMessageBox::information(this,"info","信息");
- 显示提问对话框。
这个对话框提供一个问号图标,并且其显示的按钮是“是”和“否”。
QMessageBox::StandardButton question(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = ..., QMessageBox::StandardButton defaultButton = NoButton)
int question(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButton button0, QMessageBox::StandardButton button1)
//提问对话框示例
QMessageBox::question(this,"quest","疑问");
QMessageBox::question(this,"quest","疑问",QMessageBox::Save|QMessageBox::Cancel);
//参数1:父亲 参数2:标题 参数3:提示内容 参数4:按钮类型 参数5:默认关联回车的按钮
QMessageBox::question(this,"quest","疑问",QMessageBox::Save|QMessageBox::Cancel,QMessageBox::Cancel);
- 显示警告对话框。
这个对话框提供一个黄色叹号图标。
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton = NoButton)
int warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButton button0, QMessageBox::StandardButton button1)
//警告对话框示例
QMessageBox::warning(this,"waring","警告");
返回值是QMessageBox::StandardButton类型,可以用来判断用户选择的按钮:
//判断用户选择的按钮
if( QMessageBox::Save
== QMessageBox::question(this,"quest","疑问",QMessageBox::Save|QMessageBox::Cancel,QMessageBox::Cancel) )
{
qDebug()<<"Save";
}
else
qDebug()<<"Cancel";
QMessageBox类的 static 函数优点是方便使用,缺点也很明显:非常不灵活。我们只能使用简单的几种形式。为了能够定制QMessageBox细节,我们必须使用QMessageBox的属性设置 API。如果我们希望制作一个询问是否保存的对话框,我们可以使用如下的代码:
QMessageBox msgBox;
msgBox.setText("The document has been modified.");
msgBox.setInformativeText("Do you want to save your changes?");
msgBox.setDetailedText("Differences here...");
msgBox.setStandardButtons(QMessageBox::Save
| QMessageBox::Discard
| QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret)
{
case QMessageBox::Save:
qDebug() << "Save document!";
break;
case QMessageBox::Discard:
qDebug() << "Discard changes!";
break;
case QMessageBox::Cancel:
qDebug() << "Close document!";
break;
}
msgBox 是一个建立在栈上的QMessageBox实例。我们设置其主要文本信息为“The document has been modified.”,informativeText 则是会在对话框中显示的简单说明文字。下面我们使用了一个detailedText,也就是详细信息,当我们点击了详细信息按钮时,对话框可以自动显示更多信息。我们自己定义的对话框的按钮有三个:保存、丢弃和取消。然后我们使用了exec()是其成为一个模态对话框,根据其返回值进行相应的操作。
2)QColorDialog示例:
返回选择的颜色
QColor getColor(const QColor &initial = Qt::white, QWidget *parent = nullptr, const QString &title = QString(), QColorDialog::ColorDialogOptions options = ...)
//颜色对话框 返回选择的颜色
QColor color = QColorDialog::getColor(QColor(255,0,0));
qDebug()<< "r = "<<color.red()<<" g = "<<color.green()<<" b = "<<color.blue();
3)QFileDialog示例:
返回值 选取的文件路径
QString getSaveFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = ...)
//文件对话框 //返回值 选取的文件路径
QFileDialog::getOpenFileName(this,"打开文件","D:/");
//参数1:父亲 参数2:标题 参数3:默认路径 参数4:默认过滤文件类型
QString str = QFileDialog::getOpenFileName(this,"打开文件","D:/","(*.txt)");
4)QFontDialog示例:
返回值选取的字体属性
QFont getFont(bool *ok, const QFont &initial, QWidget *parent = nullptr, const QString &title = QString(), QFontDialog::FontDialogOptions options = ...)
QFont getFont(bool *ok, QWidget *parent = nullptr)
//字体对话框
bool flag;
QFont font =QFontDialog::getFont(&flag,QFont("宋体",36));
qDebug() << "字体:"<<font.family().toUtf8().data() << " 字号:" <<font.pointSize()
<< " 是否加粗:"<< font.bold() << " 是否倾斜"<< font.italic();
3、自定义消息框
支持模态对话框和非模态对话框。
- 使用QDialog::exec()实现应用程序级别的模态对话框
- 使用QDialog::open()实现窗口级别的模态对话框
- 使用QDialog::show()实现非模态对话框。
1)模态对话框
Qt 有两种级别的模态对话框:
- 应用程序级别的模态
当该种模态的对话框出现时,用户必须首先对对话框进行交互,直到关闭对话框,然后才能访问程序中其他的窗口。 - 窗口级别的模态
该模态仅仅阻塞与对话框关联的窗口,但是依然允许用户与程序中其它窗口交互。窗口级别的模态尤其适用于多窗口模式。
一般默认是应用程序级别的模态。
示例:
//模态创建 阻塞
QDialog dlg(this);
dlg.setWindowTitle("test");
dlg.resize(200,100);
dlg.exec();
调用了exec()将对话框显示出来,因此这就是一个模态对话框。当对话框出现时,我们不能与主窗口进行任何交互,直到我们关闭了该对话框。
2)非模态对话框
非模态对话框需要把.exec()换成.show(),但是show()函数不会阻塞当前线程,对话框会显示出来,然后函数立即返回,代码继续执行。若将QDialog类建立在栈上,show()函数返回,函数结束,QDialog类对象超出作用域被析构,因此对话框会消失了(一闪而过)。因此需要建立堆上的对象。
示例如下:
//非模态对话框
QDialog *dlg2 = new QDialog(this);//用栈区模式创建会一闪而过 需要用堆区创建
dlg2->resize(200,100);
dlg2->show();
dlg2->setAttribute(Qt::WA_DeleteOnClose);//设置属性,在关闭的时候释放 防止内存泄漏
但由于主窗口作为parent时,主窗口不关闭,对话框就不会被销毁,所以会一直占用内存,容易引发内存泄漏。在这种情景下,我们可以设置 dialog 的WindowAttribute,setAttribute()函数设置Qt::WA_DeleteOnClose属性,对话框关闭时,自动销毁对话框。
4、一个简单的文本文件编辑器
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
#include <QDebug>
#include <QMessageBox>
#include <QColorDialog>
#include <QFileDialog>
#include <QFontDialog>
#include <QString>
#include <QToolBar>
#include <QStatusBar>
#include <QLabel>
#include <QFile>
#include <QAction>
#include <QTextStream>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
resize(800,600);
//文本编辑功能的窗口
//设置提示
ui->actionopen->setStatusTip("Open an file");
ui->actionsave->setStatusTip("Save an file");
//工具栏
QToolBar * toolBar = new QToolBar(this);
addToolBar(Qt::LeftToolBarArea,toolBar);
toolBar->setFloatable(false);
// QAction* Open = new QAction("OPEN",this);
toolBar->addAction(ui->actionopen);
toolBar->addAction(ui->actionsave);
//状态栏
QStatusBar *stBar = statusBar();
setStatusBar(stBar);
QLabel *label = new QLabel("文本编辑工具");
stBar->addWidget(label);
//将QTextEdit类组件作为窗口的中心组件,放在窗口中央显示区。
textEdit = new QTextEdit(this);
setCentralWidget(textEdit);
//使用connect()函数,为这两个QAction对象添加响应的动作
connect(ui->actionopen,&QAction::triggered,this,&MainWindow::openFile);
connect(ui->actionsave,&QAction::triggered,this,&MainWindow::saveFile);
}
//打开文件
void MainWindow::openFile()
{
QString path = QFileDialog::getOpenFileName(this,"Open File","D:/");
if(!path.isEmpty())
{
QFile file(path);
if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
{
QMessageBox::warning(this,"Read File","Open file error");
return;
}
QTextStream in(&file);
textEdit->setText(in.readAll());
file.close();
}
else
{
QMessageBox::warning(this,"Path","Need to select any file");
}
return;
}
//保存文件
void MainWindow::saveFile()
{
QString path = QFileDialog::getSaveFileName(this,"Open File","D:/","(.txt)");
if(!path.isEmpty())
{
QFile file(path);
if(file.open(QIODevice::WriteOnly|QIODevice::Text))
{
QMessageBox::warning(this,"Write File","Can't open file");
return;
}
QTextStream out(&file);
out<<textEdit->toPlainText();
file.close();
}
else
{
QMessageBox::warning(this,"Path","Need to select any file");
}
return;
}
MainWindow::~MainWindow()
{
delete ui;
}
在openFile()函数中,我们使用QFileDialog::getOpenFileName()来获取需要打开的文件的路径。这个函数原型如下:
QString getOpenFileName(QWidget * parent = 0,
const QString & caption = QString(),
const QString & dir = QString(),
const QString & filter = QString(),
QString * selectedFilter = 0,
Options options = 0)
它的所有参数都是可选的,因此在一定程度上说,这个函数也是简单的。这六个参数分别是:
-
parent:父窗口。
Qt 的标准对话框提供静态函数,用于返回一个模态对话框; -
caption:对话框标题;
-
dir:对话框打开时的默认目录
- “.” 代表程序运行目录
- “/” 代表当前盘符的根目录(特指 Windows 平台;Linux 平台当然就是根目录),这个参数也可以是平台相关的,比如“C:\”等;
-
filter:过滤器。
用于过滤特定的后缀名。如果我们使用“Image Files(.jpg .png)”,则只能显示后缀名是 jpg 或者 png 的文件。如果需要多个过滤器,使用“;;”分割,比如“JPEG Files(.jpg);;PNG Files(.png)”; -
selectedFilter:默认选择的过滤器;
-
options:对话框的一些参数设定
比如只显示文件夹等等,它的取值是enum QFileDialog::Option,每个选项可以使用 | 运算组合起来。
QFileDialog::getOpenFileName()返回值是选择的文件路径。我们将其赋值给 path。通过判断 path 是否为空,可以确定用户是否选择了某一文件。只有当用户选择了一个文件时,我们才执行下面的操作。
在saveFile()中使用的QFileDialog::getSaveFileName()也是类似的。使用这种静态函数,在 Windows、Mac OS 上面都是直接调用本地对话框,但是 Linux 上则是QFileDialog自己的模拟。这暗示了,如果你不使用这些静态函数,而是直接使用QFileDialog进行设置,那么得到的对话框很可能与系统对话框的外观不一致。这一点是需要注意的。