11.5 通用对话框:QMessageBox、QWizard

本节介绍通用对话框:QMessageBox、QWizard,并通过示例程序展示消息对话框和向导对话框的各自使用方法。

11.5.1 QMessageBox

QMessageBox 消息框一般作为模态对话框,告知用户信息、提供选项并接收用户反馈。消息框同时提供三种文本显示:primary text(基本文本,必须有的,显示情况信息)、informative text(信息文本,向用户解释更多情况或提出问题)、detailed text(详细文本,可选的,根据用户需要显示更多数据信息),这些文本都是用于提供信息给用户理解消息框提出的问题,用户理解后作出相应的选择。消 息框还提供图标和一系 列标准按钮,程序员根据需要设置图标和按 钮,用户点击按钮后,消息框返回用户点击的按钮数值。消息框可以使用自定义对象的方式,也可以直接调用定制好的静态函数弹窗。我们先介绍消息框的普通成员函数和其 主要属性,然后介绍静态函数。
(1)普通成员函数
QMessageBox 基类是 QDialog 对话框类,QMessageBox 构造函数如下:
QMessageBox(QWidget * parent = 0)
QMessageBox(Icon icon, const QString & title, const QString & text, StandardButtons buttons = NoButton, QWidget * parent = 0, Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint)
第一个是默认构造函数,只有 parent  父窗口参数。第二个是带有详细参数的构造函数,参数 icon 是消息框显示的图标;title 是标题栏文本;text 是基本文本,显示展示给用户的信息内容;buttons 是消息框显示的按钮,可以通过 | 位或操作指定多个按钮,构造函数里的NoButton 是指不设置按钮,默认用 Ok 按钮;parent 是父窗口参数; f 是窗口标志位,Qt::Dialog 代表是对话框标志,窗口右上角没有最大化、最小化按钮,Qt::MSWindowsFixedSizeDialogHint 是指 Windows 的固定尺寸对话框的边框风格。

针对消息框的按钮使用,有多种添加、设置和访问函数。
首先是添加按钮函数:
void    addButton(QAbstractButton * button, ButtonRole role)
QPushButton *    addButton(const QString & text, ButtonRole role)
QPushButton *    addButton(StandardButton button)
第一个添加按钮函数,参数为自定义的按钮对象指针,role 是按钮的角色类型。
第二个添加按钮函数,根据文本新建按钮添加到消息框,并返回新建的按钮对象指针。
第三个添加按钮函数,根据标志位数值,添加标准按钮,并返回新建的按钮对象指针。
按钮角色是枚举类型,具体如下表:

QMessageBox::ButtonRole 数值 描述
QMessageBox::InvalidRole -1 不可用角色,该按钮不可用。
QMessageBox::AcceptRole 0 接收角色,点击该按钮导致对话框被接受,例如 OK 按钮。
QMessageBox::RejectRole 1 拒绝角色,点击该按钮导致对话框被拒绝,例如 Cancel 按钮。
QMessageBox::DestructiveRole 2 放弃角色,点击该按钮导致放弃变更并关闭对话框,例如 放弃变更、不保存就退出。
QMessageBox::ActionRole 3 动作角色,点击该按钮导致对话框元素做变动动作。
QMessageBox::HelpRole 4 帮助角色,点击该按钮请求帮助。
QMessageBox::YesRole 5 同意角色,表示 Yes 意思的按钮。
QMessageBox::NoRole 6 否定角色,表示 No 意思的按钮。
QMessageBox::ApplyRole 8 应用角色,应用当前的变更操作。
QMessageBox::ResetRole 7 重置角色,将对话框元素重置为默认值。

点击不同角色的按钮,对话框的动作不同,并且影响对话框返回值,要根据实际需要配置按钮角色。
消息框定义了很多标准按钮,可以通过 | 位或运算符设置多个标准按钮,标准按钮的标志位如下表所示:

