精通Qt4编程
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.3 Qt内建(built-in)对话框

为了便于程序员进行某些特定功能对话框的编程,Qt提供了一套标准的通用对话框。这些内建的对话框都提供了一些便于使用的静态函数,在Windows系统上,这些静态函数将会调用本地的Windows对话框;而在Mac OS X系统下,这些静态函数将会调用本地的Mac OS X对话框。

除了上面已经详细介绍的QMessageBox消息框之外,Qt提供的内建对话框还有:

● 颜色对话框QColorDialog,能够允许用户选择颜色,效果如图2-15所示。

● 错误消息框QErrorMessage,显示错误信息,效果如图2-16所示。

图2-15 “选择颜色”对话框

图2-16 错误消息框

● 文件对话框QFileDialog,能够允许用户选择一个或者多个文件以及目录,如图2-17所示。

图2-17 “打开文件”对话框

● 字体对话框QFontDialog,允许用户选择/设置字体,效果如图2-18所示。

图2-18 “选择字体”对话框

● 输入对话框QInputDialog,允许用户进行简单的输入,比如输入一行文本或者一个数字等,效果如图2-19所示。

● 页设置对话框QPageSetupDialog,配置与页相关的打印机选项,效果如图2-20所示。

图2-19 输入对话框

图2-20 页设置对话框

● 进度对话框QProgressDialog, 指示一个长时间操作的工作进度,以提示用户该操作是否已经停滞,效果如图2-21所示。

● 打印对话框QPrintDialog,配置打印机。可以允许用户选择可用的打印机,以及允许用户设置文档相关的设置(例如,纸张大小以及方向等)、打印类型(彩色打印或黑白打印)、页码范围和打印的份数等,效果如图2-22所示。

图2-21 进度对话框

图2-22 打印对话框

下面,看一下使用内建对话框的例子。

建立名字为“builtin”的KDevelop工程,并建立类CBuiltinDlg的定义文件和实现文件。

先看一下自定义的内建对话框类CBuiltinDlg的头文件builtindlg.h,内容如下。

        // chapter02/builtin/src/builtindlg.h.
        #ifndef _BUILTINDLG_H_
        #define _BUILTINDLG_H_
        #include <QtGui/QDialog>
        class QTextEdit;
        class QPushButton;
        class CBuiltinDlg : public QDialog
        {
            Q_OBJECT
        public:
            CBuiltinDlg(QWidget* = 0);
            virtual ~CBuiltinDlg() { }
        private:
            QTextEdit*  displayTextEdit;
            QPushButton*    colorPushBtn;
            QPushButton*    errorPushBtn;
            QPushButton*    filePushBtn;
            QPushButton*    fontPushBtn;
            QPushButton*    inputPushBtn;
            QPushButton*    pagePushBtn;
            QPushButton*    progressPushBtn;
            QPushButton*    printPushBtn;
        private slots:
            void doPushBtn();
        };
        #endif

在类CBuiltinDlg的私有区,声明了一个文本编辑框,用来存放显示实例的文本信息;接下来,声明了控制Qt内建对话框显示的QPushButton按钮对象,共7个。

私有槽函数doPushBtn()响应QPushButton按钮对象的单击操作信号clicked()。

接下来,看一下自定义的内建对话框类的实现文件。

首先将必要的头文件包含进来。

        // chapter02/builtin/src/builtindlg.cpp.
        #include <QtGui/QtGui>
        #include "builtindlg.h"

