11.2 QDialog 多窗口使用

本节介绍 QDialog 类内容,并使用 QDialog 派生类实现多窗口使用,子窗口可以采用模态对话框显示,也可以使用非模态对话框显示。

11.2.1 QDialog 类

QDialog 是所有对话框(**Dialog)类的基类,之前章节使用的 QFileDialog 和 QMessageBox 都是 QDialog 派生类,本章后面会学习更多的 Qt 通用对话框。
QDialog 及其派生类总是顶层窗口,而不会作为窗口内嵌子控件存在。对话框通常用于短期任务,并简要与用户进行交互信息。对话框可以是模态的,也可以是非模态的。对话框可以提供返回 值,也可以设置默认按钮(界面按 Enter 回车键,就等于点击默认按钮)。
对话框与其父类 QWidget 窗口的标题栏外观不同, QWidget 窗口标题栏右上角是最小化、最大化和关闭三个按钮,而对话框标题栏右上角是帮助按钮和关闭按 钮,如下图所示:
dialog
点击标题栏的帮助按钮,鼠标变成问号鼠标形式,再点击对话框子控件,就会显示子控件的 whatsThis 属性,就是子控件的帮助信息,例如 Haha 按钮的帮助信息:
dialog2
对话框的标题栏没有最小化、最大化按钮,但是尺寸可以拉动边框改变。默认情况下,对话框右下角不会显示尺寸手柄(QSizeGrip,就是上图右下角 的三角形图 案,6个小点构成),可以通过调用 setSizeGripEnabled(bool) 函数设置是否显示尺寸手柄。

QDialog 类构造函数如下:
    QDialog(QWidget * parent = 0, Qt::WindowFlags f = 0)
QDialog 对话框以及其他窗口类 flags 标志位包含 Qt::Dialog 标志的窗口,总是以顶层窗口显示,而不会作为子控件。QDialog 对话框的 parent 指针如果为 NULL,那么它以桌面为背景,以桌面中心为参考弹出显示;parent 指针为其他窗口,那么以父窗口为背景,以父窗口中心为参考弹出显示。使用 setParent() 函数可以更改对话框的归属父窗口。

QDialog 作为模态对话框时,会强制占据前台焦点,不关闭模态对话框,就无法回到父窗口操作。模态对话框通常使用下面函数弹窗:
int QDialog::​exec()     //槽函数,应用程序级的模态显示,带有返回值
void QDialog::​open()   // 槽函数,窗口级别的模态显示,这个函数没有返回值
通常使用 exec() 函数的情况更多,exec() 弹窗关闭时,会返回一个数值供父窗口判断。常见的对话框返回值如下:
       QDialog::Accepted ,数值为   1 ;
       QDialog::Rejected  ,数值为  0 。
通常调用下面槽函数时,自动关闭模态对话框,并设置相应结果值:
void QDialog::​accept()       //槽函数,设置结果值为 1,exec() 返回该数值
void QDialog::​reject()         //槽函数,设置结果值为 0,exec() 返回该数值
void QDialog::​done(int r)   //槽函数,设置结果值为 r ,exec() 返回该数值
除了 exec() 能够获取返回的结果值,对话框也可以用下面函数获取或者修改返回值:
int QDialog::​result()   //获取结果数值
void QDialog::​setResult(int i)    //设置结果数值
对话框有三个信号,与关闭对话框的槽函数有关:
void QDialog::​accepted()    //当调用 accept() 或 done() 或用户有接受操作,并且结果值为 QDialog::Accepted 时触发该信号
void QDialog::​rejected()      //当调用 reject() 或 done() 或用户有拒绝操作,并且结果值为 QDialog::Rejected 时触发该信号
void QDialog::​finished(int result)  //当调用 done(), accept(),  reject() 或用户有结束操作,结果值已设置时触发该信号
上面三个信号主要根据对话框 done(), accept(),  reject() 三个槽函数触发,如果按钮关联到这三个槽函数,用户点击了按钮,也会触发这些信号。但是函数  hide() 和 setVisible(false) 不会触发上面信号。
返回值一般用于模态对话框,模态对话框强制占据前台焦点,使得用户必须有反馈操作,操作关闭对话框之后,形成接受或拒绝的返回值,再回到父窗口。