QMessageBox::StandardButtons 数值 描述
QMessageBox::Ok 0x00000400 OK 按钮,定义为 AcceptRole 角色。
QMessageBox::Open 0x00002000 Open 按钮,定义为 AcceptRole 角色。
QMessageBox::Save 0x00000800 Save 按钮,定义为 AcceptRole 角色。
QMessageBox::Cancel 0x00400000 Cancel 按钮,定义为 RejectRole 角色。
QMessageBox::Close 0x00200000 Close 按钮,定义为 RejectRole 角色。
QMessageBox::Discard 0x00800000 Discard 或 Don't Save 按钮,依赖系统平台,定义为 DestructiveRole 角色。
QMessageBox::Apply 0x02000000 Apply 按钮,定义为 ApplyRole 角色。
QMessageBox::Reset 0x04000000 Reset 按钮,定义为 ResetRole 角色。
QMessageBox::RestoreDefaults 0x08000000 Restore Defaults 按钮,定义为 ResetRole 角色。
QMessageBox::Help 0x01000000 Help 按钮,定义为 HelpRole 角色。
QMessageBox::SaveAll 0x00001000 Save All 按钮,定义为 AcceptRole 角色。
QMessageBox::Yes 0x00004000 Yes 按钮,定义为 YesRole 角色。
QMessageBox::YesToAll 0x00008000 Yes to All 按钮,定义为 YesRole 角色。
QMessageBox::No 0x00010000 No 按钮,定义为 NoRole 角色。
QMessageBox::NoToAll 0x00020000 No to All 按钮,定义为 NoRole 角色。
QMessageBox::Abort 0x00040000 Abort 按钮,定义为 RejectRole 角色。
QMessageBox::Retry 0x00080000 Retry 按钮,定义为 AcceptRole 角色。
QMessageBox::Ignore 0x00100000 Ignore 按钮,定义为 AcceptRole 角色。
QMessageBox::NoButton 0x00000000 不可用按钮

根据标志位,可以一次设置多个标准按钮,如下函数:
void    setStandardButtons(StandardButtons buttons)   //设置标准按钮
StandardButtons    standardButtons() const    //获取设置好的标准按钮标志位
StandardButton    standardButton(QAbstractButton * button) const  //根据按钮对象指针获取按钮对应的标准按钮标志位
setStandardButtons() 函数根据参数里标志位一次设置多个按钮,standardButtons() 则返回设置好的按钮标志位。
standardButton() 是根据参数里的按钮对象指针,返回该按钮对应的标准按钮标志位,如果对象不是标准按钮,返回 QMessageBox::NoButton  。
注意不要调用 msgBox.setStandardButtons(QMessageBox::NoButton) ,消息框显示时会没有任何按钮,消息框可能 出现关不掉的情况,程序界面会卡住无法操作,一般默认用 QMessageBox::Ok 按钮即可。

按钮角色可以读取,如下函数:
ButtonRole    buttonRole(QAbstractButton * button) const
按钮角色得在添加按钮 addButton() 时指定,目前没有直接修改按钮角色的函数。
从消息框卸载按钮使用如下函数:
void    removeButton(QAbstractButton * button)
注意 removeButton() 函数只是将消息框的按钮卸下,并不会删除该按钮对象。
如果需要修改按钮角色,可以先卸载按钮,再用新的角色添加给消息框。
获取消息框已设置的所有按钮对象列表,使用如下函数:
QList<QAbstractButton *>    buttons() const

当消息框有多个按钮时,可以指定默认按钮(默认对应 Enter 键)、退出按钮(对应 Esc/Escape 键),如下函数:
void    setDefaultButton(QPushButton * button)   //根据按钮指针设置默认按钮
void    setDefaultButton(StandardButton button) //根据标准按钮标志位设 置默认按钮
QPushButton *    defaultButton() const               //获取默认按钮对象指针
void    setEscapeButton(QAbstractButton * button)   //根据按钮指针设置退出按钮
void    setEscapeButton(StandardButton button)       //根据标准按钮标志位设置退出按钮
QAbstractButton *    escapeButton() const               //获取退出按钮对象指针
注意检查参数和返回值的有效性,避免操作空指针。

