C++ Qt4 编程学习笔记(三)——对话框
来源:互联网 发布:淘宝女士手提包 编辑:程序博客网 时间:2024/06/14 06:11
声明:在笔记(一)里面我已经说啦,这书是完全跟随C++ GUI Qt4 编程(第二版)一书的,而且我非常推荐喜欢Qt的人去买这本书,的确是学习Qt的好书,这些笔记也的确是个人的复习笔记,用于将来遗忘的时候快速补课。我可能会对书中讲述的内容做一些整合或者更改,但是一切都是未定义的,切记……
1.关于对话框
如图,这就是一个对话框:
在我没有保存WPS文字的情况下,点击了关闭按钮,系统就会弹出这样一个提示。对话框提供了一种用户与程序的交互方式,结构简单,目的明确,这一节,我们就要做类似的这样一个对话框。
不过呢,我还是得先说,这节的代码开始很长咯,如果是学习的话,还是推荐自己手打代码了。还有就是你一定得要坚持,坚持到我告诉你其实这些代码都可以不用写,嘿嘿,为什么?现在不告诉你(⊙o⊙)…
2.手工代码实现
首先来看看我们要做的对话框是什么样子的:
嘿嘿,就这样,其实就是我们的查找对话框啦,这个对话框有什么特别的呢?
首先,在Find what栏里面如果没有输入,那查找按钮就为不可用(灰色,不能点击),如果有输入,则Find可用(如图)。然后呢,对话框的竖直方向尺寸不可更改。
好的,大概就是这样子,这一次我们的源文件分为3部分,第一部分(main.cpp):
#include <QApplication>#include "finddialog.h" // 我们将要自己定义的类(就是对话框啦)的头文件int main(int argc, char *argv[]){ QApplication app(argc, argv); FindDialog *dialog = new FindDialog; dialog->show(); return app.exec();}
呵呵,还是那么简单,只不过这一次创建的对象是我们自己的类了,这没什么说的,接下来是第二部分(finddialog.h):
#ifndef FINDDIALOG_H // 防止二次包含#define FINDDIALOG_H#include <QDialog> // 对话框的头文件class QCheckBox; // 复选框class QLabel;class QLineEdit; // 行编辑框class QPushButton;/** 这里要说明一下,这样的前置声明可以让头文件不必包含相应的头文件,以提高编译速度* 通常这些都包含在实现文件中*/class FindDialog : public QDialog //继承自QDialog,因此,我们的类就拥有QDialog的默认属性了{ Q_OBJECT // 如果类中定义了信号和槽,那么这样的宏是必须的,以后会说明public: FindDialog(QWidget *parent = 0); // 典型Qt窗口部件类的构造函数,参数指定了父窗口部件,0则是没有signals: // C++扩展,这其实是一个宏,表明了我们这个对话框可以发送的信号,下面即是信号的定义 void findNext(const QString &str, Qt::CaseSensitivity cs); // CaseSensistivity是个枚举类型 //用来说明是否大小写敏感 void findPrevious(const QString &str, Qt::CaseSensitivity cs); // QString是Qt的字符串类型private slots: // 这就是我们的类支持的信号操作,即槽函数,槽函数必须定义 void findClicked(); // find按钮被按下 void enableFindButton(const QString &text); // 在text不为空的情况下让Find可用private: // 不应该被直接访问的成员,可以大概注意一下这些变量(对象)对应于窗口中的哪些部件 QLabel *label; QLineEdit *lineEdit; QCheckBox *caseCheckBox; QCheckBox *backwardCheckBox; QPushButton *findButton; QPushButton *closeButton;};#endif
从这个头文件中,我们应该大致了解这个对话框类的构成,包括信号和槽,下面我们看看这样的类如何实现:
#include <QtGui> // 这就是Qt的GUI库,我们用到的Dialog,CheckBox等等都在里面啦 // 不过这个头文件因为太大了,所以一般是不会直接包含的,用什么包含什么了#include "finddialog.h"FindDialog::FindDialog(QWidget *parent) // 构造函数实现(头文件中已经制定默认值为0) : QDialog(parent) // 对父类成员初始化{ label = new QLabel(tr("Find &what:")); // 初始化成员变量(就是把每个组件给定义出来),tr是翻译标记,以后会详细介绍 lineEdit = new QLineEdit; label->setBuddy(lineEdit); // 绑定好基友,大家一起走(⊙o⊙)…这样当焦点被指向label时,lineEdit就会被选定 caseCheckBox = new QCheckBox(tr("Match &case")); // 字符前面的&让c成为这个选项的快捷键(ALT+C) backwardCheckBox = new QCheckBox(tr("Search &backward")); findButton = new QPushButton(tr("&Find")); findButton->setDefault(true); // 这样让Find按钮成为对话框的默认按钮 findButton->setEnabled(false); // 初始化Find按钮不可用,也就是编程灰色了 closeButton = new QPushButton(tr("Close")); //QObject是FindDialog的父类对象,因此可以不用写前缀 connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(enableFindButton(const QString &))); // 当lineEdit的值发生改变,调用我们自己的槽函数enableFindButton connect(findButton, SIGNAL(clicked()), this, SLOT(findClicked())); // (⊙o⊙)… connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); // close()这个槽是本身就有的,可以F1一下 QHBoxLayout *topLeftLayout = new QHBoxLayout; // 好了,下面看看我们强力的布局是怎么完成的 topLeftLayout->addWidget(label); // 首先这是水平布局,把一堆好基友放平了 topLeftLayout->addWidget(lineEdit); QVBoxLayout *leftLayout = new QVBoxLayout; // 然后要把下面两个复选框和上面的好基友竖直对齐 leftLayout->addLayout(topLeftLayout); // 这里注意是addLayout,就是这样布局才可以不断嵌套 leftLayout->addWidget(caseCheckBox); // 看这一部分的时候最好对着上面的完成图来看 leftLayout->addWidget(backwardCheckBox); QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->addWidget(findButton); rightLayout->addWidget(closeButton); rightLayout->addStretch(); // 这里注意啦,addStretch可以理解为一个伸缩占位空白,用来把它旁边的按钮给挤紧了,这里当然是竖直方向了 QHBoxLayout *mainLayout = new QHBoxLayout; // 好的,把左边的模块和右边的模块在做一次水平对齐 mainLayout->addLayout(leftLayout); mainLayout->addLayout(rightLayout); setLayout(mainLayout); // ... setWindowTitle(tr("Find")); // 对话框的标题 setFixedHeight(sizeHint().height()); // 嘿嘿,这里就是让高度方向尺寸不可变,sizeHint()返回窗口合理的最小尺寸}void FindDialog::findClicked(){ QString text = lineEdit->text(); // 即是lineEdit里面的值 // 让cs得到是否大小写敏感的标识信息 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()); // 大牛都这么写(⊙o⊙)…}
小窍门:嘿嘿,这个小窍门出现在你头晕脑胀的时候呢,其实我们在读程序的时候不应该一行一行的读,这样的话,你对整个程序的框架可以说一无所知,所以我们在看一个东西是首先在东东的头文件(声明)里面就应该大概把这些东西要做什么事给明确了,怎么明确呢?其实就是函数名啦,比如enableFindButton从文字意思就可以看出来是让Find按钮可用的功能,这样你对整个程序的框架比较了解了,看到具体的实现也就不会觉得实现者的想法太奇特了,自己根本想不到(可以看到好的名字是多么的重要,Qt中的名字都是比较合理的,一般都能望文生义(⊙o⊙)…)。看这些源码后面的注释也是一样,你应该对整个框架了解之后再来仔细看细节的实现。
运行一下你的程序,看看有什么问题没?
好了,运行成功后,我们再来看一下看一下上面可能让你头晕的部分,一个是关于tr标记,一个就是信号与槽了,这一节我们先更深入的说说信号与槽了。
3.信号与槽
本来我说应该把这个单独放在一节的,一想,不对……这东西其实也没多少东西可以说的,不信?来瞧瞧!
首先我们在来说一下信号与槽的作用,看看我们之前做的按钮:
还是那么漂亮呢,不过这里我们得关心一个问题,为什么我按这个按钮程序就退出了?
其实这就是信号跟槽的作用啦,按钮在设计的时候就有一个叫clicked()的信号,如果我们点击了这个按钮,那他就会发出这个信号(即是用emit发送信号),告诉我们这个东西被点了。但是这个信号发送了,你总得做出响应吧,好的,那槽函数就是用来接收信号,然后执行操作的,这里呢,就是应用程序管理类(QApplication)的close()了。
还有个问题:这信号发送到哪儿呢,我怎么知道发送给谁呢?这个事情其实就交给QObject类(事实上,绝大部分Qt类都是继承自QObject类,也包括布局类)connect静态函数来做了,connect函数把,一个类中的信号与一个类中的槽连接起来,就相当于拿一条线给连起来了,一边的信号发送了,另一边的槽函数就跟着动作了。
好了,下面来说说怎么来使用信号与槽的机制:
1.信号的声明,在类中作为信号的函数应该在前面添加signals标号,如:
signals:
void findClicked (void); // 返回值应该为void;
注意:信号无需被实现;
2.槽的声明,在类中作为槽的函数应该在权限标号(private, public等)后添加slots,但是事实上,槽函数跟普通的函数几乎一模一样,因此就算没有标号也不会有问题,但是这样的代码就难以明白了。槽函数声明如下:
private slots:
void enableFindButton (QString &str); // 跟普通函数一模一样
3.发送信号,在任何时候需要发送信号时,只需要使用你的信号函数(前提是类中已有声明),并在前面加上emit关键字(宏),用以表示提交消息,如:
emit findClicked();
4.连接信号,将一个类的信号与另一个类的槽相连接,如:
QObject::connect ( pClass1, SIGNAL(...), pClass2, SLOT(...));
值得注意的是,如果你的对象继承于QObject,那这样的前缀可以省略;
5.所有使用信号与槽的类,都必须在类的声明开始添加Q_OBJECT宏(并无分号);
好的,下面来说说信号与槽之间的某些细节:
1.一个信号可以连接多个槽,即是说你可以在多个connect函数中使用相同的信号。
2.一个槽可以与多个信号相关联,同上。
3.信号可以与信号相连接,那么在connect左端信号发送时,右端信号也被发送。
4.连接可以被移除,但通常没有必要:
disconnect( pClass1, SINGAL(...), pClass2, SLOT(...));
5.在同一个connect中的信号与槽必须拥有相同的参数类型表,而且不能指定参数(变量名);
好了,这就是信号与槽的全部了。
4.Qt扩展简介
信号与槽的扩展可以说是Qt最重要的特征之一了,不过这并不是Qt在语言上的扩展,最终,Qt的代码都会被编译为标准的C++代码,然而这些是怎么实现的呢?其实这是用一个用标准C++实现的moc模块来做的,如果你用命令行编译Qt的代码,那你应该很容易就见到它了,然而理解这些事实上对Qt编程的影响并不大,因此我们完全可以忽略这一部分,写出优秀的Qt代码,如果你对此有兴趣,可以查阅相关资料,这里就不赘述了。
这一节真累,写得累,看着也累(⊙o⊙)…
- C++ Qt4 编程学习笔记(三)——对话框
- C++ GUI Qt4学习笔记(三)
- C++ GUI Qt4学习笔记(二) 创建对话框
- 学习笔记之Qt4内建对话框
- C++ Qt4 编程学习笔记(一)——Hello,Qt!
- C++ Qt4 编程学习笔记(二)——Qt入门
- C++.QT编程学习笔记——点击一个按钮后出现选择文件对话框
- Qt编程—学习笔记——Qt4到Qt5兼容
- C专家编程学习笔记——第三章(三):有效地解读C语言的声明
- MFC学习笔记(三)——文本编程
- windows驱动编程学习笔记——(三)IRP
- Cuda学习笔记(三)——Cuda编程Tips
- OpenCV学习笔记(三)—— OpenCV编程起点
- 多线程编程学习笔记——线程同步(三)
- Shader编程学习笔记(三)—— 三大主流编程语言 HLSL/GLSL/Cg
- Shader编程学习笔记(三)—— 三大主流编程语言 HLSL/GLSL/Cg
- Shader编程学习笔记(三)—— 三大主流编程语言 HLSL/GLSL/Cg
- Shader编程学习笔记(三)—— 三大主流编程语言 HLSL/GLSL/Cg
- oracle备份常用命令
- C/C++中嵌入Python
- 网关 DNS DHCP 路由
- 最少硬币问题(动态规划解决)
- POJ1014解题报告
- C++ Qt4 编程学习笔记(三)——对话框
- viplugin---XX方法
- 黑马程序员_多线程
- Android 在xml布局配置文件中给Button按钮添加事件
- Objective-c的@property 详解(转载)
- 命令行操作笔记
- The processing instruction target matching "[xX][mM][lL]" is not allowed.
- 第10周实验报告1
- 柔性数组