实际应用中,如果需要长时间处理某些任务,可以使用 QProgressDialog 进度对话框,进度对话框的模态显示与其他对话框不同,通常使用下面的函数:
void    setModal(bool modal)   //一般用于长时间进度对话框模态显示
将 setModal( true ) 和 show() 函数配合使用,也是显示模态对话框。

如果使用非模态的对话框,就不会强制占据前台焦点,非模态对话框和父窗口可以自由切换,非模态对话框显示直接使用 show() 或 setVisible() 函数:
void QDialog::​setVisible(bool visible)
void QWidget::​show()   //基类显示函数

QDialog 对话框有两个特殊按键,就是键盘上的 Enter 键和 Esc 键:
① 当使用 QPushButton::setDefault(), QPushButton::isDefault() 和 QPushButton::autoDefault() 设置默认按钮后, Enter 键会自动触发默认按钮的点击操作。
② 对话框界面 Esc 按键总是触发 QDialog::reject() 槽函数,这是内定的特性,在任何对话框界面按 Esc 键都会关闭对话框,并且设置 QDialog::Rejected 结果值。
QDialog 类的内容介绍到这,下面我们通过一个示例,为一个主窗口新建两个子对话框。

11.2.2 QDialog 多窗口示例

我们编写一个图片预览例子,新建两个子对话框, 使用一个非模态对话框进行图片缩放,使用另一个模态对话框进行图片旋转,模态对话框返回值就是旋转度数。
我们打开 QtCreator,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:
①项目名称 imagetransform,创建路径 D:\QtProjects\ch11,点击下一步;
②套件选择里面选择全部套件,点击下一步;
③基类选择 QWidget,注意修改主窗口类名为 ImageTransformWidget,这 样与后续的子窗口类名作区分,然后点击下一步;
④项目管理不修改,点击完成。
主窗口类名为 ImageTransformWidget,对应的界面文件、头文件、源文件名就是 imagetransformwidget.ui、imagetransformwidget.h、imagetransformwidget.cpp 。
接下来我们在项目管理器右击项目根 imagetransform,右键菜单选择“添加新文件...”,进入新建文件对话框:
new1
上图左边一栏选择“Qt”,中间一栏选择“Qt设计师界面类”,点击“Choose...”按钮,进入界面类模板选择:
new2
在中间一栏上面选择“Dialog without Buttons”模板,点击“下一步”按钮,进入类名的编辑:
new3
我们将类名修改为 ResizeImageDialog,点击“下一步”按钮,进入项目管理界面:
new4
点击“完成”按钮,这样就为项目新增了 ResizeImageDialog 对话框类,头文件、源文件、界面文件都添加到了项目里面。新建该类后,先关闭 ui 设计界面,我们回到 QtCreator 编辑界面。
 
我们重新在项目管理器右击项目根 imagetransform,右键菜单选择“添加新文件...”,按照上面流程,添加“Dialog without Buttons”模板的对话框,对话框类名为 RotateImageDialog,也完成该对话框的新建。这样 项目就有三套窗口类文件,一套是 主窗口类,另外两套是 ResizeImageDialog 和 RotateImageDialog 对话框类。

下面我们从主窗口界面开始编辑,打开 imagetransformwidget.ui,拖入控件:
ui1
界面左边是一个滚动区域 scrollArea,右侧是三个按钮,“打开图片”按钮 pushButtonOpen,“缩放图片”按钮 pushButtonResize,“旋转图片”按钮 pushButtonRotate 。
界面首先将右侧三个按钮选中,进行垂直布局;
然后在右边对象树选择根 ImageTransformWidget,点击水平布局按钮,就完成窗口整体布局,窗口尺寸是默认的 400*300。

完成布局后,我们依次右击三个按钮,为按钮添加 clicked() 信号对应的槽函数,这样就有三个槽函数添加到主窗口类里面。我们保存并关闭 imagetransformwidget.ui 。

