第八章 Qt GUI之对话框使用
对话框可以是模态(modal)的或非模态(modeless)两种。当我们在一个用户界面程序里面对一个对话框(比如选择文件对话框)的操作没有结束前,界面的其他窗口无法操作,遇到的这个对话框就是模态对话框,而当我们在一个字处理软件中利用查找和替换对话框时,可以在字处理软件和查找替换对话框之间切换进行交互,这就是个非模态对话框。
先来看一下QDialog类的继承关系,如下图所示。
QDialog从QWidget继承,然后它下面又被Qt的内置对话框类(QFileDialog选择文件或目录对话框、QFontDialog选择字体对话框、QMessageBox消息提示对话框等)继承。用户要实现自己的对话框,需要继承自QDialog类,并包含头文件<QDialog>。
一、快速设计对话框
例子1:
实现一个Find(查找对话框),它的运行效果如下图所示,这将实现一个拥有自主权的对话框。
程序清单如下:
finddialog.h
1 #ifndef FINDDIALOG_H 2 #define FINDDIALOG_H 3 4 #include <QDialog> 5 #include <QCheckBox> 6 #include <QLabel> 7 #include <QLineEdit> 8 #include <QPushButton> 9 10 class FindDialog : public QDialog 11 { 12 Q_OBJECT 13 14 public: 15 FindDialog(QWidget *parent = 0); 16 ~FindDialog(); 17 18 signals: 19 void findNext(const QString &str, Qt::CaseSensitivity cs); 20 void findPrevious(const QString &str, Qt::CaseSensitivity cs); 21 22 private slots: 23 void findClicked(); 24 void enableFindButton(const QString &text); 25 26 private: 27 QLabel *label; 28 QLineEdit *lineEdit; 29 QCheckBox *caseCheckBox; 30 QCheckBox *backwardCheckBox; 31 QPushButton *findButton; 32 QPushButton *closeButton; 33 }; 34 35 #endif // FINDDIALOG_H
finddialog.cpp:
1 #include <QtGui> 2 #include "finddialog.h" 3 4 FindDialog::FindDialog(QWidget *parent) 5 : QDialog(parent) 6 { 7 label = new QLabel(tr("Find &what")); //tr()函数是把它们翻译成其他语言的标志,“&”来表示快捷键(Alt+W) 8 lineEdit = new QLineEdit; 9 label->setBuddy(lineEdit);//设置行编辑器为标签的伙伴,按下标签的快捷键(Alt+W)时接收焦点,焦点会移动到行编辑器 10 11 caseCheckBox = new QCheckBox(tr("Math &case")); 12 backwardCheckBox = new QCheckBox(tr("Search &backward")); 13 14 findButton = new QPushButton(tr("&Find")); 15 findButton->setDefault(true);//设置“Find”按钮为默认按钮,默认按钮就是当用户Enter键时能够按下对应的按钮 16 findButton->setEnabled(false);//禁用“Find”按钮,它通常显示为灰色,不能和用户进行交互操作 17 18 closeButton = new QPushButton(tr("Close")); 19 20 connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(enableFindButton(const QString &))); 21 connect(findButton, SIGNAL(clicked()), this, SLOT(findClicked())); 22 connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); 23 24 QHBoxLayout *topLeftLayout = new QHBoxLayout; 25 topLeftLayout->addWidget(label); 26 topLeftLayout->addWidget(lineEdit); 27 28 QVBoxLayout *leftLayout = new QVBoxLayout; 29 leftLayout->addLayout(topLeftLayout); 30 leftLayout->addWidget(caseCheckBox); 31 leftLayout->addWidget(backwardCheckBox); 32 33 QVBoxLayout *rightLayout = new QVBoxLayout; 34 rightLayout->addWidget(findButton); 35 rightLayout->addWidget(closeButton); 36 rightLayout->addStretch(); 37 38 QHBoxLayout *mainLayout = new QHBoxLayout; 39 mainLayout->addLayout(leftLayout); 40 mainLayout->addLayout(rightLayout); 41 setLayout(mainLayout); //将mainLayout布局安装在FindDialog 42 43 setWindowTitle(tr("Find")); 44 setFixedHeight(sizeHint().height()); 45 } 46 47 void FindDialog::findClicked() 48 { 49 QString text = lineEdit->text(); 50 Qt::CaseSensitivity cs = caseCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive; 51 if (backwardCheckBox->isChecked()) 52 { 53 emit findPrevious(text, cs); 54 } 55 else 56 { 57 emit findNext(text, cs); 58 } 59 } 60 61 void FindDialog::enableFindButton(const QString &text) 62 { 63 findButton->setEnabled(!text.isEmpty()); 64 } 65 66 FindDialog::~FindDialog() 67 { 68 69 }
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
1 #include <QApplication> 2 #include "mywidget.h" 3 4 int main(int argc, char *argv[]) 5 { 6 QApplication a(argc, argv); 7 MyDialog w; 8 w.show(); 9 10 return a.exec(); 11 }
mywidget.h
1 #ifndef MYWIDGET_H 2 #define MYWIDGET_H 3 4 #include <QDialog> 5 6 namespace Ui { 7 class MyDialog; 8 } 9 10 class MyDialog : public QDialog 11 { 12 Q_OBJECT 13 14 public: 15 explicit MyDialog(QDialog *parent = 0); 16 ~MyDialog(); 17 18 private slots: 19 void on_lineEdit_textChanged(); 20 21 private: 22 Ui::MyDialog *ui; 23 }; 24 25 #endif // MYWIDGET_H
mywidget.cpp
1 #include "mywidget.h" 2 #include "ui_mywidget.h" 3 4 MyDialog::MyDialog(QDialog *parent) : 5 QDialog(parent), 6 ui(new Ui::MyDialog) 7 { 8 ui->setupUi(this); 9 10 QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");//建立一个正则表达式 11 ui->lineEdit->setValidator(new QRegExpValidator(regExp, this)); //检验器限制输入的范围, 12 //Qt提供3个内置检验器类:QIntValidator、QDoubleValidator、QRegExpValidator 13 14 connect(ui->okButton, SIGNAL(clicked()), this, SLOT(accept())); //accept()槽函数关闭对话框,将对话框返回的结果变量设置为QDialog::Accepted(其值为1) 15 connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject()));//reject()槽函数关闭对话框,将对话框返回的结果变量设置为QDialog::Reject(其值为0) 16 } 17 18 void MyDialog::on_lineEdit_textChanged() 19 { 20 ui->okButton->setEnabled(ui->lineEdit->hasAcceptableInput()); 21 } 22 23 MyDialog::~MyDialog() 24 { 25 delete ui; 26 }
最后点击编译运行就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源文件:
1 #include <QtGui> 2 #include "sortdialog.h" 3 SortDialog::SortDialog(QWidget *parent):QDialog(parent) 4 { 5 setupUi(this); 6 secondaryGroupBox->hide(); 7 tertiaryGroupBox->hide(); 8 layout()->setSizeConstraint(QLayout::SetFixedSize); 9 setColumnRange('A', 'Z'); 10 } 11 void SortDialog::setColumnRange(QChar first, QChar last) 12 { 13 primaryColumnCombo->clear(); 14 secondaryColumnCombo->clear(); 15 tertiaryColumnCombo->clear(); 16 secondaryColumnCombo->addItem(tr("None")); 17 tertiaryColumnCombo->addItem(tr("None")); 18 primaryColumnCombo->setMinimumSize(secondaryColumnCombo->sizeHint()); 19 QChar ch = first; 20 while (ch <= last) { 21 primaryColumnCombo->addItem(QString(ch)); 22 secondaryColumnCombo->addItem(QString(ch)); 23 tertiaryColumnCombo->addItem(QString(ch)); 24 ch = ch.unicode() + 1; 25 } 26 }
在构造函数中,隐藏了secondaryGroupBox和tertiaryGroupBox群组框部分。并设置对话框的sizeConstraint的属性为QLayout::setFixedSize,这样用户不能随便改变对话框的大小。
下面是main.cpp文件:
编译运行这个程序,点击“更多”按钮,查看对话框的改变,结果应该与图1一样。另一种可以改变的对话框是多页对话框。这类对话框也可以用两种方式创建。相关的类有QTabWidget,QStackedWidget,QListWidget,QTreeWidget等。