Fork me on GitHub

C++GUI Programming with QT学习笔记day02


创建对话框

派生对话框类

第一个例子是一个用 C++实现的查找对话框。我们把这个对话框实现为一个类,这样
它就是一个独立的控件,并有自己的信号(signal)和 slot 函数
类的源代码分别放在 finddialog.h 和 finddialog.cpp 中。

首先看 finddialog.h 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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

一共 27 行,第 1,2,27 行是为了避免头文件被多次包含。

  1. 第 3 行包含 QDialog 头文件,这个类从 QDialog 继承,QDialog 从 QWidget 继承。

  2. 第 10 行,Q_OBJECT 是一个宏定义,如果类里面用到了 signal 或者 slots,就要声
    明这个宏。

  3. 第 12 行, FindDialog(QWidget *parent = 0);构造函数是 Qt 控件类的标准格式,
    默认的父参数为 NULL,说明没有父控件。

  4. 第 13 行,signal 声明了这个对话框发出的两个信号,如果选择向前查找,那么对话框
    就发出 findPrevious()信号,否则,发出 findNext()信号。signal 也是一个宏,在编译之
    前,C++预处理把它变成标准的 c++代码。Qt::CaseSensitivity 是一个枚举类型,有
    Qt::CaseSensitive 和 Qt::CaseInsensitive 两个值。

  5. 在类的私有部分,声明有两个 slot 函数。为了实现这两个函数,需要用到对话框的其
    他控件的信息,所以保存了一些控件的指针。slot 关键字和 signal 一样,也是一个宏。
    对于私有成员变量,我们只是使用了它们的指针,没有对它们进行存取操作,编译器
    不需要知道它们的详细定义,所以只使用了这些类的前向声明。

下面看一下 finddialog.cpp 源文件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#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();
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());
}
  1. 第四行,把parent参数传递给了基类的构造函数。然后创建子窗口部件。
  2. 使用tr()函数来包含字符串是一个很不错的习惯。在这些字符串中,使用了表示“与”操作的符号”&”来
    表示快捷键。例如,在第11行创建了一个Find按钮,用户可在哪些支持快捷键的平台下通过 Alt+F 来激活它。
  3. 在第8行设置了行编辑器作为标签的伙伴。所谓”伙伴”就是一个窗口部件,它可以在按下标签的快捷键时
    接收焦点。所以当用户按下该标签的快捷键时,焦点就会移动到这个行编辑器上。
  4. 布局中既可以包含多个窗口部件,也可以包含其他子布局

当用户点击 findButton 按钮,findClicked()就会调用,根据 backwardCheckBox
状态,他发出 findPrevious()或者 findNext()信号。emit 也是一个 Qt 的宏。
当用户改变 lineEdit 中的文本, enableFindButton()slot 函数就会调用。如果输入了文本,
那么让 findButton 有效,否则就无效。

深入信号和槽

在目前有的例子中,我们已经连接了不同的信号和槽。实际使用中还要考虑如下一些
规则:

  1. 一个信号可以连接到多个槽:
    connect(slider, SIGNAL(valueChanged(int)),spinBox, SLOT(setValue(int)));
    connect(slider, SIGNAL(valueChanged(int)),this,
    SLOT(updateStatusBarIndicator(int)));
    当信号发出后,槽函数都会被调用,但是调用的顺序是随机的,不确定的。
  2. 多个信号可以连接到一个槽
    connect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));
    connect(calculator, SIGNAL(divisionByZero()),this, SLOT(handleMathError()));
    任何一个信号发出,槽函数都会执行。
  3. 一个信号可以和另一个信号相连
    connect(lineEdit, SIGNAL(textChanged(const QString &)),
    this, SIGNAL(updateRecord(const QString &)));
    第一个信号发出后,第二个信号也同时发送。除此之外,信号与信号连接上和信号和槽连
    接相同。
  4. 连接可以被删除
    disconnect(lcd, SIGNAL(overflow()),this, SLOT(handleMathError()));
    这个函数很少使用,一个对象删除后,Qt 自动删除这个对象的所有连接。

信号和槽函数必须有着相同的参数类型,这样信号和槽函数才能成功连接:

1
2
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),this,
SLOT(processReply(int, const QString &)));

如果信号里的参数个数多于槽函数的参数,多余的参数被忽略:

1
2
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),this,
SLOT(checkErrorCode(int)));

如果参速类型不匹配,或者信号和槽不存在,在 debug 状态时,Qt 会在运行期间给出
警告。如果信号和槽连接时包含了参数的名字,Qt 将会给出警告。

以前我们列举的例子中都是控件的信号和槽。但是信号和槽机制在 QObject 中就实现了,
可以实现在任何从 QObject 继承的子类中。


坚持原创技术分享,您的支持将鼓励我继续创作
-------------本文结束感谢您的阅读-------------
0%