我们打开 resizeimagedialog.ui 文件,进行缩放尺寸对话框界面编辑,拖入控件:
ui2
对话框第一行是标签“现在的尺寸”,单行编辑器 lineEditOldSize。
第二行是标签“宽度×高度”,旋钮框 spinBoxWidthNew,旋钮框 spinBoxHeightNew,“设置新尺寸”按钮 pushButtonSetNewSize 。
选中两个旋钮框,将它们的 sizePolicy 属性中的水平策略改为 Expanding 。
ui3
第一行控件采用水平布局器排布;第二行也是采用水平布局器排布;
然后选择对象树根 ResizeImageDialog,点击垂直布局按钮,窗口整体采用垂直布局,窗口大小修改为 320*200 。
我们右击“设置新尺寸”按钮,为按钮添加 clicked() 信号对应的槽函数。完成这些编辑后,我们保存并关闭 resizeimagedialog.ui 文件。

下面我们编辑第三个界面文件 rotateimagedialog.ui,就是旋转图片对话框,拖入控件:
ui4
只有一行控件,标签“顺时针旋转角度”,旋钮框 spinBoxAngle,“执行旋转”按钮 pushButtonRotating。
选中旋钮框 spinBoxAngle,修改它的 sizePolicy 属性中的水平策略为 Expanding。
在右边选中对象树根 RotateImageDialog,点击上面的水平布局按钮,就完成了窗口整体布局。
窗口的大小修改为 320*100。
然后我们右击“执行旋转”按钮,为按钮添加 clicked() 信号对应的槽函数。
完成这些操作后,我们保存并关闭 rotateimagedialog.ui 界面文件。

对于缩放图片对话框,使用非模态显示,与主窗口采用信号和槽函数通信:
sig-slot
对于旋转图片对话框,使用模态显示,我们调用 exec() 函数后,该函数直接返回需要旋转的角度,不需要信号和槽函数进行通信。exec() 属于子对话框的公有函数,对话框的 result() 和 setResult(int i) 函数也可以进行数据传递。
信号和槽函数机制可以传输数据,成对的读写公有函数也可以传递数据,都是多个窗口之间可行的通信方式。

模态对话框都是短时间强制占据前台获取用户输入,得到用户输入后就关闭对话框,模态对话框不会长时间显示。模态对话框弹出一次就是获取一次性数据,因 此使用公有函 数方式更简洁。Qt 的通用对话框通常都是模态对话框,一次性获取输入数据并返回结果信息。

信号和槽函数机制更有利于模块的独立性,就是松耦合设计。比如主窗口调用子窗口公有函数比较合理,但是如果同时子窗口又需要调用父窗口 函数,那么交 叉调用函数就使得父窗口模块与子窗口模块互相依赖,子窗口就难以单独使用,失去了独立性。如果子窗口发射信号,由信号去触发父窗口的槽函数,那么子窗口就脱离了交 叉依赖,可以作为独立模块,对移植到其他项目使用更为有利。

下面我们开始编辑代码,先打开主窗口的头文件 imagetransformwidget.h,编辑代码如下:
#ifndef IMAGETRANSFORMWIDGET_H
#define IMAGETRANSFORMWIDGET_H

#include <QWidget>
#include <QLabel>
#include <QPixmap>   //图片操作类
#include <QFileDialog>  //文件选择对话框
#include <QMatrix>   //变换矩阵类
#include "resizeimagedialog.h"  //缩放尺寸对话框
#include "rotateimagedialog.h"  //旋转图片对话框

namespace Ui {
class ImageTransformWidget;
}

class ImageTransformWidget : public QWidget
{
    Q_OBJECT

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

signals:
    //发送旧的宽度高度给子对话框
    void SendOldSize(int nOldWidth, int nOldHeight);

private slots:
    void on_pushButtonOpen_clicked();

    void on_pushButtonResize_clicked();

    void on_pushButtonRotate_clicked();