下面是内建对话框类CBuiltinDlg的构造函数。

        CBuiltinDlg::CBuiltinDlg(QWidget* parent)
        :  QDialog(parent)
        {
            displayTextEdit = new QTextEdit(tr("Qt的标准通用对话框。"));

创建一个显示内容为“Qt的标准通用对话框。”的文本编辑框QTextEdit对象,该文本编辑框将在演示Qt的各种内建对话框时用到。

        QGridLayout* gridLayout = new QGridLayout;
        colorPushBtn    = new QPushButton(tr("颜色对话框"));
        errorPushBtn    = new QPushButton(tr("错误消息框"));
        filePushBtn     = new QPushButton(tr("文件对话框"));
        fontPushBtn     = new QPushButton(tr("字体对话框"));
        inputPushBtn    = new QPushButton(tr("输入对话框"));
        pagePushBtn     = new QPushButton(tr("页设置对话框"));
        progressPushBtn = new QPushButton(tr("进度对话框"));
        printPushBtn    = new QPushButton(tr("打印对话框"));
        gridLayout->addWidget(colorPushBtn, 0, 0, 1, 1);
        gridLayout->addWidget(errorPushBtn, 0, 1, 1, 1);
        gridLayout->addWidget(filePushBtn, 0, 2, 1, 1);
        gridLayout->addWidget(fontPushBtn, 1, 0, 1, 1);
        gridLayout->addWidget(inputPushBtn, 1, 1, 1, 1);
        gridLayout->addWidget(pagePushBtn, 1, 2, 1, 1);
        gridLayout->addWidget(progressPushBtn, 2, 0, 1, 1);
        gridLayout->addWidget(printPushBtn, 2, 1, 1, 1);
        gridLayout->addWidget(displayTextEdit, 3, 0, 3, 3);

        setLayout(gridLayout);

创建一个网格布局管理器QGridLayout对象,gridLayout布局管理器将会管理和排布所有的窗口部件。

接下来,创建7个QPushButton对象,这些对象分别用来控制颜色对话框、错误消息框、文件对话框、字体对话框、输入对话框、页设置对话框、进度对话框和打印对话框的创建和显示。

然后调用QGridLayout::addWidget()函数,将所有的QPushButton以及QTextEdit窗口部件排布在网格布局管理器gridLayout中。

最后,函数QDialog::setLayout()将网格布局管理器gridLayout设置为内建对话框CBuiltinDlg对象的顶层布局管理器。

        connect(colorPushBtn, SIGNAL(clicked()), this, SLOT(doPushBtn()));
        connect(errorPushBtn, SIGNAL(clicked()), this, SLOT(doPushBtn()));
        connect(filePushBtn, SIGNAL(clicked()), this, SLOT(doPushBtn()));
        connect(fontPushBtn, SIGNAL(clicked()), this, SLOT(doPushBtn()));
        connect(inputPushBtn, SIGNAL(clicked()), this, SLOT(doPushBtn()));
        connect(pagePushBtn, SIGNAL(clicked()), this, SLOT(doPushBtn()));
        connect(progressPushBtn, SIGNAL(clicked()), this, SLOT(doPushBtn()));
        connect(printPushBtn, SIGNAL(clicked()), this, SLOT(doPushBtn()));

将所有的QPushButton对象的clicked()信号关联到内建对话框类CBuiltinDlg的槽函数doPushButton(),即所有QPushButton对象的单击操作都由统一的槽函数doPushButton()来处理。

            setWindowTitle(tr("内建对话框"));
            resize(400, 300);
        }

函数QDialog::setWindowTitle()设置对话框的标题为“内建对话框”。

最后,调用函数QDialog::resize()改变对话框的大小尺寸。

