QT创建对话框

2015/06/09 QT学习

本节内容主要介绍完全手写代码的方式创建第一个对话框。

子类话QDialog

完全使用C++编写一个Find(查找)对话框。创建文件finddialog.h和finddialog.cpp文件。首先是finddialog.h文件说起:

#ifndef FINDDIALOG_H
#define FINDDIALOG_H

#include <QDialog>

class QCheckBox;
class QLabel;
class QLineEidt;
class QPushButton;

第1、2行能够防止对这个头文件的多重包含。

第3行包含了QDialog的定义,它是QT中对话框的基类。QDialog从QWidget类中派生出来。

第4行到第7行前置声明了一些用于这个对话框实现的QT类。前置声明会告诉C++编译程序类的存在,而不用提供类定义中所有细节。

然后定义FindDialog,并让他成为QDialog的子类:

class FindDialog: public 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按钮时对话框所发射的两个信号。如果向前查询(search backward)选项生效,对话框就发射findPrevious()信号,否则它就发射findNext()信号。signals关键字实际上是一个宏,C++预处理器会在编译程序找到它之前把它转换成标准C++代码。

private slots:
	void findClicked();
	void enableFindButton(const QString &text);

private:
	QLabel *label;
	QLineEidt *lineEidt;
	QCheckBox *caseCheckBox;
	QCheckBox *backwardCheckBox;
	QPushButton *findButton;
	QPushButton *closeButton;

在这个类中private段中声明了两个槽,为了实现这两个槽,几乎需要访问这个对话框的所有子窗口部件,所以也保留了指向它们的指针。关键字slots也是一个宏

finddialog.cpp中包含了对FindDialog类的实现:

#include <QtGui>
#include "finddialog.h"

首先,需要包含,该头文件包含了Qt GUI的定义。Qt由数个模块构成,每个模块都有自己的类库。最为重要的模块有QtCore、QtGui、QtNetwork、QtOpenGL、QtSql、QtSvg和QtXml。其中,在头文件中为构成QtCore和QtGui组成部分的所有类进行了定义。

在finddialog.h文件中,**本可以简单地添加一个包含即可,而不用包含和使用QCeckBox、QLabel、QLineEdit和QPushButton的前置声明,但是,在一个头文件中在包含一个大的头文件着实不是一种好的编程风格**。

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")); //创建关闭按钮

第1行,把parent参数传递给了基类的构造函数,然后创建子窗口部件。

第9行创建了一个Find按钮,用户可在那些支持快捷键的平台下通过按下Alt+F快捷键来激活它。

符号“&”可以用来控制焦点:在第4行创建了一个带有快捷点(Alt + W)的标签,而在第6行设置了行编辑器作为标签的伙伴。所谓伙伴,就是一个窗口部件,它可以按下标签的快捷键时接收焦点。所以当用户按下Alt+W,也就是按下该标签的快捷键,焦点就会移动到这个行编辑器上。

第10行,通过调用setDefault(true)让Find按钮成为对话框的默认按钮。默认按钮就是当用户按下Enter键是能够按下对应的按钮

第11行,禁用了Find按钮。当禁用一个窗口部件时,它通常会显示为灰色,并且不能和用户发生交互操作

//当编辑框中文本发生改变时候,会触发槽函数enableFindButton()
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中继承而来,并且它的默认行为就是把窗口部件从用户视野中隐藏起来。

由于QObject是FindDialog的父对象之一,所以可以省略connect()函数前面的QObject::前缀

对话框中所用到的槽:

void finddialog::findClicked()
{
    QString text = lineEdit->text();
    Qt::CaseSensitivity cs =
            caseCheckBox->isChecked() ? Qt::CaseSensitive
                                      : Qt::CaseInsensitive;

    if(backwardCheckBox->isChecked()){   //search的CheckBox是否被勾选
        emit findPrevious(text, cs);
    } else{
        emit findNext(text, cs);
    }
}

void finddialog::enableFindButton(const QString &text)
{
    findButton->setEnabled(!text.isEmpty());
}

当用户单击Find按钮时,就会调用findClicked()槽。而该槽将会发射findPrevious()或findNext()信号,这取决于Search backward选项的取值。emit是Qt中的关键字,像其他Qt扩展一样,它也会被C++预处理器转换成标准的C++代码。

只要用户改变了行编辑器中的文本,就会调用enableFindButton()槽,如果行编辑器中有中文,该槽就会启用Find按钮,否则它就会禁用Find按钮。

//布局管理器
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);

布局中既可以包含多个窗口部件,也可包含其他子布局。对于Find对话框,使用了两个QHBoxLayout布局和两个QVBoxLayout布局。如下图为对话框的布局:

图右下角的小“小弹簧”是一个分隔符,或者称为“伸展器”,用它来占据Find按钮和Close按钮所余下的空白区域,这样可以确保这些按钮完全占用它们所在布局的上部空间

当将子布局对象添加到父布局对象中时,子布局对象就会自动重定义自己的父对象,也就是说,当将主布局装到对话框中去时,它就会成为对话框的子对象了,于是它的所有窗口部件会重定义自己的父对象,从而变成对话框的子对象。如图为父子层次关系的最终结果:

最后设置显示在对话框标题栏的标题内容,并让窗口具有一个固定的高度,QWidget::sizeHint()函数可以返回一个窗口部件所“理想”尺寸大小。

setWindowTitle(tr("Find"));
setFixedHeight(sizeHint().height());  //返回一个窗口部件所理想的尺寸大小

由于在创建这个对话框中的窗口部件和布局时使用的new,所以需要写一个能够调用delete的析构函数,以便可以删除所创建的每一个窗口部件和布局。但是这样做并不是必须的,因为QT会在删除父对象的时候自动删除其所属的所有子对象,也就会删除FindDialog中作为子孙的所有子窗口部件和子布局

完整代码

finddialog.h:

#ifndef FINDDIALOG_H
#define FINDDIALOG_H

#include <qdialog.h>

class QCheckBox;
class QLabel;
class QLineEdit;
class QPushButton;   //前置声明告诉C++编译程序类的存在

class finddialog : public QDialog
{
	Q_OBJECT        //对于定义了信号和槽的类,在类定义开始处的Q_OBJECT宏是必须

public:
	finddialog(QWidget *parent = 0);  //类的构造函数,parent指定了父窗口部件
	~finddialog();                    //类的析构函数

signals:
	//单击Find按钮时对话框发射两个信号,当search backward生效时
	//发射findPrevious()信号,否则发射findNext信号
	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; //check选择框
	QCheckBox *backwardCheckBox;
	QPushButton *findButton; //find按钮
	QPushButton *closeButton;//关闭按钮
};

#endif // FINDDIALOG_H

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 关闭")); //创建关闭按钮
	
	//当编辑框中文本发生改变时候,会触发槽函数enableFindButton()
	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);

	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 "finddialog.h"
#include <QTextCodec>
#include <QtGui/QApplication>

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	QTextCodec::setCodecForTr(QTextCodec::codecForLocale());  //设置中文字体
	finddialog *dialog = new finddialog;
	dialog->show();
	return a.exec();
}

程序运行结果

Search

    Post Directory