    //收到新尺寸后进行缩放
    void RecvNewSizeAndResize(int nNewWidth, int nNewHeight);

private:
    Ui::ImageTransformWidget *ui;
    //图片显示的标签
    QLabel *m_pLabelImage;
    //图片操作类
    QPixmap m_image;
    //图片文件名
    QString m_strFileName;
    //缩放尺寸对话框
    ResizeImageDialog *m_pResizeDlg;
    //旋转图片对话框
    RotateImageDialog *m_pRotateDlg;
    //初始化函数
    void Init();
};

#endif // IMAGETRANSFORMWIDGET_H
我们为头文件添加了多个类包含:
QLabel 用于显示图片,QPixmap用于打开图片文件,保存图片内容,QFileDialog 用于选择图片文件名,QMatrix 是变换矩阵,用于旋转图片,然后是两个子对话框的头文件包含。
我们为主窗口类添加了信号 SendOldSize(int nOldWidth, int nOldHeight),用于给缩放图片对话框传递图片旧尺寸。
在三个按钮槽函数之后,我们手动添加了一个槽函数 RecvNewSizeAndResize(int nNewWidth, int nNewHeight),用于从缩放图片对话框接收新的尺寸,并进行实际的图片缩放操作。
添加了 m_pLabelImage,保存显示图片的标签对象;
m_image 就是图片对象内容;
m_strFileName 保存打开的文件名;
m_pResizeDlg 是缩放尺寸的子对话框;
m_pRotateDlg 是旋转图片的子对话框;
最后是用于对话框初始化的函数 Init() 。

下面我们分段编辑主窗口的源文件 imagetransformwidget.cpp,首先是构造函数和初始化部分:
#include "imagetransformwidget.h"
#include "ui_imagetransformwidget.h"
#include <QDebug>
#include <QMessageBox>

ImageTransformWidget::ImageTransformWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ImageTransformWidget)
{
    ui->setupUi(this);
    //初始化
    Init();
}

ImageTransformWidget::~ImageTransformWidget()
{
    delete m_pResizeDlg; m_pResizeDlg = NULL;
    delete m_pRotateDlg; m_pRotateDlg = NULL;
    delete ui;
}

//初始化函数
void ImageTransformWidget::Init()
{
    //新建标签对象
    m_pLabelImage = new QLabel();
    //标签内容对齐方式,左上角对齐
    m_pLabelImage->setAlignment( Qt::AlignLeft | Qt::AlignTop );
    m_pLabelImage->setStyleSheet( "background-color: lightgray;" ) ;
    //把标签设置给滚动区域,该标签隶属于滚动区域,滚动区域销毁时,也销毁该标签
    ui->scrollArea->setWidget(m_pLabelImage);

    //新建缩放尺寸对话框
    m_pResizeDlg = new ResizeImageDialog(this);
    //主窗口发送旧尺寸给子对话框
    connect(this, SIGNAL(SendOldSize(int,int)),
            m_pResizeDlg, SLOT(RecvOldSize(int,int)) );
    //子对话框发送新尺寸给主窗口
    connect(m_pResizeDlg, SIGNAL(SendNewSize(int,int)),
            this, SLOT(RecvNewSizeAndResize(int,int)) );

    //新建旋转图片对话框
    m_pRotateDlg = new RotateImageDialog(this);
    //模态对话框,要求用户输入角度后,由 exec() 函数返回角度,不需要信号和槽传递
    //使用子窗口的公有函数也可以进行数据传递
}
构造函数里面添加了 Init() 函数调用,析构函数里面删除了新建的两个子对话框。
Init() 函数里面,新建了标签存到 m_pLabelImage,并设置内容显示对齐为左上角对齐,设置背景色为浅灰色,然后把标签设置给滚动区域对象,由滚动区域对象负责管理和显示标签。
然后新建了缩放图片对话框 m_pResizeDlg,并将缩放图片对话框和主窗口的信号、槽函数关联,主窗口发送旧尺寸给子对话框,子对话框将新尺寸发给主窗口。
然后新建了旋转图片子对话框 m_pRotateDlg ,这个模态对话框用 exec() 函数返回旋转角度值。