根据用户的操作,可以获取用户点击的按钮对象,或者返回 0 (NULL):
QAbstractButton *    clickedButton() const
用户如果点击了按钮,就返回点击的按钮对象;如果没有设置退出键时用户按下键盘 Esc 键,返回 0 。
在未调用 exec() 显示消息框时, clickedButton() 也返回 0 。
消息框还可以添加一个复选框,比如用于设置是否在文件关闭前自动保存修改内容,注意复选框对象要自己新建,消息框默认并没有复选框。设置和获取复选框 对象函数如 下:
QCheckBox *    checkBox() const        //获取复选框对象指针,默认为 NULL
void    setCheckBox(QCheckBox * cb)   //手动新建复选框对象 cb,添加给消息框
  checkBox() 默认是 NULL,消息框自己不会创建复选框,必须自己手动新建一个复选框对象,用setCheckBox( cb ) 函数设置复选框对象。
一个典型的消息框对象使用如下代码示例:
    QMessageBox msgBox;
    msgBox.setText(tr("文本已经修改。"));   //基本文本设置
    msgBox.setInformativeText(tr("是否保存修改后的文本?"));  //信息文本设置
    msgBox.setDetailedText(tr("勾选上面复选框,设置总是自动保存修改的文本。"));  //详细文本设置
    msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
    msgBox.setDefaultButton(QMessageBox::Save);
    //补充复选框
    QCheckBox *pCheckBox = new QCheckBox(tr("关闭前总是自动保存修改文本。"));
    msgBox.setCheckBox(pCheckBox);
    int ret = msgBox.exec();
    if(NULL != msgBox.checkBox())
    {
        bool bAlwaysSave = msgBox.checkBox()->isChecked();
        if(bAlwaysSave)
        {
            qDebug()<<tr("关闭前总是自动保存修改文本。");
        }
        else
        {
            qDebug()<<tr("关闭前不自动保存修改文本。");
        }
    }
显示效果如下图所示:
msgboxshow1
消息框第一行显示的是基本文本,第二行是信息文本,当设置了详细文本后,消息框自动添加“Show Details”按钮,点击该按钮就会展开下方的文本框显示详 细文本。
添加复选框之后,复选框显示在第三行,可以访问复选框对象,获取用户是否选中了复选框,以进行后续操作。
消息框的三种文本访问和设置函数如下:
QString    text() const   //获取基本文 本
void    setText(const QString & text)  //设置基本文本
QString    informativeText() const   //获取信息文本
void    setInformativeText(const QString & text)    //设置信息文本
QString    detailedText() const       //获取详细文本
void    setDetailedText(const QString & text)    //设置详细文本
设置详细文本后,消息框自动添加“Show Details”按钮,如果没有设置详细文本,消息框就没有“Show Details”按钮。
消息框文本可以设置文本格式,如下函数:
Qt::TextFormat    textFormat() const    //获取文本格式
void    setTextFormat(Qt::TextFormat format)   //设置文本格式
Qt::TextFormat 枚举类型内容比较简单,三种取值如下表所示:

Qt::TextFormat 枚举常量 数值 描述
Qt::PlainText  0 设置的文本理解为纯文本,不解析 HTML 语法。
Qt::RichText  1 设置的文本理解为丰富文本,按照 HTML 语法解析呈现内容。
Qt::AutoText  2 自动判断文本类型,如果 Qt::mightBeRichText() 函数返回 true,当做丰富文本呈现,否则都当成纯文本。

默认文本格式为 Qt::AutoText ,自动判断设置文本类型。
注意 setTextFormat() 函数只影响基本文本,不影响信息文本和详细文本。
信息文本默认总是自动文本格式 Qt::AutoText,详细文本默认总是纯文本格式 Qt::PlainText。

