这一节主要讲述对话框类,先讲述两种不同类型的对话框,再介绍Qt提供的几个标准对话框。对应本节的内容,可以在帮助索引中查看 QDialog 和 Dialog Windows 关键字。
一、模态和非模态对话框
QDialog 类是所有对话框窗口类的基类。对话框窗口是一个经常用来完成一个短小任务或者和用户进行简单交互的顶层窗口。按照运行对话框时是否还可以和该程序的其他窗口进行交互,对话框常被分为两类,模态的(modal)和非模态的(modeless)。 关于这两个概念,下面先看一个例子。
新建 Qt Gui 应用,项目名称为 myDialog1,类名为 MyWidget,基类选择 QWidget。然后在 mywidget. cpp 文件中添加代码:
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QDialog>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
QDialog *dialog = new QDialog(this);
dialog->show();
}
MyWidget::~MyWidget()
{
delete ui;
}
运行程序,dialog和MyWidget窗口都显示出来,此时dialog是非模态对话框。
将代码更改如下:
QDialog dialog(this);
dialog.exec();
这时运行程序就会发现对话框弹出来了,但是 MyWidget 类对象的窗口并没有出来;关闭这个对话框后,MyWidget类对象的窗口才弹出来。这个与上面的那个对话框的效果不同,称它为模态对话框。
模态对话框就是没有关闭它之前,不能再与同一个应用程序的其他窗口进行交互, 比如新建项目时弹出的对话框。而对于非模态对话框,既可以与它交互,也可以与同一 程序中的其他窗口交互,例如 Microsoft Word 中的查找替换对话框。就像前面看到的,要想使一个对话框成为模态对话框,只需要调用它的 exec() 函数,而要使其成为非模态对话框,可以使用 new 操作来创建,然后使用 show() 函数来显示。其实使用 show() 函数也可以建立模态对话框,只需在其前面使用 setModal() 函数即可。例如:
QDialog *dialog = new QDialog(this);
dialog->setModal(true);
dialog->show();
现在运行程序就可以看到生成的对话框是模态的。但是,它与用exec()函数时的效果是不一样的,因为现在的 MyWidget 对象窗口也显示出来了。这是因为调用完 show() 函数后会立即将控制权交给调用者,那么程序可以继续往下执行。而调用 exec () 函数却不同,它只有当对话框被关闭时才会返回。
与 setModal() 函数相似的还有一 个 setWindowModality() 函数,它有一个参数来设置模态对话框要阻塞的窗口类型,可以是Qt::NonModal(不阻塞任何窗口,就是非模态)、Qt::WindowModal(阻塞它的父窗口和所有祖先窗口以及它们的子窗口)、Qt::ApplicationModal(阻塞整个应用程序的所有窗口)三者之一。而 setModal() 函数默认设置的是 Qt: :ApplicationModal。
二、标准对话框
Qt 提供了一些常用的对话框类型,全部继承自 QDialog 类,并增加了自己的特色功能,比如获取颜色、显示特定信息等。
这里新建一个项目,首先新建 Qt Gui 应 用,项目名称为 myDialog2,类名改为 MyWidget,基类选择 QWidget。双击 mywidget.ui 文件进入设计模式,在界面上添加一些按钮,如图2 - 1所示。然后回到编辑模式, 在main. cpp 中添加支持中文的代码。
2.1 颜色对话框
颜色对话框类 QColorDialog 提供了一个可以获取指定颜色的对话框部件。下面创建一个颜色对话框。先在 mywidget. cpp 文件中添加 #include <QDebug> 和 #include <QColorDialog> 头文件,然后从设计模式进人“颜色对话框”按钮的 clicked() 单击信号槽。更改如下:
//颜色对话框
void MyWidget::on_pushButton_clicked()
{
/*** 第一种方式 ***/
//QColor color = QColorDialog::getColor(Qt::red, this, tr("颜色对话框"),QColorDialog::ShowAlphaChannel);
//qDebug() <<"color:" <<color;
/*** 第二种方式 ***/
QColorDialog dialog(Qt::red, this); //创建对象
dialog.setOption(QColorDialog::ShowAlphaChannel); //显示 alpha 选项
dialog.exec(); //以模态方式运行对话框
QColor color = dialog.currentColor(); //获取颜色对话框当前颜色
qDebug() <<"color:" <<color; //输出颜色信息
}
第一种方式,使用了 QColorDialog 的静态函数 getColor() 来获取颜色,它的3个参数的作用分别是设置初始颜色、父窗口和对话框标题。现在运行程序,然后单击“颜色对话框”按钮,显示出如图2-2 所示的颜色对话框。第一种方式的好处是不用不用创建对象。但是如果想要更灵活地设置时,采用第二种方式,先创建对象,然后进行各项设置。两者的实现效果是等效的。
2.2 文件对话框
文件对话框类 QFileDialog 提供了一个允许用户选择文件或文件夹的对话框。继续添加 #include <QFileDialog>头文件,然后进入“文件对话框”按钮的单击信号槽,并更改如下:
//文件对话框
void MyWidget::on_pushButton_2_clicked()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("文件对话框"), "F:",tr("图片文件(* png * jpg)"));
qDebug() <<"fileName:" <<fileName;
}
这里使用了 QFileDialog 类中的 getOpenFileName() 函数来获取选择的文件名,这个函数会以模态方式运行一个文件对话框,然后选择一个文件,单击“打开”按钮后,这个函数便可以返回选择的文件的文件名。它的4个参数的作用分别是:指定父窗口、 设置对话框标题、指定默认打开的目录路径和设置文件类型过滤器。如果不指定文件过滤器,默认是选择所有类型的文件。这里只选择png和jpg两种格式的图片文件(注意代码中* png和* jpg之间需要一个空格),那么在打开的文件对话框中只能显示目录下这两种格式的文件。还可以设置多个不同类别的过滤器,不同类别间使用两个分 号“;;”隔开,例如添加文本文件类型:
QString fileName = QFileDialog::getOpenFileName(this, tr("文件对话框"), "F:",tr("图片文件(* png * jpg);;文本文件(* txt)"));
前面这个程序只能选择单个文件,要同时选择多个文件,可以使用 getOpenFileNames() 函数, 例如:
//文件对话框
void MyWidget::on_pushButton_2_clicked()
{
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("文件对话框"), "F:",tr("图片文件(* png * jpg);;文本文件(* txt)"));
qDebug() <<"fileNames:" <<fileNames;
}
这时运行程序就可以同时选择多个图片文件了,多个文件名存放在 QStringList 类型变量中。也可以不使用这些静态函数,而是建立对话框对象来操作。除了上面的两个函数,QFileDialog 类还提供了 getSaveFileName() 函数来实现保存文件对话框和文件另存为对话框,还有 getExistingDirectory() 函数来获取一个已存在的文件夹路径。它们的用法与上面的例子类似,这里就不再举例。
2.3 字体对话框
字体对话框 QFontDialog 类提供了一个可以选择字体的对话框部件。先添加 #include <QFontDialog>头文件,然后转到“字体对话框”按钮的单击信号槽,更改如下:
//字体对话框
void MyWidget::on_pushButton_3_clicked()
{
//ok 用于标记是否单击了 OK 按钮。然后获得选择的字体
bool ok;
QFont font = QFontDialog::getFont(&ok, this);
//如果单击 Cancel 按钮,那么更改字体
if (ok)
ui->pushButton_3->setFont(font);
else
qDebug() <<tr("没有选择字体!");
}
这里使用了 QFileDidog 类的 getFont() 静态函数来获取选择的字体。第一个参数是 bool 类型变量,用来存放按下的按钮状态,比如在打开的字体对话框中单击了 OK 按钮,那么这里的 ok 就为 true,从而告诉用户已经选择了字体。
2.4 输入对话框
输人对话框 QInputDialog 类用来提供一个简单方便的对话框,从而从用户那里获取一个单一的数值或字符串。先添加头文件 #include <QlnputDialog>,然后进人“输人对话框”按钮的单击信号槽,更改如下:
// 输入对话框
void MyWidget::on_pushButton_4_clicked()
{
bool ok;
//获取字符串
QString string = QInputDialog::getText(this, tr("输入字符串对话框"),tr("请输入用户名:"),QLineEdit::Normal, tr("admin"), &ok);
if(ok)
qDebug() <<tr("string:") <<string;
//获取整数
int value1 = QInputDialog::getInt(this, tr("输入整数对话框"),tr("请输入 -1000到1000之间的数值"), 100, -1000, 1000, 10, &ok);
if(ok)
qDebug() <<tr("value1:") <<value1;
// 获取浮点数
double value2 = QInputDialog::getDouble(this, tr("输入浮点数对话框"),tr("请输入-1000到1000之间的数值"), 0.00, -1000, 1000, 2, &ok);
if(ok) qDebug() << "value2:" << value2;
QStringList items;
items << tr("条目1") << tr("条目2");
// 获取条目
QString item = QInputDialog::getItem(this, tr("输入条目对话框"),tr("请选择或输入一个条目"), items, 0, true, &ok);
if(ok) qDebug() << "item:" << item;
}
这里一共创建了 4 个不同类型的输入对话框。
- getText() 函数可以提供一个可输入字符串的对话框,参数的作用分别是:指定父窗口、设置窗口标题、设置对话框中的标签的显示文本、设置输入的字符串的显示模式(例如密码可以显示成小黑点,这里选择了显示用户输人的实际内容)、设置输人框中的默认字符串和设置获取按下按钮信息的 bool 变量。
- getInt() 函数可以提供一个输入整型数值的对话框,其中的参数100表示默认的数值是100,-1000表示可输入的最小值是-1000,1000表示可输人的最大值是1000,10表示使用箭头按钮,数值每次变化10。
- getDouble() 函数可以提供一 个输人浮点型数值的对话框,其中的参数2表示小数的位数为2。
- getltem() 函数提供一个可以输入一个条目的对话框,需要先给它提供一些条目,例如这里定义的QStringList类型的items。它的参数0表示默认显示列表中的第0个条目(0就是第一个);然后是参数true,设置条目是否可以被更改,true就是可以被更改。这里使用了这些方便的静态函数,也可以自己定义对象,然后使用相关的函数进行设置。
2.5 消息对话框
消息对话框 QMessageBox 类提供了 一个模态的对话框用来通知用户一些信息,或者向用户提出一个问题并且获取答案。先添加头文件 #include<QMessageBox>,然后进人“消息对话框”按钮的单击信号槽中,添加如下代码:
// 消息对话框
void MyWidget::on_pushButton_5_clicked()
{
// 问题对话框
int ret1 = QMessageBox::question(this, tr("问题对话框"),tr("你了解Qt吗?"), QMessageBox::Yes, QMessageBox::No);
if(ret1 == QMessageBox::Yes) qDebug() << tr("问题!");
// 提示对话框
int ret2 = QMessageBox::information(this, tr("提示对话框"),tr("这是Qt书籍!"), QMessageBox::Ok);
if(ret2 == QMessageBox::Ok) qDebug() << tr("提示!");
// 警告对话框
int ret3 = QMessageBox::warning(this, tr("警告对话框"),tr("不能提前结束!"), QMessageBox::Abort);
if(ret3 == QMessageBox::Abort) qDebug() << tr("警告!");
// 错误对话框
int ret4 = QMessageBox::critical(this, tr("严重错误对话框"),tr("发现一个严重错误!现在要关闭所有文件!"), QMessageBox::YesAll);
if(ret4 == QMessageBox::YesAll) qDebug() << tr("错误");
// 关于对话框
QMessageBox::about(this, tr("关于对话框"),tr("yafeilinux致力于Qt及Qt Creator的普及工作!"));
}
这里创建了4个不同类型的消息对话框,分别拥有不同的图标及提示音(这个是操作系统设置的),参数分别是父窗口、标题栏、显示信息和拥有的按钮。这里使用的按钮都是QMessageBox类提供的标准按钮。这几个静态函数的返回值就是那些标准的按钮,它们是枚举类型,可以使用这个返回值来判断用户按下了哪个按钮。about() 函数没有返回值,因为它默认只有一个按钮,与其相似的还有一个 aboutQt() 函数,用来显示现在使用的Qt版本等相关信息。如果想使用自己的图标和自定义按钮,那么可以创建 QMessageBox 类对象,然后使用相关函数进行操作。
2.6 进度对话框
进度对话框 QProgressDialog 对一个耗时较长的操作进度提供了反馈。还是先添加#include <QProgressDialog> 头文件,然后进入“进度对话框”按钮的单击信号槽, 更改如下:
// 进度对话框
void MyWidget::on_pushButton_6_clicked()
{
QProgressDialog dialog(tr("文件复制进度"), tr("取消"), 0, 50000, this);
dialog.setWindowTitle(tr("进度对话框")); // 设置窗口标题
dialog.setWindowModality(Qt::WindowModal); // 将对话框设置为模态
dialog.show();
for(int i=0; i<50000; i++) { // 演示复制进度
dialog.setValue(i); // 设置进度条的当前值
QCoreApplication::processEvents(); // 避免界面冻结
if(dialog.wasCanceled()) break; // 按下取消按钮则中断
}
dialog.setValue(50000); // 这样才能显示100%,因为for循环中少加了一个数
qDebug() << tr("复制结束!");
}
这里创建了一个 QProgressDialog 类对象 dialog,构造函数的参数分别为对话框的标签内容、取消按钮的显示文本、最小值、最大值和父窗口。然后将其设置为了模态对话框并显示。后面的 for() 循环语句模拟了文件复制过程,使用 setValue() 函数使进度条向前推进。为了避免长时间的操作而使用户界面冻结,必须不断调用 QCoreApplication 类的静态函数 processEvents (), 可以将它放在 for() 循环语句中。然后使用 QProgressDialog 的 wasCanceled() 函数来判断用户是否单击了 “取消”按钮,如果是, 则中断复制过程。这里使用了模态对话框,其实 QProgressDialog 还可以实现非模态对话框,不过它需要定时器等的帮助。
2.7 错误信息对话框
错误信息对话框 QErrorMessage 类提供了一个显示错误信息的对话框。首先添加头文件 #include <QErrorMessage>,然后转到“错误信息对话框“按钮的单击信号槽添加代码:
// 错误信息对话框
void MyWidget::on_pushButton_7_clicked()
{
QErrorMessage *errordlg = new QErrorMessage(this);;
errordlg->setWindowTitle(tr("错误信息对话框"));
errordlg->showMessage(tr("这里是出错信息!"));
}
这里新建了一个 QErrorMessage 对话框,并且调用它的 showMessage() 函数来显示错误信息,调用这个函数时对话框会以非模态的形式显示出来。在错误信息对话框中默认有一个 Show this message again 复选框,可以选择以后是否还要显示相同错误信息。
2.8 向导对话框
向导对话框 QWizard 类提供了一个设计向导界面的框架。对于向导对话框读者应该已经很熟悉了,比如安装软件时的向导和创建项目时的向导。QWizard之所以被称为框架,是因为它具有设计一个向导的全部功能函数,可以使用它来实现想要的效果。Qt 演示程序中的 Dialogs 分类下有 Trivial Wizard、License Wizard 和 Class Wizard这3个示例程序,可以参考一下。
打开 mywidget. h文件,然后添加头文件 #include <QWizard>,在 MyWidget 类的声明中添加 private 类型函数声明:
private:
Ui::MyWidget *ui;
QWizardPage *createPage1(); // 新添加
QWizardPage *createPage2(); // 新添加
QWizardPage *createPage3(); // 新添加
这里声明了 3 个返回值为 QWizardPage 类对象的指针的函数,用来生成3个向导页面。然后在 mywidget. cpp 文件中对这 3 个函数进行定义:
QWizardPage * MyWidget::createPage1() // 向导页面1
{
QWizardPage *page = new QWizardPage;
page->setTitle(tr("介绍"));
return page;
}
QWizardPage * MyWidget::createPage2() // 向导页面2
{
QWizardPage *page = new QWizardPage;
page->setTitle(tr("用户选择信息"));
return page;
}
QWizardPage * MyWidget::createPage3() // 向导页面3
{
QWizardPage *page = new QWizardPage;
page->setTitle(tr("结束"));
return page;
}
在各个函数中分别新建了向导页面,并且设置了标题。下面进人“向导对话框”按钮的单击信号槽中,更改如下:
// 向导对话框
void MyWidget::on_pushButton_8_clicked()
{
QWizard wizard(this);
wizard.setWindowTitle(tr("向导对话框"));
wizard.addPage(createPage1()); // 添加向导页面
wizard.addPage(createPage2());
wizard.addPage(createPage3());
wizard.exec();
}
这里新建了 QWizard 类对象,然后使用 addPage() 函数为其添加了 3 个页面,这里的参数是 QWizardPage 类型的指针,所以直接使用了生成向导页面函数。运行程序可以看到,向导页面出现的顺序和添加向导页面的顺序是一致的。
上面程序中的向导页面是线性的,而且什么内容也没有添加。如果想设计自己的向导页面,或添加图片和自定义按钮,或设置向导页面顺序等,那么就需要再多了解 QWizard 类和 QWizardPage 类的成员函数了。
到这里,Qt提供的几个标准对话框就基本讲完了。其实还有3个与打印有关的标准对话框类QPageSetupDialog(页面设置对话框)、QPrintDialog(打印对话框)和 QPrintPreviewDialog(打印预览对话框)这里暂时不介绍。关于这些对话框的使用,Qt提供了一个示例程序 Standard Dialogs,它在 Dialogs 分类中。