接下来我们编辑“打开图片”按钮对应的槽函数:
//打开图片
void ImageTransformWidget::on_pushButtonOpen_clicked()
{
    //获取图片文件名
    QString strFile = QFileDialog::getOpenFileName(this, tr("打开图片文件"),
                           tr(""), tr("Image files(*.png *.jpg *.bmp)"));
    if( strFile.isEmpty() )
    {
        return; //没有文件名
    }
    //正常图片
    bool bLoadOK = m_image.load( strFile );
    if( ! bLoadOK )
    {
        QMessageBox::warning(this, tr("加载图片文件"), tr("加载图片文件失败,请检查文件格式。"));
        return;
    }
    //加载正常,保存文件名
    m_strFileName = strFile;
    //将新图片设置给标签显示
    m_pLabelImage->setPixmap( m_image );
    //窗口标题栏设置为文件名
    setWindowTitle( tr("预览文件为 %1").arg(m_strFileName) );
}
我们使用 QFileDialog 类的静态函数获取要打开的文件名,存到 strFile;
判断 strFile 如果为空就返回,不操作;如果不为空就进行后面操作。
使用 m_image.load( strFile ) 尝试加载图片内容,如果返回值为 false,加载失败,就弹窗提示加载失败,返回。
如果加载成功,保存文件名到 m_strFileName;
将图片对象设置给 m_pLabelImage,显示图片内容;
并在窗口标题栏显示预览的文件名。

接下来编辑“缩放图片”按钮对应的槽函数:
//缩放图片
void ImageTransformWidget::on_pushButtonResize_clicked()
{
    if( m_image.isNull() ) //没有图片
    {
        return;
    }
    //有图片时,发送旧尺寸给子对话框
    emit SendOldSize(m_image.width(), m_image.height());
    //显示子对话框
    m_pResizeDlg->show();
    m_pResizeDlg->raise();
}
函数先判断 m_image 对象内容,如果为空就返回;
如果图片内容不为空,那么触发 SendOldSize() 信号,将现有图片的宽度、高度发给子对话框;
调用 m_pResizeDlg 的 show() 和 raise() 显示该对话框,并放到顶层显示。

将旧尺寸发送给缩放图片对话框之后,用户在子对话框操作,设置的新尺寸会由子对话框发回给主窗口,这时候槽函数 RecvNewSizeAndResize() 接收新尺寸, 并进行实际的缩放操作,如下面代码所示:
//收到新尺寸后进行缩放
void ImageTransformWidget::RecvNewSizeAndResize(int nNewWidth, int nNewHeight)
{
    //如果尺寸没变,直接返回
    if( (m_image.width() == nNewWidth) &&
        (m_image.height() == nNewHeight) )
    {
        return;
    }
    //缩放到新尺寸
    QPixmap imgNew = m_image.scaled(nNewWidth, nNewHeight);
    //存到成员变量
    m_image = imgNew;
    //显示新图片
    m_pLabelImage->setPixmap( m_image );
}
该函数先判断新尺寸是否与原先尺寸一样大,如果是一样的就返回,不需要操作。
如果尺寸发生了变化,那么使用 m_image.scaled(nNewWidth, nNewHeight) 缩放尺寸,形成新图片 imgNew ;然后把 imgNew 复制给成员变量 m_image;
最后将缩放后的新图片 m_image 设置给标签对象进行显示。

接下来我们编辑“旋转图片”按钮对应的槽函数:
//旋转图片
void ImageTransformWidget::on_pushButtonRotate_clicked()
{
    if( m_image.isNull() ) //没有图片
    {
        return;
    }
    int nAngle = m_pRotateDlg->exec();
    if( 0 == nAngle )
    {
        return; // 没旋转
    }
    //旋转变换矩阵
    QMatrix mxRotate;
    mxRotate.rotate( nAngle );
    //执行旋转变换
    QPixmap imgNew = m_image.transformed( mxRotate );
    //保存到成员
    m_image = imgNew;
    //显示
    m_pLabelImage->setPixmap(m_image);
}
该函数首先检查 m_image 图片是否为空,为空就返回,不处理。
图片对象非空时,显示模态对话框 m_pRotateDlg->exec(),返回值就是需要旋转的角度,存到 nAngle;
判断角度是否为 0 度,如果是 0 度就返回,不需要旋转。
如果不是 0 度,定义变换矩阵 mxRotate,操作其按照 nAngle 角度旋转,得到旋转图片需要的矩阵;
然后调用 m_image.transformed( mxRotate ) ,让图片按照矩阵进行旋转变换,结果就是将图片顺时针旋转 nAngle 角度。将旋转后的图片保存到 m_image。
最后把新图片 m_image 设置给标签显示。

