大家好,又见面了,我是你们的朋友全栈君。
总结: 本文先通过一个例子介绍了Qt项目的大致组成,即其一个简单的项目框架,如何定义窗口类,绑定信号和槽,然后初始化窗口界面,显示窗口界面,以及将程序的控制权交给Qt库。
然后主要对Qt中的信号与槽机制、Qt 元对象系统、布局管理器的概念进行分析。
---------------------------------------------
1. 下载 Qt5
http://download.qt.io/official_releases/qt/5.8/5.8.0/
2. 新建Qt 项目
具体过程见我的另一篇随笔,如何新建QT项目。
有两种创建图形化程序界面的方法,一、通过系统自动生成的.ui文件,二、通过代码实现。
方法一可通过鼠标拖拉布局界面,较方便,但在设计较复杂的界面时最好还是使用Qt提供的布局管理器。
本文的例子是采用代码实现计算圆面积的界面程序,便于展示信号与槽的通信机制以及整个程序的框架。
3. 计算圆面积的程序代码实例
整个程序的架构图如下图
程序运行界面:
4、QT项目框架分析
4.1 每一个工程都有一个执行的入口函数,此项目中的main.cpp中的 main()函数就是此工程的入口。
main.cpp
代码语言:javascript复制#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
如上所示,其中:
1) #include “dialog.h” 包含了程序中要完成功能的Dialog类的定义,在Dialog类中封装完成所需要的功能。
注意:程序中使用哪个类,就要引用包含该类定义的头文件。如 dialog.h中使用QLabel、QLineEdit、QPushButton必须包含头文件
#include <QLabel> #include <QLineEdit> #include <QPushButton>
2) #include <QApplication>:Application类的定义。
3) int main(int argc, char *argv[]): 应用程序的入口,几乎在所有使用Qt的情况下,main()函数只需要在将控制权交给Qt库之前执行初始化,然后Qt 库通过事件向程序告知用户的行为。所有的Qt程序中都 必须有且只有一个main()函数。
4) QApplication a(argc, argv): a 是这个程序的 QApplication对象。
在每一个使用Qt图形化应用程序中都必须使用一个QApplication 对象。QApplication管理了各种和样的图形化应用程序的广泛资源、基本设置、控制流及事件处理等。
在任何的窗口部件被使用之前必须创建QApplication 对象。它在这里被创建并且处理这些命令行变量。所有被Qt识别的命令行参数都将从argv中被移去(并且 argc 也因此而减少)。
5) w.show():当创建一个窗口部件的时候,默认它是不可见的,必须调用show()函数使它变为可见。、
6) return a.exec():程序进入消息循环,等待可能的输入进行响应。这里就是main()函数将控制权交给Qt, Qt完成事件处理工作,当应用程序退出的时候,exec()函数的值就会返回。在exec()函数中,Qt接收并处理用户和系统的事件并且将它们传递给适当的窗口部件。
4.2 dialog.h 头文件
代码语言:javascript复制在类Dialog中的定义中,Q_OBJECT宏的作用是启动Qt元对象系统的一些特性(如支持信号和槽等),它必须放到类定义的私有区。
代码语言:javascript复制private slots: 表示下面的函数为槽函数。
dialog.h
代码语言:javascript复制#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
const static double PI = 3.1416;
class Dialog : public QDialog
{
Q_OBJECT public:
Dialog(QWidget *parent = 0);
~Dialog();
private:
QLabel *label1, *label2;
QLineEdit *lineEdit;
QPushButton *button;
private slots:
void showArea();
};
#endif // DIALOG_H
4.3 dialog.cpp
在该文件中初始化部件,创建布局管理器,将部件加到布局管理器中,并且连接
代码语言:javascript复制#include "dialog.h" Dialog::Dialog(QWidget *parent) : QDialog(parent) {
//初始化部件 label1 = new QLabel(this); label1->setText(tr("请输入圆的半径:")); lineEdit = new QLineEdit(this); label2 = new QLabel(this); button = new QPushButton(this); button->setText(tr("显示对应的圆的面积"));
//创建布局管理器,将部件加到布局管理器中 QGridLayout *mainLayout = new QGridLayout(this); mainLayout->addWidget(label1,0,0); mainLayout->addWidget(lineEdit,0,1); mainLayout->addWidget(label2,1,0); mainLayout->addWidget(button,1,1);
// 连接信号与槽
//分别是 点击和文本内容改变信号 connect(button,SIGNAL(clicked()),this,SLOT(showArea())); connect(lineEdit,SIGNAL(textChanged(QString)),this,SLOT(showArea())); } Dialog::~Dialog() { delete label1,label2,lineEdit,button; } //定义槽函数 void Dialog::showArea() { bool ok; QString tempStr; QString valueStr = lineEdit->text(); int valueInt = valueStr.toInt(&ok); double area = valueInt * valueInt * PI; label2->setText(tempStr.setNum(area)); }
4.4 上面三部分为这个工程代码的分析,介绍了主程序中完成图形化界面程序所需的编写的代码,即窗口部件的初始化,以及窗口的使用。dialog.h dialog.cpp主要告诉如何自定义一个对话框Dialog类(继承自QDialog)即定义窗口部件,以及如何将窗口部件的信号与处理事件的槽函数进行绑定。
通过上面的分析我们可以创建一个简单的计算圆面积的图形应用程序。
5、概念解析
5.1 信号和槽机制(Signal & Slot)
Qt提供了信号和槽机制用于完成界面操作的响应,是完成任意两个Qt对象之间的通信机制。其中,信号会在某个特定的情况或动作下被触发,槽是等同于接收并处理信号的函数。
每个Qt对象都包含若干个预定义的信号和若干个预定义的槽,当某一个特定的事件发生时,一个信号被发送,与信号相关的槽则会响应信号并完成相应的处理。当一个类被继承时,该类的信号和槽也同时被继承,也可以根据需要自定义信号和槽。
1. 信号与槽的连接方式
1)一个信号可以与另一个信号相连
代码语言:javascript复制 connect(Object1,SIGNAL(signal1),Object2,SLOT(signal2));
表示Object1的信号1可以触发Object2的信号2的发送。
2)一个信号可以与多个槽相连
connect(Object1,SIGNAL(signal1),Object2,SLOT(slot2));
connect(Object1,SIGNAL(signal1),Object3,SLOT(slot3));
3)一个槽可以响应多个信号
connect(Object1,SIGNAL(signal1),Object2,SLOT(slot2));
connect(Object3,SIGNAL(signal3),Object2,SLOT(slot2));
常用的连接方式为
connect(Object1,SIGNAL(signal),Object2,SLOT(slot));
其中,signal为Object1的信号,slot为Object2的槽
SIGNAL()和SLOT()是Qt定义的两个宏,它们返回其参数的C语言风格的字符串(const char*)。如下所示,两者等同。
connect(Object1,SIGNAL(signal),Object2,SLOT(slot));
connect(Object1,“signal”,Object2,“slot”);
2.信号与槽机制的优点
代码语言:javascript复制 1)类型安全。需要关联的信号和槽的签名必须是等同的,即信号的参数类型和参数个数与接收该信号的槽的参数类型和参数个数相同。
但是槽的参数个数可以少于信号的参数个数,但缺少的参数必须是信号参数的最后一个或几个参数。如信号和槽的参数签名不符,编译器就会报错。
2)松散耦合。信号与槽机制减弱了Qt对象的耦合度。
激发信号的Qt对象无须知道是哪个对象的槽接收它发出的信号,它只需在适当的时候发送适当的信号即可,它不需要关心它发出的信号有没有被接收到,以及哪个对象的哪个槽接收到该信号。
对象的槽也不需要知道哪些信号关联了自己,而一旦关联信号和槽,Qt就保证了适合的槽得到了调用。即使关联的对象在运行时被删除程序也不会崩溃。
注意:一个类若要支持信号和槽,就必须从QObject或QObject的子类继承。Qt的信号和槽机制不支持对模板的使用。
3)信号与槽机制的效率
信号与槽机制增强了对象间通信的灵活性,然而损失发一些性能。同回调函数相比,信号和槽机制运行速度有些慢。通常,传递一个信号来调用槽函数将会比直接调用非虚函数运行速度慢10倍。原因如下:
a. 需要定位接收信号的对象。
b. 安全地遍历所有的关联(如一个信号关联多个槽的情况)。
c. 编组(marshal)/解组(unmarshal)传递的参数。
d. 多线程的时候,信号可能需要排队等待。
然而,与创建对象的new操作和删除对象的delete操作相比,信号和槽的运行代价只是它们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的;同信号和槽提供的灵活性和简便性相比,这点性能损耗是值得的。
5.2 Qt5元对象系统
Qt 元对象系统提供了对象间的通信机制(信号和槽)、运行时类型信息和动态属性系统的支持,是标准C++的一个,它使Qt能够更好地实现GUI图形用户界面编程。
Qt的元对象系统不支持C++模板,尽管模板扩展了标准C++的功能,但是元对象系统提供了模板无法提供的一些特性。
Qt元对象系统基于以下三个事实。
1)基类QObject: 任何需要使用元对象系统功能的类必须继承自QObject。
2)Q_OBJECT宏:Q_OBJECT宏必须出现在类的私有声明区,用于启动元对象的特性。
3)元对象编译器 (Meta-Object Complier, moc): 为QObject子类实现元对象特性提供必要的代码实现。
5.3 布局管理器
在设计较复杂的GUI用户界面时,仅通过指定窗口部件的父子关系以期达到加载和排列窗口部件的方法是行不通的,最好的办法是使用Qt提供的布局管理器。
代码语言:javascript复制 QGridLayout *mainLayout = new QGridLayout(this); //(a) mainLayout->addWidget(label1,0,0); //(b) mainLayout->addWidget(lineEdit,0,1); mainLayout->addWidget(label2,1,0); mainLayout->addWidget(button,1,1); setLayout(mainLayout); //(c)
其中:
(a) QGridLayout *mainLayout = new QGridLayout(this) : 创建一个网格布局管理器对象 mainLayout, 并且 this 指出父窗口。
(b) mainLayout->addWidget(…) : 分别将控件label1等放置在该布局管理器中,还可以在创建布局管理器对象时不必指明父窗口。
(c) QWidget::setLayout(…) : 将布局管理器添加到对应的窗口部件对象中。因为这里的主窗口就是父窗口,所以直接调用 setLayout(mainLayout)即可。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/167817.html原文链接:https://javaforall.cn