1、子类化QDialog
第一个例子是完全使用C++编写的Find对话框。
finddialog.h:
1 #ifndef FINDDIALOG_H 2 #define FINDDIALOG_H 3 4 #include <QDialog> 5 6 class QCheckBox; 7 class QLabel; 8 class QLineEdit; 9 class QPushButton; 10 11 class FindDialog : public QDialog 12 { 13 Q_OBJECT 14 public: 15 FindDialog(QWidget *parent = 0); 16 signals: 17 void findNext(const QString &str, Qt::CaseSensitivity cs); 18 void findPrevious(const QString &str, Qt::CaseSensitivity cs); 19 private slots: 20 void findClicked(); 21 void enableFindButton(const QString &text); 22 private: 23 QLabel *label; 24 QLineEdit *lineEdit; 25 QCheckBox *caseCheckBox; 26 QCheckBox *backwardCheckBox; 27 QPushButton *findButton; 28 QPushButton *closeButton; 29 }; 30 31 #endif // FINDDIALOG_H
头部的预编译指令是为了防止对这个头文件的多重包含
对于定义了信号与槽的类来说在类定义的开始处的Q_OBJECT宏都是必须的,它给出了一些函数的声明
FindDialog类的构造函数是典型的Qt窗口部件类的定义方式。parent参数指定了它的父窗口部件。该参数的默认值是一个空指针,意味着该对话框没有父对象。
signals关键字实际上是一个宏,C++预处理器会在编译程序找到它之前把它转换成标准C++代码。我的疑问是为什么槽的声明是私有的。
finddialog.cpp:
1 #include <QtGui> 2 #include <QLabel> 3 #include <QCheckBox> 4 #include <QLineEdit> 5 #include <QPushButton> 6 #include <QHBoxLayout> 7 #include <QVBoxLayout> 8 #include "finddialog.h" 9 10 FindDialog::FindDialog(QWidget *parent) 11 :QDialog(parent) 12 { 13 label = new QLabel(tr("Find &what:")); 14 lineEdit = new QLineEdit; 15 label->setBuddy(lineEdit); 16 17 caseCheckBox = new QCheckBox(tr("Match &case")); 18 backwardCheckBox = new QCheckBox(tr("Search &backward")); 19 20 findButton = new QPushButton(tr("&Find")); 21 findButton->setDefault(true); 22 findButton->setEnabled(false); 23 24 closeButton = new QPushButton("Close"); 25 connect(lineEdit, SIGNAL(textChanged(const QString &)), 26 this, SLOT(enableFindButton(const QString &))); 27 connect(findButton, SIGNAL(clicked()), 28 this, SLOT(findClicked())); 29 connect(closeButton, SIGNAL(clicked()), 30 this, SLOT(close())); 31 32 QHBoxLayout *topLeftLayout = new QHBoxLayout; 33 topLeftLayout->addWidget(label); 34 topLeftLayout->addWidget(lineEdit); 35 36 QVBoxLayout *leftLayout = new QVBoxLayout; 37 leftLayout->addLayout(topLeftLayout); 38 leftLayout->addWidget(caseCheckBox); 39 leftLayout->addWidget(backwardCheckBox); 40 41 QVBoxLayout *rightLayout = new QVBoxLayout; 42 rightLayout->addWidget(findButton); 43 rightLayout->addWidget(closeButton); 44 rightLayout->addStretch(); 45 46 QHBoxLayout *mainLayout = new QHBoxLayout; 47 mainLayout->addLayout(leftLayout); 48 mainLayout->addLayout(rightLayout); 49 setLayout(mainLayout); 50 51 setWindowTitle(tr("Find")); 52 setFixedHeight(sizeHint().height()); 53 } 54 55 void FindDialog::findClicked() 56 { 57 QString text = lineEdit->text(); 58 Qt::CaseSensitivity cs = 59 caseCheckBox->isChecked() ? Qt::CaseSensitive 60 : Qt::CaseInsensitive; 61 if(backwardCheckBox->isChecked()) 62 { 63 emit findPrevious(text, cs); 64 } 65 else 66 { 67 emit findNext(text, cs); 68 } 69 } 70 71 void FindDialog::enableFindButton(const QString &text) 72 { 73 findButton->setEnabled(!text.isEmpty()); 74 }
在字符串周围的tr函数调用时把它们翻译成其他语言的标记,即使现在没有需要将程序翻译成其它语言,但是这样做也是有好处的。
在部件的text字符创中使用了符号“&”表示快捷键,使用alt+“&”后的第一个字母可以实现快捷的选中该部件。
所谓buddy,就是一个窗口部件,它可以再按下标签的快捷键时接受焦点输入。
setDefault让Find按钮称为默认按钮,默认按钮即当用户按下回车时能够按下对应的键。
omit是Qt中的关键字,它会被C++预处理器转换成标准C++代码。
main.cpp:
1 #include <QApplication> 2 #include "finddialog.h" 3 4 int main(int argc, char** argv) 5 { 6 QApplication app(argc, argv); 7 FindDialog *dialog = new FindDialog; 8 dialog->show(); 9 return app.exec(); 10 }
运行效果:
2、深入介绍槽与信号
信号与槽是Qt编程的基础。它可以让编程人员将这些互不了解的对象绑定在一起。
槽和普通的C++函数几乎是一样的,可以使虚函数、可以被重载、可以使公有的、保护的、私有的。并且也可以被其他C++成员函数直接调用,还有它们的参数可以使任意类型,唯一不同的是:槽还可以和信号连接在一起,在这种情况下,每当发射这个信号的时候,就会自动调用这个槽。
connect(sender, SIGNAL(signal), receiver,SLOT(slot));
这里的sender和receiver是指向QObject的指针,signal和slot是不带参数的函数名。实际上,SIGNAL和SLOT宏会把它们的参数转换成相应的字符串。
- 一个信号可以连接多个槽
在发射这个信号的时候会以不确定的顺序一个接一个的调用这些槽
- 多个信号可以连接同一个槽
无论发射哪一个信号都会调用这个槽
- 一个信号可以与另外一个信号连接
当发射第一个信号的时候也会发射第二个信号
- 连接可以被移除
这种情况很少用
注意:要把信号连接到槽,它们的参数必须具有相同的顺序和相同的类型。例外:如果信号比连接的槽的参数多,那么后面多余的参数将被忽略。
3、快速设计对话框
这一节会使用Qt Designer来可视化的设计一个对话框。
操作步骤就是使用Qt Designer来设计需要的对话框,然后保存为.ui的格式。
在编译的时候会将.ui格式的文件转换为C++并且存储在ui_name.h和ui_name.cpp中。
生成的类没有任何基类,当在程序中使用它时,可以创建一个QDialog类的对象,然后把它传递给setupUi()函数。
现在定义一个新的类继承自QDialog和Ui::GoToCellDialog类(我填写的Ui文件生成的类名)。
gotocelldialog.h:
1 #ifndef GOTOCELLDIALOG_H 2 #define GOTOCELLDIALOG_H 3 4 #include <QDialog> 5 #include "ui_gocelldialog.h" 6 7 class GotoCellDialog : public QDialog, public Ui::GoToCellDialog 8 { 9 Q_OBJECT 10 public: 11 GotoCellDialog(QWidget *parent = 0); 12 private slots: 13 void on_lineEdit_textChanged(); 14 }; 15 16 #endif // GOTOCELLDIALOG_H
该类继承自两个类:QDialog、Ui::GoToCellDialog
gotocelldialog.cpp:
1 #include <QtGui> 2 #include "gotocelldialog.h" 3 4 GotoCellDialog::GotoCellDialog(QWidget *parent) 5 : QDialog(parent) 6 { 7 setupUi(this); 8 9 // QRegExp regExp("[A-Za-z][1-9][0-9]{0, 2}"); 10 QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}"); 11 lineEdit->setValidator(new QRegExpValidator(regExp, this)); 12 13 connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); 14 connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); 15 } 16 17 void GotoCellDialog::on_lineEdit_textChanged() 18 { 19 okButton->setEnabled(lineEdit->hasAcceptableInput()); 20 }
实现文件中绑定了需要绑定的信号与槽。
对编辑框的输入进行了验证,使用正则表达式的方法来验证。
accept()槽可以讲对话框返回的结果变量设置为QDialog::Accepted,其值为1、而reject()槽会把对话框的值设置为QDialog::Rejected,其值为0。可以根据对话框的返回值来判断是否点击了OK按钮。
main.cpp:
1 #include <QApplication> 2 3 #include "gotocelldialog.h" 4 5 int main(int argc, char *argv[]) 6 { 7 QApplication a(argc, argv); 8 9 GotoCellDialog *dialog = new GotoCellDialog; 10 dialog->show(); 11 12 return a.exec(); 13 }
实例化一个该类的对象并显示出来。
使用QDialogButtonBox来制作这个对话框,以使它在MAC平台上显得更加圆润。