主窗口的代码介绍完了, 下面我们编辑缩放图片的对话框头文件 resizeimagedialog.h :
#ifndef RESIZEIMAGEDIALOG_H
#define RESIZEIMAGEDIALOG_H

#include <QDialog>

namespace Ui {
class ResizeImageDialog;
}

class ResizeImageDialog : public QDialog
{
    Q_OBJECT

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

signals:
    //发送新的尺寸给主窗口,由主窗口完成缩放
    void SendNewSize(int nNewWidth, int nNewHeight);

private slots:
    void on_pushButtonSetNewSize_clicked();

    //接收图片旧尺寸并显示
    void RecvOldSize(int nOldWidth, int nOldHeight);

private:
    Ui::ResizeImageDialog *ui;
    //初始化函数
    void Init();
};

#endif // RESIZEIMAGEDIALOG_H
我们为该对话框添加信号 SendNewSize(int nNewWidth, int nNewHeight),用于发送新尺寸给主窗口。
在按钮槽函数之后,手动添加了槽函数 RecvOldSize(int nOldWidth, int nOldHeight),用于从主窗口接收旧的图片尺寸。类声明末尾添加了初始化函数 Init() 。

下面我们分段编辑缩放图片对话框的源文件 resizeimagedialog.cpp, 首先是构造函数和初始化内容:
#include "resizeimagedialog.h"
#include "ui_resizeimagedialog.h"

ResizeImageDialog::ResizeImageDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ResizeImageDialog)
{
    ui->setupUi(this);
    //初始化
    Init();
}

ResizeImageDialog::~ResizeImageDialog()
{
    delete ui;
}

//初始化函数
void ResizeImageDialog::Init()
{
    //旧尺寸编辑器设置为只读
    ui->lineEditOldSize->setReadOnly(true);
    ui->lineEditOldSize->setStyleSheet( "background-color: lightgray;" );
    //限定新的宽度高度数值范围
    ui->spinBoxWidthNew->setRange(1, 10000);
    ui->spinBoxHeightNew->setRange(1, 10000);
    //设置标题栏
    setWindowTitle(tr("缩放图片尺寸"));
}
构造函数添加了初始化函数调用。
Init() 函数先设置旧尺寸单行编辑器为只读,并设置背景色为浅灰色。
然后设置宽度旋钮框和高度旋钮框的数值范围  1 到 10000;
设置窗口标题栏文本为 "缩放图片尺寸" 。

接下来我们编辑接收旧尺寸的槽函数:
//接收图片旧尺寸并显示
void ResizeImageDialog::RecvOldSize(int nOldWidth, int nOldHeight)
{
    //构造旧尺寸字符串并显示
    QString strOldSize = tr("%1 X %2").arg(nOldWidth).arg(nOldHeight);
    ui->lineEditOldSize->setText( strOldSize );
    //把两个旋钮框默认值改为现在的数值
    ui->spinBoxWidthNew->setValue(nOldWidth);
    ui->spinBoxHeightNew->setValue(nOldHeight);
}
从主窗口收到旧的尺寸后,我们构造旧尺寸字符串,并显示到单行编辑器  ui->lineEditOldSize ;
并把旧的宽度和高度设置给宽度旋钮框、高度旋钮框,方便用户根据旧尺寸调整新尺寸。