消息框的文本还可以设置文本交互操作的标志位,如下函数:
Qt::TextInteractionFlags    textInteractionFlags() const      //获取文本交互标志位,默认值依赖 style 风格
void    setTextInteractionFlags(Qt::TextInteractionFlags flags)  //设置文本交互标志位
  Qt::TextInteractionFlags 标志位定义文本的交互性,比如是否可以复制,是否可以编辑,如下表所示:
- setTextInteractionFlags() 只影响基本文本,不影响信息文本和详细文本。
消息框除了显示文本,还可以选择图标配合显示,表示消息的类别,比如普通消息、警告消息、严重错误消息等类型。
图标支持使用 QMessageBox 自带图标或者自定义图标,如下函数:
void    setIcon(Icon)    //设置 QMessageBox 自带的图标
Icon    icon() const     //获取所用的 QMessageBox 自带图标
void    setIconPixmap(const QPixmap & pixmap)   //设置自定义图片作为图标
QPixmap    iconPixmap() const   //获取设置的自定义图标的图片
默认情况下, iconPixmap() 是空的 null ,没有自定义图片文件。如果需要用自定义图片,根据图片文件名构建 QPixmap 对象,设置给消息框即可。
icon() 默认情况下返回 0,就是 QMessageBox::NoIcon ,QMessageBox 自带的图标如下表所示:

QMessageBox::​Icon 枚举类型 数值 描述 图标示例
QMessageBox::NoIcon  0 消息框没有任何图标。
QMessageBox::Question  4 询问图标,说明提示的消息是向用户询问。 iconquestion
QMessageBox::Information  1 信息图标,说明提示的消息是告诉用户信息。 iconinformation
QMessageBox::Warning  2 警告图标,说明提示的消息是警告信息,但目前程序能够处理。 iconwarning
QMessageBox::Critical  3 严重错误图标,说明程序发生了严重错误,很可能崩溃。 iconcritical

上表图标示例仅供参考,实际运行时图标依赖系统平台的风格,不同操作系统显示图标不一样。
消息框还可以修改窗口标题栏文本,设置模态或非模态显示,如下函数设置:
void    setWindowTitle(const QString & title)   //设置消息框标题栏文本
QString    windowTitle() const          //获取消息框标题栏文本
void    setWindowModality(Qt::WindowModality windowModality)   //设置消息框显示模态类型
Qt::WindowModality    windowModality() const   //获取消息框的显示模态类型
窗口的显示模态类型见 11.1.1 节QWidget 类的介绍,主要就是非模态 Qt::NonModal、Qt::WindowModal 窗口级模态、Qt::ApplicationModal 应用级模态三种。
消息框还有一个特别的 open() 函数,可以指定接受信号的对象和槽函数:
void    open(QObject * receiver, const char * member)
该函数打开消息框,并将 finished() 或 buttonClicked() 信号关联到参数里对象槽函数,如果槽函数有一个指针参数,那么关联 buttonClicked() 信号,否则关联 finished() 信号到槽函数。
注意消息窗口关闭后,自动解除信号和 open() 参数里对象的槽函数关联。

消息框类本级的槽函数只有 1 个,如下函数:
virtual int    exec()   //模态显示消息框
消息框类本级的信号也只有 1 个,如下信号:
void    buttonClicked(QAbstractButton * button)  //点击按钮信号,参数里是被点击的按钮对象

消息框主要从基类 QDialog 和 QWidget 继承各种信号和槽函数,详见 11.1 节和 11.2 节内容。

