实验内容:使用C++编写一个Find(查找)对话框。
源代码分别保存在finddialog.h和finddialog.cpp文件中。首先finddialog.h说起。
#ifndef FINDDIALOG_H
#define FINDDIALOG_H
#include <QDialog>
class QCheckBox;
class QLabel;
class QLineEdit;
class QPushButton;
class FindDialog : public QDialog
{
Q_OBJECT
public:
FindDialog(QWidget *parent=0);
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
#ifndef FINDDIALOG_H
#define FINDDIALOG_H 防止对这个头文件的多重包含。
#include <QDialog>他是Qt中对话框的基类。从QWidget类中派生出来。
class QCheckBox;
class QLabel;
class QLineEdit;
class QPushButton;声明一些用于这个对话框实现中的Qt类。前置声明会告诉C++程序类的存在,而不用提供类定义的所有细节。
class FindDialog : public QDialog定义FindDialog,并让他成为QDialog的子类:对于所有定义了信号和槽的类。
Q_OBJECT 在类定义开始处的是必要的。
public:
FindDialog(QWidget *parent=0);
FindDialog的构造函数就是一个典型的Qt窗口部件类的定义方式。parent参数指定了他的父窗口部件。该参数的默认值是一个空指针,意味着对话框没有父对象。
signals:
void findNext(const QString &str,Qt::CaseSensitivity cs);
void findPrevious(const QString &str,Qt::CaseSensitivity cs);
signals部分声明了当用户单击Find按钮时对话框所发射的两个信号。如果向前查询选项生效,对话框就发射findPrevious()信号,否则发射findNext信号。signals关键字实际上是一个宏。C++预处理器会在编译程序找到他之前把他之前转化成标准的C++程序。Qt::CaseSensitivity是一个枚举类型,他有两个值:Qt::CaseSensitive和Qt::CaseInsensitive两个取值。
private slots:
void findClicked();
void enableFindButton(const QString &text);
在这个类的private段声明了两个槽。为了实现这两个槽,几乎需要访问这个对话框的所有子窗口部件,所以保留了指向他们的指针。关键slots就像signals一样也是一个宏,也可以拓展成C++编译程序可以处理的一种结构形式。
private:
QLabel *label;
QLineEdit *lineEdit;
QCheckBox *caseCheckBox;
QCheckBox *backwardCheckBox;
QPushButton *findButton;
QPushButton *closeButton;
对于这些私有变量,我们使用了他们的类前置声明。这是可行的,因为他们都是指针,而且没有必要再头文件中就去访问他们,因而编译程序就无须这些类的完整定义。我们没有包含与这几个类相关的头文件,而是使用了一些前置声明,这样编译更快。
现在看看finddialog.cpp,其中包含对finddialog.cpp类的实现。
#include <QtGui>
#include "finddialog.h"
FindDialog::FindDialog(QWidget *parent)
:QDialog(parent)
{
label=new QLabel(tr("Find &what:"));
lineEdit=new QLineEdit;
label->setBuddy(lineEdit);
caseCheckBox=new QCheckBox(tr("Match &case"));
backwardCheckBox=new QCheckBox(tr("Search &backward"));
findButton=new QPushButton(tr("&Find"));
findButton->setDefault(true);
findButton->setEnabled(false);
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();
QVBoxLayout *mainLayout=new QVBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
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());
}
#include <QtGui>
#include "finddialog.h"
<QtGui>包含了Qt GUI类的定义。Qt由数个模块构成,每个模块都有自己的类库。最为重要的模块有:QtCore、QtGui、QtNetwork、QtOpenGL、QtScript、QtSql、QtSvg和QtXml。其中<QtGui>包含QtCore、QtGui、。
FindDialog::FindDialog(QWidget *parent)
:QDialog(parent)
把parent参数传递给了基类的构造函数,
label=new QLabel(tr("Find &what:"));
lineEdit=new QLineEdit;
label->setBuddy(lineEdit);
caseCheckBox=new QCheckBox(tr("Match &case"));
backwardCheckBox=new QCheckBox(tr("Search &backward"));
findButton=new QPushButton(tr("&Find"));
findButton->setDefault(true);
findButton->setEnabled(false);
closeButton=new QPushButton(tr("Close"));
创建了子窗口部件。在字符串周围的tr函数调用是把她们翻译成其他的语言标志。在每一个QObject对象以及包含有Q_OBJECT宏的子类中都有这个函数的声明。尽管也许并没有将你的应用程序立即翻译成其他语言的打算,但是在每一个用户可见的字符串周围使用tr函数还是一个不错的习惯。
在这些字符串中,使用了表示“与”操作的符号“&”来表示快捷键。findButton=new QPushButton(tr("&Find"));使用Alt+F实现
findButton->setDefault(true);让find按钮成为对话框的默认钮。就是当用户按下Enter键是能够按下对应的按钮。
findButton->setEnabled(false);禁用了find按钮。当禁用一个窗口部件时,他通常会显示为灰色,并且不能和用户发生交互操作。
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()));
只要编辑器中的文本发生变化,就会用私有槽enableFindButton(const QString &)。当用户单击find按钮时,会调用findClicked()私有槽。而当用户单击close时,对话框会关闭。close()槽是从QWidget中继承而来的,并且他的默认行为时吧窗口部件从用户的视野中隐藏起来(而无须将其删除)。稍后会看到enableFindButton和
findClicked()槽的代码。
由于QObject是FindDialog的父对象之一,所以可以省略connect()函数前面的QObject::前缀。
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();
QVBoxLayout *mainLayout=new QVBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
接下来使用布局管理器摆放这些子窗口部件。布局中既可以包含多个窗口部件,也可以包含其他字布局。通过 QHBoxLayout、QVBoxLayout、QGridLayout可以构建出相当复杂的对话框。
对于Find对话框,使用两个QHBoxLayout和两个QVBoxLayout布局。外面的布局是主布局,通过第35行代码将其安装在FindDialog中,并且由其负责对话框的整个区域。其他三个布局作为子布局。
rightLayout->addStretch();是一个小弹簧,是一个分隔符,用它来占据find按钮和close按钮所余下的空白区域,这也能个可以确保这些按钮完全占用他们所在布局的上部空间。
布局管理器类的一个精妙之处在于他们他们不是窗口部件。相反他们派生自Qlayout,因而是进一步派生自QObject。窗口部件用实现轮廓来表示,布局用点线来表示,这样就能够很好的区分窗口部件和布局。在一个运行的应用程序中,布局是不可见的。
当将子布局添加副布局对象时(leftLayout->addLayout(topLeftLayout);、 leftLayout->addLayout(topLeftLayout);、mainLayout->addLayout(leftLayout);、mainLayout->addLayout(rightLayout);)子布局对象就会自动重定义自己的父对象。也就是说,当将主布局装到对话框中去时( setLayout(mainLayout);),他就会成为对话框的子对象,于是他的所有子窗口部件都会重定义自己的父对象。
setWindowTitle(tr("Find"));
setFixedHeight(sizeHint().height());最后,设置了显示在对话框标题栏上的标题内容,并让窗口具有一个固定的高度,这是因为在对话框的垂直方向上再没有其他窗口部件可以去占用所多出的空间。QWidget::sizeHint()函数可以返回一个窗口部件所“理想”的尺寸大小。
这样,就完成了对Finddialog对话框的构造函数的分析。由于在构建这个对话框中窗口部件和布局时使用的new,所以需要写一个能够调用delete的析构函数,以便可以删除所创建的每一个窗口部件和布局。但是这样做并不是必需的,因为Qt会在删除父对象的时候自动删除所属的所有子对象,也就会删除FindDialog中作为其子孙的所有窗口部件和子布局。
现在来看看这个对话框所用到的槽。
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());
}
当用户单击按钮时,就会调用findClicked()槽,而该槽会发射
findPrevious和findNext信号,这取决于backwardCheckBox的选项取值,emit在Qt中是关键字,想其他Qt拓展一样,他也会变成C++编译成标准C++。
只要用花改变行编辑器中的文本,就会调用enableFindButton()槽。如果在行编辑器中文本,该槽就会启用find按钮,否则他就会禁用Find按钮。
利用两个槽就完成了这个对话框的功能。
最终创建main函数,创建一个main.cpp测试这个finddialog窗口部件。
#include <QtGui/QApplication>
#include "finddialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FindDialog *dialog=new FindDialog;
dialog->show();
return a.exec();
}
现在运行程序,如果在你的系统上能够显示快捷键,可以检验一下快捷键。这样一个子对话框就完成了。
文章评论(0条评论)
登录后参与讨论