下面,看一下槽函数doPushBtn(),它接收并处理所有的QpushButton按钮的单击信号clicked()。

        void CBuiltinDlg::doPushBtn()
        {
            QPushButton* btn = qobject_cast<QPushButton*>(sender());
            if(btn == colorPushBtn)
            {// 颜色对话框.
            QPalette palette = displayTextEdit->palette();
            const QColor& color =
                QColorDialog::getColor(palette.color(QPalette::Base), this);
            if(color.isValid())
            {
                palette.setColor(QPalette::Base, color);
                displayTextEdit->setPalette(palette);
            }
          }

槽函数的开头,首先是获取发送信号的QPushButton对象的指针。函数QOjbect::sender()返回发送信号的对象的指针,返回类型为QObject*。模板函数

        T qobject_cast(QObject* object)

完成类型的转换,将<QOjbect*>类型的对象指针转换为类型为<T *>的对象指针,如果转换成功,返回正确的对象指针,否则返回0。

注意

类型T必须是直接或间接继承自QOjbect的类,并且在该类的定义里有Q_OBJECT宏变量(否则qobject_cast()函数的返回值是未定义的)。

此外,可以认为一个类继承自它自身。qobject_cast()模板函数的作用类似于标准C++的dynamic_cast()模板函数,不过qobject_cast()不需要运行时类型信息(Run-Time Type Information, RTTI)的支持。

接下来是if()条件判断语句,判断发送信号的对象是否是相应的QPushButton对象(colorBtn、errorPushBtn等),如果是则创建相应的Qt内建对话框并进行显示;否则将会跳过该段代码,直到找到条件为true的if()条件语句。

上面这段代码是颜色对话框的例子,它的功能是利用Qt内建的颜色对话框QColorDialog类获取用户选择的颜色,然后设置文本编辑框的背景色。

函数QTextEdit::palette()获取文本编辑框对象displayTextEdit的调色板。

接下来,调用QColorDialog::getColor()函数创建并显示一个模态的颜色对话框,并返回用户选择的颜色对象的常量引用(对临时对象的引用是无效的,必须使用常量引用)赋给变量color;如果用户单击“取消”按钮,颜色对话框将返回一个无效的颜色;颜色对话框的颜色初始化为palette.color (QPalette::Base),即文本编辑框的背景色;父窗口部件为this。

QColor::isValid() 函数判断颜色对话框返回的颜色是否有效。如果颜色对话框返回的颜色是有效的,函数QPalette::setColor() 设置调色板的背景颜色为颜色对话框返回的颜色。此处的QPalette::setColor() 函数具有2个参数,第1个参数QPalette::Base指定了调色板的角色,告诉程序应该设置调色板的所有三个颜色组中的哪一个角色的颜色(该函数将会影响到所有的三个颜色组);第2个参数color指定应该设置的颜色。最后,函数QtextEdit::setPalette()重新设置文本编辑框的调色板。

扩展阅读

关于Qt调色板类QPalette

QPalette类包含了Qt窗口部件的颜色组(color group):

● Active组,该组的颜色用于当前活跃的(active)窗口,即具有键盘或鼠标焦点的窗口;

● Inactive组,该组用于其他的窗口;

● Disabled组,该组用于状态为不可用的(disabled)的子窗口部件(不包含窗口)。

所有Qt窗口部件都拥有一个调色板并使用它绘制自己。通常,活跃状态的窗口的标题栏显示为蓝色的,而非活跃(inactive)状态的窗口的标题栏显示为灰色的;活跃状态的窗口和非活跃状态的窗口都可以包含状态为不可用的窗口部件,一个不可用的窗口部件(包括该窗口部件包含的子窗口部件)显示为灰色的,用户是无法与它进行交互的。通过改变窗口部件的调色板的各个组中的颜色,能够改变窗口部件的显示颜色,比如改变背景色、文本颜色等。

可以通过QWidget::palette()获取一个窗口部件的调色板,然后通过QWidget::setPalette()函数为该窗口部件设置修改后的调色板。或者通过QApplication::palette() 函数获取应用程序的调色板,并通过QApplication::setPalette() 为该应用程序设置修改后的调色板。修改一个窗口部件的调色板只会影响到该窗口部件以及子窗口部件(不包含子窗口);而改变一个应用程序的调色板将会影响到该应用程序的所有窗口部件。当对一个窗口部件的调色板已经作了修改后,再对其父窗口部件调色板的修改不会影响到该窗口部件的调色板;同样,对应用程序调色板的修改不会影响已经单独做出了调色板修改的窗口部件。

调色板类QPallete提供了颜色角色(color roles)概念,是指当前GUI界面中颜色的职责,通过枚举变量QPalette::ColorRole来定义,比较常用的颜色角色有:

● QPalette::Window,通常指窗口部件的背景色;

● QPalette::WindowText,通常指窗口部件的前景色;

● QPalette::Base,指文本输入窗口部件(比如QTextEdit、QLineEidt等)的背景色,上面的例子中使用了该颜色角色;

● QPalette::Text,与QPalette::Base一块使用,指文本输入窗口部件的前景色;

● QPalette::Button,指按钮窗口部件的背景色;

● QPalette::ButtonText,指按钮窗口部件的前景色。

图2-23 颜色角色

        else if(btn == errorPushBtn)
        {// 错误消息框.
            QErrorMessage box(this);
            box.setWindowTitle(tr("错误消息框"));
            box.showMessage(tr("错误消息框实例xx。"));
            box.showMessage(tr("错误消息框实例xx。"));
            box.showMessage(tr("错误消息框实例xx。"));
            box.showMessage(tr("错误消息框实例yy。"));
            box.showMessage(tr("错误消息框实例zz。"));
            box.exec();
        }

该段代码是一个错误消息框的例子。在这个例子中,多次调用了QErrorMessage::showMessage()函数,该函数的功能是在错误消息框中显示错误信息。多次调用该函数,是为了演示显示不同错误信息、多个相同错误信息以及错误消息框的“再次显示这个消息”复选框选中与否的效果。最后,执行QErrorMessage::exec()显示对话框。此外,还可以new一个错误消息框对象,仅仅通过QErrorMessage::showMessage() 函数就可以显示对话框,而无需执行QErrorMessage::exec() 函数。

            else if(btn == filePushBtn)
            {// 文件对话框.
            QString fileName = QFileDialog::getOpenFileName(
                                      this,
                                      tr("打开文件"),
                                      "/home/czm",
                                      tr("任何文件(*.*)"
                                      ";;文本文件(*.txt)"
                                                ";;XML文件(*.xml)"));
            displayTextEdit->setText(fileName);
        }

该段代码打开一个文件对话框,获取用户选择的文件名并显示在文本编辑框中。此处,函数QFileDialog::getOpenFileName()具有4个参数。其中,实参this指定文件对话框的父窗口部件;实参tr("打开文件") 指定文件对话框的标题;实参“/home/czm”指定了文件对话框的默认路径;最后一个参数比较复杂,它指定了文件对话框的多个文件过滤器,过滤器之间通过两个分号“;;”间隔。如果用户选择了文件,并单击“确定”按钮,那么该文件对话框将返回用户选择的文件名;而如果用户单击“取消”按钮,该对话框将返回一个NULL字符串。

        else if(btn == fontPushBtn)
        {// 字体对话框.
            bool ok;
            const QFont& font = QFontDialog::getFont(&ok,
                                    displayTextEdit->font(),
                                    this,
                                    tr("字体对话框"));
            if(ok)
            {// 如果<确定>,设置字体.
            displayTextEdit->setFont(font);
            }
        }

QFontDialog::getFont() 函数创建并显示一个字体对话框。此处,该函数具有四个参数:第1个参数是一个输出参数,用于标识用户的选择状态,如果用户单击“确定”按钮,该字体对话框将会设置ok变量为true;而如果用户单击“取消”按钮,ok将会被设置为false。第2个参数指定了对话框的初始颜色,当用户取消颜色的选择时,字体对话框将初始颜色作为返回值。this参数指定了父窗口部件,最后一个参数指定了字体对话框的标题。

        else if(btn == inputPushBtn)
        {// 输入对话框.
            bool ok;
            QString text = QInputDialog::getText(this,
                                    tr("输入对话框"),
                                    tr("输入文本:"),
                                    QLineEdit::Normal,
                                    QDir::home().dirName(),
                                    &ok);
            if (ok && !text.isEmpty())
            displayTextEdit->setText(text);
        }

QInputDialog::getText() 函数创建并显示一个文本输入对话框。我们赋给该函数6个参数,前2个参数分别指定了输入对话框的父窗口部件和对话框的标题;第3个参数指定了标签的显示文本;第4个参数指定了行编辑框QLineEdit输入内容的显示模式,此处为QLineEdit::Normal,即按用户输入的实际内容显示;第5 个参数指定了行编辑框默认显示的内容,函数QDir::home() 返回用户的home路径,QDir::dirName() 返回路径的名字;最后一个参数和QFontDialog::getFont()函数的第1个参数的作用相同。

        else if(btn == pagePushBtn)
        {// 页设置对话框.
            QPrinter printer;
            QPageSetupDialog dlg(&printer, this);
            dlg.setWindowTitle(tr("页设置对话框"));
            if (dlg.exec() == QDialog::Accepted)
            {
            // 进行下一步的处理。
            }
        }

首先,定义了一个打印机QPrinter对象printer。然后创建了一个页设置对话框QPageSetupDialog对象,并设置对话框的标题。在这个例子中,只是简单地创建和显示一个页设置对话框,该对话框返回后没有进行下一步的处理。

            else if(btn == progressPushBtn)
            {// 进度对话框.
            QProgressDialog progress(tr("正在复制文件..."),
                                        tr("取消"),
                                        0,
                                        10000,
                                        this);
            progress.setWindowModality(Qt::WindowModal);
            progress.setWindowTitle(tr("进度对话框"));
            progress.show();
            for (int i = 0; i < 10000; i++)
            {
                progress.setValue(i);
                qApp->processEvents();
                if (progress.wasCanceled())
                        break;
                //... 复制文件处理。
                qDebug() << i;
            }
            progress.setValue(10000);
        }

这一段代码创建了一个进度对话框,并模拟了显示一个长时间操作的工作进程。

首先,调用了QProgressDialog的构造函数,创建了一个进度对话框的栈对象。构造函数有5个参数。第1个参数是一个字符串,它指定了显示给用户的提示信息,表明当前正在进行的工作:第2个参数指定了“取消”按钮的显示文本,如果该参数为0的话,进度对话框将没有“取消”按钮,即创建进度对话框的代码为

        QProgressDialog progress(tr("正在复制文件..."), 0 , 0, 10000, this)

接下来的2 个参数分别指定了操作的步数(在上面的例子中,可以假定进度对话框显示复制10000个文件的进展情况,第3个参数设定为0,第4个参数设定为10000,即这两个参数的差值决定了这个复制操作的步数为10000)。第5个参数指定了进度对话框的父窗口部件。

接下来,函数setWindowModality()设置进度对话框的类型为Qt::WindowModal,即为模态对话框。有两种方式使用进度对话框QProgressDialog:

● 模态对话框方式。使用一个模态进度对话框显示长时间操作的工作进度对于编程是比较简单的,但是必须调用QApplication::processEvents() 函数或者QEventLoop::processEvents (QEventLoop::ExcludeUserInputEvents)函数以保证事件循环还可以处理其他事件,以防止应用程序因为长时间的操作而导致没有反应。在自定义对话框的例子中,使用了模态进度对话框,通过QProgressDialog::setValue() 函数推进显示的进度;通过QProgressDialog::wasCancled() 函数判断用户是否中途选择了“取消”按钮,如果是,将中断复制文件操作。此外,代码使用了qDebug() 函数打印for()语句的运行进度,模拟复制操作。

● 非模态对话框方式。非模态进度对话框适用于显示在后台运行的长时间操作的工作进度,这样的话,用户就能够和应用程序的其他窗口进行交互。

            else if(btn == printPushBtn)
            {// 打印对话框.
            QPrinter printer;
            QPrintDialog dlg(&printer, this);
            dlg.setWindowTitle(tr("打印对话框"));
            if (dlg.exec() == QDialog::Accepted)
            {
                // 进行下一步的处理。
            }
          }
        }

创建并显示打印对话框的例子很简单,在对话框返回后没有进行下一步的处理。

现在修改主程序文件builtin.cpp(内容基本和mydialog.cpp相同,不同的是把包含的头文件替换为“builtindlg.h”,对话框类改为“CBuiltinDlg”),并编译运行内建对话框应用程序,显示效果如图2-23所示。

图2-24 “内建”对话框