(2)静态成员函数
消息框可以通过定义对象、调用普通成员函数方式使用,也可以通过最简单的静态成员函数来使用。
静态成员函数仅需一句代码即可完成调用,非常便捷。
针对询问、信息、警告、严重错误四种情况,对应的静态成员函数如下:
StandardButton    question(QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = StandardButtons( Yes | No ), StandardButton defaultButton = NoButton)
StandardButton    information(QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
StandardButton    warning(QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
StandardButton    critical(QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
上面静态函数的参数都很类似,title 是标题文本,text 是基本文本,buttons 是设置的标准按钮,询问时为 Yes 和 No 按钮,其他情况默认为 Ok 按钮,defaultButton 默认 NoButton;返回值就是用户点击的标准按钮数值。
静态函数调用非常简单,如下示例:
int ret = QMessageBox::warning(this, tr("My Application"),
                               tr("The document has been modified.\n"
                                  "Do you want to save your changes?"),
                               QMessageBox::Save | QMessageBox::Discard
                               | QMessageBox::Cancel,
                               QMessageBox::Save);
窗口标题文本 "My Application",基本文本 "The document has been modified.\nDo you want to save your changes?" ,显示三个按钮 Save 、Discard、Cancel,默认焦点是 Save 按钮。

另外,消息框还提供了两个用于显示本程序信息和 Qt 版本信息的静态函数:
void    about(QWidget * parent, const QString & title, const QString & text) 
void    aboutQt(QWidget * parent, const QString & title = QString())
about() 函数用于显示本程序的信息,title 是标题栏文本,text 一般为自定义的本程序介绍文本,比如 "某某软件 1.0 版本,版权归某某所有。"
aboutQt() 函数显示关于所用 Qt 版本的信息,title 是窗口标题文本,窗口内的消息文本是 Qt 自行设置的,包括 Qt 版本信息、许可证信息等等。

QMessageBox 类的内容介绍到这,下面我们通过一个简单文本编辑器示例熟悉消息框的使用。
我们打开 QtCreator,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:
①项目名称 simpletextedit,创建路径 D:\QtProjects\ch11,点击下一步;
②套件选择里面选择全部套件,点击下一步;
③基类选择 QWidget,注意修改主窗口类名为 WidgetSimpleTextEdit,然后点击下一步;
④项目管理不修改,点击完成。
由于使用 Qt 通用对话框,就不再需要额外新建自己的子窗口类和UI文件。
我们打开 widgetsimpletextedit.ui 界面文件,拖入控件:
ui1
第一行控件是“文件名”标签,单行编辑器 lineEditFileName,“浏览”按钮 pushButtonBrowser。
第二行是纯文本编辑器 plainTextEdit,编辑器右侧还有 5 个按钮,
“打开文件”按钮 pushButtonOpenFile,“保存文件”按钮 pushButtonSaveFile,“关闭文件”按钮 pushButtonCloseFile,“关于本程序”按钮 pushButtonAboutThis,“关于Qt”按钮 pushButtonAboutQt。
第一行控件按照水平布局器排列,第二行先将右边 5 个按钮按照垂直布局器排列,再与纯文本编辑器一起按水平布局器排列。窗口整体按照垂直布局器排列,窗口大小 640 * 480 。

完成界面布局后,依次右击节目 6 个按钮,为按钮添加  clicked() 信号的槽函数。
然后右击纯文本编辑器 plainTextEdit ,为纯文本编辑器添加 textChanged() 信号的槽函数。
槽函数添加完成后,我们保存并关闭 ui 文件,回到代码编辑界面。
我们首先编辑头文件 widgetsimpletextedit.h 内容:
#ifndef WIDGETSIMPLETEXTEDIT_H
#define WIDGETSIMPLETEXTEDIT_H

#include <QWidget>
#include <QFile>
#include <QFileDialog>

namespace Ui {
class WidgetSimpleTextEdit;
}

class WidgetSimpleTextEdit : public QWidget
{
    Q_OBJECT

public:
    explicit WidgetSimpleTextEdit(QWidget *parent = 0);
    ~WidgetSimpleTextEdit();

private slots:
    void on_pushButtonBrowser_clicked();

    void on_pushButtonOpenFile_clicked();

    void on_pushButtonSaveFile_clicked();

    void on_pushButtonCloseFile_clicked();

    void on_pushButtonAboutThis_clicked();

    void on_pushButtonAboutQt_clicked();

    void on_plainTextEdit_textChanged();

private:
    Ui::WidgetSimpleTextEdit *ui;
    //文件对象
    QFile m_file;
    //保存文本是否被修改的状态
    bool m_bTextIsChanged;
    //根据编辑框内容写入文件
    void SaveTextFile();
};

#endif // WIDGETSIMPLETEXTEDIT_H
我们添加了 QFile 和 QFileDialog 的头文件引用。
在类内容,6 个按钮的槽函数和纯文本编辑器的槽函数是通过界面编辑操作添加的。
然后手动添加了成员文件对象 m_file,标志位 m_bTextIsChanged 表示文本是否被修改。
添加了函数 SaveTextFile() ,用于将编辑器内容写入到文件中。

接下来我们分段编辑源文件 widgetsimpletextedit.cpp 内容,首先是头文件包含和构造函数:
#include "widgetsimpletextedit.h"
#include "ui_widgetsimpletextedit.h"
#include <QDebug>
#include <QMessageBox>


WidgetSimpleTextEdit::WidgetSimpleTextEdit(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::WidgetSimpleTextEdit)
{
    ui->setupUi(this);

    //保存文本是否被修改的状态
    m_bTextIsChanged = false;
}
添加了 QDebug 和 QMessageBox 头文件包含。
在构造函数里面添加了初始化代码,m_bTextIsChanged 默认为 false。
然后是析构函数代码:
WidgetSimpleTextEdit::~WidgetSimpleTextEdit()
{
    //判断文本修改状态
    if( m_bTextIsChanged )
    {
        //提示用户保存修改后的文本
        int nRet = QMessageBox::question(this, tr("保存文件询问"),
                                         tr("是否保存文件编辑的内容?"),
                                         QMessageBox::Yes|QMessageBox::No,
                                         QMessageBox::Yes);
        if( QMessageBox::Yes == nRet )
        {
            //保存一下文件
            SaveTextFile();
        }
    }
    //关闭文件
    m_file.close();
    //清空编辑框
    ui->plainTextEdit->clear();
    //删除界面
    delete ui;
}
在析构函数里,我们添加了对 m_bTextIsChanged 标志位的检查,如果为 true,说明文本修改了但未保存,
弹出消息框提示用户是否需要保存文件编辑内容,如果用户选择 Yes 按钮,那么调用 SaveTextFile() 保存文件。
然后关闭文件,清空纯文本编辑器,最后删除界面。

接下来是“浏览”按钮对应槽函数代码:
//浏览文件
void WidgetSimpleTextEdit::on_pushButtonBrowser_clicked()
{
    //文件名
    QString strFileName;
    strFileName = QFileDialog::getOpenFileName(this, tr("选择文本文件"), tr(""),
                                               tr("Text files (*.txt);;All files (*)"));
    if(strFileName.isEmpty())
    {
        //没有文件名
        return;
    }
    //正常文件名
    ui->lineEditFileName->setText(strFileName);
}
该函数先定义字符串对象 strFileName,然后调用 QFileDialog::getOpenFileName() 获取要打开的文件名,
然后判断文件名是否为空,如果为空就不处理返回;
文件名不为空,就将获取的文件名显示到文件名编辑器里。

接下来是“打开文件”按钮对应槽函数代码:
//打开文件
void WidgetSimpleTextEdit::on_pushButtonOpenFile_clicked()
{
    //获取文件名
    QString strFileName = ui->lineEditFileName->text().trimmed();
    //判断文件名是否为空
    if( strFileName.isEmpty() )
    {
        QMessageBox::warning(this, tr("文件名检查"), tr("文件名为空,请先浏览选择文本文件。"));
        return;
    }
    //判断文本修改状态
    if( m_bTextIsChanged )
    {
        //提示用户保存修改后的文本
        int nRet = QMessageBox::question(this, tr("保存文件询问"),
                                         tr("是否保存上一个文件编辑的内容?"),
                                         QMessageBox::Yes|QMessageBox::No,
                                         QMessageBox::Yes);
        if( QMessageBox::Yes == nRet  )
        {
            //保存一下文件
            SaveTextFile();
        }
    }
    //关闭旧文件
    m_file.close();
    //清空编辑框
    ui->plainTextEdit->clear();

    //尝试打开文件
    m_file.setFileName( strFileName );
    bool bRet = m_file.open( QIODevice::ReadWrite|QIODevice::Text );
    //判断是否打开
    if( ! bRet )
    {
        //没有打开,报错
        QMessageBox::warning(this, tr("文件打开报错"),
                             tr("指定文件无法打开,请检查文件是否存在,是否有读写权限。"));
        //没有文件,没有编辑
        m_bTextIsChanged = false;
        //修改保存按钮文本,不带 *
        ui->pushButtonSaveFile->setText(tr("保存文件"));
        return;
    }
    //文件成功打开
    QByteArray baData = m_file.readAll();
    //转为 QString
    QString strText = QString::fromLocal8Bit(baData);
    //显示到文本编辑器
    ui->plainTextEdit->setPlainText(strText);
    //修改标记位,新文件,还没有编辑
    m_bTextIsChanged = false;
    //修改保存按钮文本,不带 *
    ui->pushButtonSaveFile->setText(tr("保存文件"));
    return;
}
该函数先从单行编辑器获取文件名,并判断文件名是否为空,如果为空,弹出消息框提示用户文件名为空 并返回;
如果文件名非空,继续后面操作。
检查 m_bTextIsChanged 标志位是否为真,如果为真,说明上一个文件修改了但没有保存,
弹出消息框询问用户,是否保存上一个文件编辑内容,如果用户点击 Yes 就保存上一个文件。
然后关闭上一个文件,清空纯文本编辑器。
设置新文件名给文件对象,然后尝试以 QIODevice::ReadWrite|QIODevice::Text  模式打开文本文件,
如果打开失败,就重置 m_bTextIsChanged 为 false,并重置“保存文件”按钮文本,然后返回。
如果打开文件成功,将文件所有内容读取到 baData,然后转为 QString 类型,存到 strText 。
将 strText 设置给纯文本编辑器显示,并重置 m_bTextIsChanged 为 false,并重置“保存文件”按钮文本。

这里说明一下,当文本发生修改时,保存文件按钮显示“保存文件*”字样(多个 * 提示),其他时候显示“保存文件”。

接下来我们编写实际写入文件内容的 SaveTextFile() 函数:
//根据编辑框内容写入文件
void WidgetSimpleTextEdit::SaveTextFile()
{
    //获取文本
    QString strText = ui->plainTextEdit->toPlainText();
    //转为本地字节数组
    QByteArray baData = strText.toLocal8Bit();
    //保存文件
    if( m_file.isOpen() )
    {
        //文件是打开的
        m_file.resize(baData.size());  //设置文件大小
        m_file.reset(); //光标移动到开头位置
        m_file.write(baData);
        //修改标记位
        m_bTextIsChanged = false;
        //修改保存按钮文本,不带 *
        ui->pushButtonSaveFile->setText(tr("保存文件"));
    }
}
该函数先从纯文本编辑器获取纯文本,存到 strText,然后转为本地字节数组 baData。
判断文件对象是否正常打开状态,如果是打开的,重新设置文件大小为 baData 数组的大小,
然后移动读写光标到文件开头,写入 baData 数组。
然后重置 m_bTextIsChanged 为 false,并重置“保存文件”按钮文本。

接下来是“保存文件”按钮对应槽函数代码:
//保存文件
void WidgetSimpleTextEdit::on_pushButtonSaveFile_clicked()
{
    //判断标志位
    if( m_bTextIsChanged )
    {
        //保存文件内容
        SaveTextFile();
    }
}
该函数比较简单,判断 m_bTextIsChanged 标志位,为真时调用 SaveTextFile() 保存文件;如果标志位为假,就不处理。

接下来是“关闭文件”按钮对应槽函数代码:
//关闭文件
void WidgetSimpleTextEdit::on_pushButtonCloseFile_clicked()
{
    //判断标志位
    if( m_bTextIsChanged )
    {
        //有修改文件
        QMessageBox msgBox;
        msgBox.setText(tr("文本已修改。"));
        msgBox.setInformativeText(tr("您希望保存到文件吗?"));
        msgBox.setIcon( QMessageBox::Question );
        //三个按钮
        msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
        msgBox.setDefaultButton(QMessageBox::Save);
        int nRet = msgBox.exec();
        //判断返回值,分别处理
        switch (nRet) {
        case QMessageBox::Save:
            //保存文件
            SaveTextFile();
            break;
        case QMessageBox::Discard:
            //不保存,在后面代码关闭文件
            break;
        case QMessageBox::Cancel:
            return;  //取消关闭文件
            break;
        default:
            return;  //取消关闭文件
            break;
        }
    }
    //关闭文件
    m_file.close();
    //清空编辑器
    ui->plainTextEdit->clear();
    //没有文件,没有编辑
    m_bTextIsChanged = false;
    //修改保存按钮文本,不带 *
    ui->pushButtonSaveFile->setText(tr("保存文件"));
}
该函数先判断 m_bTextIsChanged 标志位,如果为真,说明文件修改了尚未保存,
定义消息框对象 msgBox,设置基本文本、信息文本、询问图标,
设置三个按钮 Save、Discard 、Cancel ,分别代表保存修改、丢弃修改、取消关闭操作。
默认按钮为 Save,然后弹出模态消息框,根据用户点击结果判断:
msgBox 返回值如果为 Save,调用 SaveTextFile() 保存文件;
如果为 Discard,就跳到后面代码,在 switch 后面关闭文件;
如果为 Cancel 或默认情况,均直接返回,不进行后续关闭操作。
switch 后面代码先关闭文件,清空纯文本编辑器,
重置 m_bTextIsChanged 为 false,并重置“保存文件”按钮文本。

接下来是“关于本程序”按钮对应槽函数代码:
//关于本程序
void WidgetSimpleTextEdit::on_pushButtonAboutThis_clicked()
{
    QMessageBox::about(this, tr("关于本程序"),
                       tr("简单文本编辑器 1.0"));
}
该函数就是简单显示本程序信息,标题栏是 "关于本程序",窗口内文本是 "简单文本编辑器 1.0" 。

接下来是“关于Qt”按钮对应槽函数代码:
//关于Qt
void WidgetSimpleTextEdit::on_pushButtonAboutQt_clicked()
{
    QMessageBox::aboutQt(this, tr("关于Qt"));
}
这个函数更简单,只设置消息框标题,内容由 aboutQt() 函数自动填充,显示 Qt 版本和许可证等信息。

最后是纯文本编辑器控件的文本修改信号对应的槽函数:
//文本修改变化
void WidgetSimpleTextEdit::on_plainTextEdit_textChanged()
{
    //文本已修改
    m_bTextIsChanged = true;
    //修改保存按钮文本,带 *
    ui->pushButtonSaveFile->setText(tr("保存文件*"));
}
该函数先修改 m_bTextIsChanged 为 true,然后设置保存文件按钮文本为 "保存文件*",多个星号提示用户文本已修改,但是尚未保存。
示例代码介绍到这,我们生成项目,运行示例,
“浏览”选择一个文本文件,然后“打开文件”,如下图所示:
run1
我们在编辑器修改一下文本,然后再点击“关闭文件”按钮,显示弹出如下:
run2
点击不同按钮,效果不同,Save 会保存文件后再关闭,Discard 会丢弃修改并关闭文件,Cancel 则放弃关闭文件的操作。
如果我们修改了文本编辑器,尚未保存,直接再点击“打开文件”按钮, 则会弹出显示是否保存消息框,如下图所示:
run3
如果点击 Yes,程序先保存修改内容再打开新文件;如果点击 No,则放弃保存修改的内容,直接打开新文件。
其他功能请自行测试,这里不再逐一测试截图。下一小节我们学习向导对话框。

11.5.2 QWizard






prev
contents
next