接下来我们编辑“设置新尺寸”按钮对应的槽函数:
//设置新尺寸,交给主窗口完成缩放
void ResizeImageDialog::on_pushButtonSetNewSize_clicked()
{
    int nNewWidth = ui->spinBoxWidthNew->value();
    int nNewHeight = ui->spinBoxHeightNew->value();
    //触发信号
    emit SendNewSize(nNewWidth, nNewHeight);
    //更新尺寸字符串
    QString strSize = tr("%1 X %2").arg(nNewWidth).arg(nNewHeight);
    ui->lineEditOldSize->setText( strSize );
}
用户编辑好新的尺寸后,点击“设置新尺寸”按钮就会触发上面槽函数。
槽函数获取新的宽度、高度,并触发信号 SendNewSize(nNewWidth, nNewHeight),从而让主窗口完成实际的缩放图片操作。
之后我们将新的宽度、高度显示到对话框上面的单行编辑器,与新图片的尺寸同步。
这个对话框是非模态显示,用户可以在对话框界面连续多次缩放图片尺寸,主窗口进行实际的缩放操作,主窗口与子对话框始终保持同步操作。

最后我们编辑旋转图片对话框的代码,头文件 rotateimagedialog.h 内容如下:
#ifndef ROTATEIMAGEDIALOG_H
#define ROTATEIMAGEDIALOG_H

#include <QDialog>

namespace Ui {
class RotateImageDialog;
}

class RotateImageDialog : public QDialog
{
    Q_OBJECT

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

private slots:
    void on_pushButtonRotating_clicked();

private:
    Ui::RotateImageDialog *ui;
    //初始化函数
    void Init();
};

#endif // ROTATEIMAGEDIALOG_H
这个模态对话框内容更简单,角度数值通过基类的 exec() 函数传递,不需要自定义函数来传递信息。在类声明末尾添加了一个初始化函数 Init(),其他代码是自动生成的。

旋转图片对话框的源文件 rotateimagedialog.cpp 内容也很简单,该源文件的全部内容如下:
#include "rotateimagedialog.h"
#include "ui_rotateimagedialog.h"

RotateImageDialog::RotateImageDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::RotateImageDialog)
{
    ui->setupUi(this);
    //初始化函数
    Init();
}

RotateImageDialog::~RotateImageDialog()
{
    delete ui;
}

//初始化函数
void RotateImageDialog::Init()
{
    //一圈的范围
    ui->spinBoxAngle->setRange(0, 360);
    ui->spinBoxAngle->setSuffix( tr("°") ); //单位:度
    //设置标题栏
    setWindowTitle(tr("旋转图片"));
}

//执行旋转
void RotateImageDialog::on_pushButtonRotating_clicked()
{
    int nAngle = ui->spinBoxAngle->value();
    done(nAngle);  //角度由 exec() 返回
}
构造函数里面添加了初始化函数 Init() 调用。
Init() 函数先设置角度旋钮的数值范围 0 到 360 度,并设置数值单位后缀为 "°" ;
设置标题栏文本为 "旋转图片" 。
“执行旋转”按钮的槽函数代码也非常简单,从角度旋钮框获取数值,然后调用对话框 done(nAngle) 函数,这个函数会关闭模态对话框,并设置 exec() 结果值为 nAngle 。主窗口获得角度值后进行实际的旋转操作。

该项目全部代码讲解完了,我们生成项目,运行示例,并打开一个图片文件:
run1
点击“缩放图片”按钮,弹出非模态对话框:
run2
原始尺寸200*200,我们把宽度修改为 300,高度不变,点击“设置新尺寸”按钮:
run3
可以看到主窗口图片拉伸到 300*200 尺寸了,明显变宽了。
我们在缩放图片对话框界面按 Esc 键,该对话框就关闭了,回到主窗口。
我们在主窗口点击“旋转图片”按钮,弹出模态对话框:
run4
我们设置角度 90 度,点击“执行旋转”按钮,模态对话框自动关闭,并且主窗口做出相应角度的旋转:
run4
示例的内容讲解到这,我们下一节开始学习 Qt 自带的各种功能对话框。


prev
contents
next