第八章 Qt GUI之对话框使用
对话框可以是模态(modal)的或非模态(modeless)两种。当我们在一个用户界面程序里面对一个对话框(比如选择文件对话框)的操作没有结束前,界面的其他窗口无法操作,遇到的这个对话框就是模态对话框,而当我们在一个字处理软件中利用查找和替换对话框时,可以在字处理软件和查找替换对话框之间切换进行交互,这就是个非模态对话框。
先来看一下QDialog类的继承关系,如下图所示。
QDialog从QWidget继承,然后它下面又被Qt的内置对话框类(QFileDialog选择文件或目录对话框、QFontDialog选择字体对话框、QMessageBox消息提示对话框等)继承。用户要实现自己的对话框,需要继承自QDialog类,并包含头文件<QDialog>。
一、快速设计对话框
例子1:
实现一个Find(查找对话框),它的运行效果如下图所示,这将实现一个拥有自主权的对话框。
程序清单如下:
finddialog.h
#ifndef FINDDIALOG_H
#define FINDDIALOG_H #include <QDialog>
#include <QCheckBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton> class FindDialog : public QDialog
{
Q_OBJECT public:
FindDialog(QWidget *parent = );
~FindDialog(); signals:
void findNext(const QString &str, Qt::CaseSensitivity cs);
void findPrevious(const QString &str, Qt::CaseSensitivity cs); private slots:
void findClicked();
void enableFindButton(const QString &text); private:
QLabel *label;
QLineEdit *lineEdit;
QCheckBox *caseCheckBox;
QCheckBox *backwardCheckBox;
QPushButton *findButton;
QPushButton *closeButton;
}; #endif // FINDDIALOG_H
finddialog.cpp:
#include <QtGui>
#include "finddialog.h" FindDialog::FindDialog(QWidget *parent)
: QDialog(parent)
{
label = new QLabel(tr("Find &what")); //tr()函数是把它们翻译成其他语言的标志,“&”来表示快捷键(Alt+W)
lineEdit = new QLineEdit;
label->setBuddy(lineEdit);//设置行编辑器为标签的伙伴,按下标签的快捷键(Alt+W)时接收焦点,焦点会移动到行编辑器 caseCheckBox = new QCheckBox(tr("Math &case"));
backwardCheckBox = new QCheckBox(tr("Search &backward")); findButton = new QPushButton(tr("&Find"));
findButton->setDefault(true);//设置“Find”按钮为默认按钮,默认按钮就是当用户Enter键时能够按下对应的按钮
findButton->setEnabled(false);//禁用“Find”按钮,它通常显示为灰色,不能和用户进行交互操作 closeButton = new QPushButton(tr("Close")); connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(enableFindButton(const QString &)));
connect(findButton, SIGNAL(clicked()), this, SLOT(findClicked()));
connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); QHBoxLayout *topLeftLayout = new QHBoxLayout;
topLeftLayout->addWidget(label);
topLeftLayout->addWidget(lineEdit); QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(backwardCheckBox); QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(findButton);
rightLayout->addWidget(closeButton);
rightLayout->addStretch(); QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout); //将mainLayout布局安装在FindDialog setWindowTitle(tr("Find"));
setFixedHeight(sizeHint().height());
} void FindDialog::findClicked()
{
QString text = lineEdit->text();
Qt::CaseSensitivity cs = caseCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive;
if (backwardCheckBox->isChecked())
{
emit findPrevious(text, cs);
}
else
{
emit findNext(text, cs);
}
} void FindDialog::enableFindButton(const QString &text)
{
findButton->setEnabled(!text.isEmpty());
} FindDialog::~FindDialog()
{ }
main.cpp
#include <QApplication>
#include "finddialog.h" int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FindDialog *w = new FindDialog;
w->show(); return a.exec();
}
编译运行就出现上图效果了。
例子2:
使用Qt designer设置对话框,实现效果如下图:
基本步骤:
1、创建并初始化子窗口部件;
2、把子窗口部件放在布局中;
3、设置Tab键顺序;
4、建立信号-槽之间的连接;
5、实现对话框的自定义槽。
第一步是创建子窗口部件并且把它们放置到窗体中。创建一个标签(Label)、一个行编辑器(Line Edit)、一个水平分隔符(Horizontal Spacer)、两个按钮(Push Button)。
使用Qt设计师的属性编辑器设置每个窗口部件的属性:
1、单击文本标签。确保此时objectName的属性是“label”,将它的text属性设置成“&Cell Location”。
2、单击行编辑器。确保objectName属性是“lineEdit”。
3、单击第一个按钮。将它的objectName属性设置成“okButton”,将它的enabled属性设置成“false”,将它的text属性设置成“OK”,并且把它的default属性设置成“true”。
4、单击第二个按钮。将它的objectName属性设置成“cancelButton”,并且将它的text属性设置成“Cancel”。
5、单击这个窗体中空白的地方,选中窗体本身。将它的windowTitle属性设置成“Go to Cell”。
6、单击Edit->Edit Buddies进入一种允许设置窗口部件伙伴(buddy)的特殊模式。然后,单击这个标签并把红色箭头拖到行编辑器上,释放鼠标按键。单击Edit->Edit Widgets离开伙伴设置模式。
下一步是在窗体中摆放这些窗口部件,步骤如下:
1、单击“Cell Location”标签并且按下ctrl键是单击与之相邻的行编辑器,这样就可以同时选择了它们,然后选择水平布局(Lay Out Horizontally)。
2、选中分隔符、OK按钮和Cancel按钮,然后选择水平布局(Lay Out Horizontally)。
3、单击窗体中的空白,取消对所有已选中项的选择,然后单击垂直布局(Lay Out Vertically)。
4、单击Edit->Edit Tab Order。在每一个可以接受焦点的窗口部件上,都会出现一个带蓝色矩形的数字,如下图所示。按照你所希望的接受焦点的顺序单击每一个窗口部件,然后单击Edit->Edit Widgets离开Tab键顺序设置模式。
接下来添加代码:
main.cpp
#include <QApplication>
#include "mywidget.h" int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyDialog w;
w.show(); return a.exec();
}
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H #include <QDialog> namespace Ui {
class MyDialog;
} class MyDialog : public QDialog
{
Q_OBJECT public:
explicit MyDialog(QDialog *parent = );
~MyDialog(); private slots:
void on_lineEdit_textChanged(); private:
Ui::MyDialog *ui;
}; #endif // MYWIDGET_H
mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h" MyDialog::MyDialog(QDialog *parent) :
QDialog(parent),
ui(new Ui::MyDialog)
{
ui->setupUi(this); QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");//建立一个正则表达式
ui->lineEdit->setValidator(new QRegExpValidator(regExp, this)); //检验器限制输入的范围,
//Qt提供3个内置检验器类:QIntValidator、QDoubleValidator、QRegExpValidator connect(ui->okButton, SIGNAL(clicked()), this, SLOT(accept())); //accept()槽函数关闭对话框,将对话框返回的结果变量设置为QDialog::Accepted(其值为1)
connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject()));//reject()槽函数关闭对话框,将对话框返回的结果变量设置为QDialog::Reject(其值为0)
} void MyDialog::on_lineEdit_textChanged()
{
ui->okButton->setEnabled(ui->lineEdit->hasAcceptableInput());
} MyDialog::~MyDialog()
{
delete ui;
}
最后点击编译运行就ok了。
二、可扩展的对话框
这部分内容直接截取别人的,不想写。
前面我们设计的对话框都是不能改变它的样子的。但是有时需要对话框根据要求进行适当的改变。两个最常用的需要改变的对话框是可扩展对话框和多页对话框。这两种类型的可以通过代码编写,也可以用Qt Designer设计。一个例子如下图所示:
可扩展对话框通常外观简单,带有一个可扩展按钮来切换对话框的简单外观和可扩展外观。这种对话框通常为了迎合普通用户和高端用户而设计的,如果没有特别请求隐藏高级应用部分。在这个实验中,我们使用 Qt Designer设计一个可扩展对话框。
对话框是一个表格程序的排序对话框,对用户选择的一些列按要求排列。对话框的简单外观允许用户输入一个简单排序关键词,扩展部分允许输入两个额外的排序关键词。一个More按钮使用户在简单外观和扩展外观进行切换。
我们使用 Qt Designer创建这个可扩展的对话框,在运行时刻隐藏高级功能,这个看起来很复杂的对话框用Qt Designer可以很容易实现。首先设计好第一个关键词,第二个和第三个关键词通过复制就可以得到:
1、 启动“文件”?“新建”菜单,选择“Dialog without Buttons”模板。
2、 拖动一个PushButton按钮并把它拖放到窗体的右上角。将它的objectName修改为“okButton”,并将它的default属性设置为“true”,将它的text属性设置为“确定”,这就创建了OK按钮。
3、 同样的办法创建Cancel按钮,放到OK按钮的下方,将objectName修改为“cancelButton”,将text属性设置为“取消”。
4、 创建一个垂直分隔符(Vertical Spacer)并将它放到Cancel按钮的下方,然后再创建一个More按钮,并将它放在垂直分隔符的下方,将More按钮的objectName修改为“moreButton”,text属性设置为“(&M)更多”,checkable属性设置为“true”。
5、 选择OK按钮,Cancel按钮,垂直分隔符和More按钮,将它们的布局设定为“垂直布局”。
6、 创建一个群组框(GroupBox),两个标签(Label),两个下拉组合框(Combo Box)和一个水平分隔符(Horizontal Spacer),先把它们放在对话框的任何地方。
7、 拖动群组框的右下角,把它拖动变大些,把上一步中其他控件移动到群组框中,按比例调整位置。当你按住鼠标左键将其他部件拖动到群组框内的时候,应该在群组框显示出灰色的时候才松开鼠标,否则,有可能部件只是挨着群组框并没有正确的放入其中。
8、 拖动第二个下拉组合框,将其宽度调整为第一个下拉框的二倍左右。现在看起来的情况大概如下图所示:
9、 将群组框的title属性设置为“&Primary Key”,第一个标签的text属性设置为“Column:”,
第二个标签的text属性为“Order:”。
10、 右键单击第一个组合框,从Qt设计师弹出的右键菜单的组合框编辑器中选择“编辑项目”,
用文本“None”创建一个项目。
11、同样的方式对第二个组合框创建两个项目,项目为“Ascending”和“Descending”两个项目,
即升序和降序排列。
12、右键单击群组框,然后选择“布局”?“栅格布局”。再次右键单击群组框,并选择“布局”
->“调整大小”,这个操作也可以通过单击工具栏上的按钮“”来完成,产生的布局如上图2的右图所示布局。
如果设计过程中出现错误,可以选择 “编辑”?“撤销”或者“打破布局”工具按钮,重新进行布局。当然只要看起来不是很难看,也可以是其他的样子,只要易于理解就是ok。
现在加入其它两个群组框:Secondary Key和Teriary Key:
1、将对话框拖动到足够大,以便能容纳下另外两个部分。
2、复制第一个组合框,粘贴两次,依次拖动到下面。
3、把复制的两个组合框的title属性为“&Secondary Key”和“&Tertiary Key”。
4、创建一个垂直分隔符(Vertical Spacer),并将它放在Primary Key群组框和Secondary Key群组框的中间。
5、适当调整添加的控件的位置。
6、选择对话框中的所有控件,降它们的布局设置为“栅格布局”,得到的效果应该如下图3(a)。
7、单击窗体,取消对窗口中任意控件的选择,将整个对话框窗体的布局设置为“栅格布局”。然后向上和向左拖动窗体的右下角,将窗体变得尽可能小,设置两个垂直空白的sizeHint属性为[20,0]。现在窗体应该像下图3(b)所示:
最终的网格布局是4行2列,一共有8个单元格。如果你做出来的不是这样,那么请撤销布局,重新进行布局。
按照下图命名每一个控件。命名对话框为 SortDialog,窗口标题为“Sort”。对各个控件进行命名,也就是修改控件的objectName属性,最终结果如下图4:
设置Tab顺序,从上到下点击下拉框,然后点击Ok,Cancel,More按钮。最终的顺序如下图(a):
以上是对话框的设计。然后用 Qt Designer建立控件的信号连接,单击工具栏上的(即编辑信号/槽),切换到信号/槽的编辑状态。将“确认”和“取消”按钮连接到对话框的accept()和reject()槽函数。操作方法是点击“确认按钮”并拖动到对话框的空白处即可,然后在弹出的对话框中左边点选clicked()信号,右边点选accept()槽函数,如上图(b)所示。同样的方法将“取消”按钮连接到reject()槽函数。
然后连接“更多”按钮和secondaryGroupBox群组框,将按钮的toggled(bool)信号和群组框的setVisible(bool)槽函数连接。同样将“更多”按钮与tertiaryGroupBox群组框setVisible(bool)槽函数连接。在连接的时候,可能看不到群组框的setVisible(boo)槽函数,这时在连接对话框(图5(b))中点选“显示从QWidget继承的信号和槽”即可看到。
最终的连接情况如下图6所示,我们可以在“信号/槽编辑器”中清楚地看到连接情况。
创建一个 sort目录,将对话框保存到sort目录下,文件名为:sortdialog.ui。
下面给这个窗体添加代码,我们使用多继承的方式使用这个对话框,也就是创建一个新的类来继承QDialog类和这个窗体的类。
首先新建一个sortdialog.h头文件,代码如下:
然后新建sortdialog.cpp源文件:
#include <QtGui>
#include "sortdialog.h"
SortDialog::SortDialog(QWidget *parent):QDialog(parent)
{
setupUi(this);
secondaryGroupBox->hide();
tertiaryGroupBox->hide();
layout()->setSizeConstraint(QLayout::SetFixedSize);
setColumnRange('A', 'Z');
}
void SortDialog::setColumnRange(QChar first, QChar last)
{
primaryColumnCombo->clear();
secondaryColumnCombo->clear();
tertiaryColumnCombo->clear();
secondaryColumnCombo->addItem(tr("None"));
tertiaryColumnCombo->addItem(tr("None"));
primaryColumnCombo->setMinimumSize(secondaryColumnCombo->sizeHint());
QChar ch = first;
while (ch <= last) {
primaryColumnCombo->addItem(QString(ch));
secondaryColumnCombo->addItem(QString(ch));
tertiaryColumnCombo->addItem(QString(ch));
ch = ch.unicode() + ;
}
}
在构造函数中,隐藏了secondaryGroupBox和tertiaryGroupBox群组框部分。并设置对话框的sizeConstraint的属性为QLayout::setFixedSize,这样用户不能随便改变对话框的大小。
下面是main.cpp文件:
编译运行这个程序,点击“更多”按钮,查看对话框的改变,结果应该与图1一样。另一种可以改变的对话框是多页对话框。这类对话框也可以用两种方式创建。相关的类有QTabWidget,QStackedWidget,QListWidget,QTreeWidget等。