信号和槽基本特点

被刻印的时光 ゝ 提交于 2019-11-29 19:14:52

本文链接:https://blog.csdn.net/hhhuang1991/article/details/79829784
信号和槽机制是 QT 的核心机制,要精通QT编程就必须对信号和槽有所了解。

一、使用方法
1. 为控件添加信号和槽
手动添加
//MyDlg.h
#pragma once
#include <QtWidgets/QDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QHBoxLayout>

class MyDlg : public QDialog
{
    //只有加入了Q_OBJECT,你才能使用QT中的signal和slot机制。
    Q_OBJECT   
private:
    QLabel* plabel;
    QLineEdit* pEdit;
    QPushButton* pBtn;

public slots:
    void Print();
public:
    MyDlg(QWidget* parent = Q_NULLPTR);
};

//MyDlg.cpp
#include "MyDlg.h"
#include <QtWidgets/QApplication>
MyDlg::MyDlg(QWidget* parent/* = Q_NULLPTR*/) :
    QDialog(parent)
{
    /** @breif QT5中如何解决中文乱码问题:
    * 常量:QString::fromLocal8Bit和QStringLiteral
    * 对于字符串变量,使用QString自带函数
    */
    plabel = new QLabel(QString::fromLocal8Bit("姓名"));
    pEdit = new QLineEdit;
    pBtn = new QPushButton(QString::fromLocal8Bit("确认"));
    // 使用布局管理器布局控件
    QHBoxLayout *Layout = new QHBoxLayout;
    Layout->addWidget(plabel);
    Layout->addWidget(pEdit);
    QVBoxLayout* BottomLayout = new QVBoxLayout;
    BottomLayout->addLayout(Layout);
    BottomLayout->addWidget(pBtn);
    // 设置窗口的布局管理器
    setLayout(BottomLayout);
    setWindowTitle(tr("Find"));
    setFixedHeight(sizeHint().height());
    //信号与槽的绑定
    connect(pBtn, SIGNAL(clicked()), this, SLOT(Print()));
}

void MyDlg::Print()
{
    QMessageBox::information(this, QString::fromLocal8Bit("确认"),
        QString::fromLocal8Bit("你点击了我"), QMessageBox::Ok);
}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyDlg mydlg;
    mydlg.show();
    return a.exec();
}
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
54
55
56
57
58
59
60
61
62
63
64
65
解析:
1. MyDlg类中必须要在一行定义Q_OBJECT这个宏,这样才能在类中使用信号和槽的功能;
2. 在类中声明并实现槽函数void print(), 槽函数都是声明在public slots下,当然也可以是private slots或protected slots,

//MyDlg.h
public slots:
    void Print();
//MyDlg.cpp
void MyDlg::Print()
{
    QMessageBox::information(this, QString::fromLocal8Bit("确认"),
        QString::fromLocal8Bit("你点击了我"), QMessageBox::Ok);
}
1
2
3
4
5
6
7
8
9
3 . 使用connect函数关联按钮的clicked()信号和槽函数print(),

connect(pBtn, SIGNAL(clicked()), this, SLOT(Print()));
1
4.运行程序,点击按钮,会弹出“你点击了我”的消息框。

如果将带有Q_OBJECT宏的类声明放在cpp文件中,编译就会出现如下错误:
错误:1>main.obj : error LNK2001: 无法解析的外部符号 “public: virtual struct QMetaObject const * __thiscall MyDlg::metaObject(void)const ” (?metaObject@MyDlg@@UBEPBUQMetaObject@@XZ)
1>main.obj : error LNK2001: 无法解析的外部符号 “public: virtual void * __thiscall MyDlg::qt_metacast(char const *)” (?qt_metacast@MyDlg@@UAEPAXPBD@Z)
1>main.obj : error LNK2001: 无法解析的外部符号 “public: virtual int __thiscall MyDlg::qt_metacall(enum QMetaObject::Call,int,void * *)” (?qt_metacall@MyDlg@@UAEHW4Call@QMetaObject@@HPAPAX@Z)
1>main.obj : error LNK2001: 无法解析的外部符号 “public: static struct QMetaObject const MyDlg::staticMetaObject” (?staticMetaObject@MyDlg@@2UQMetaObject@@B)
原因:只有继承了QObject类的类,才具有信号槽的能力。所以,为了使用信号槽,必须继承QObject。凡是QObject类(不管是直接子类还是间接子类),都应该在第一行代码写上Q_OBJECT。不管是不是使用信号槽,都应该添加这个宏。这个宏的展开将为我们的类提供信号槽机制、国际化机制以及 Qt 提供的不基于 C++ RTTI 的反射能力。因此,如果你觉得你的类不需要使用信号槽,就不添加这个宏,就是错误的。其它很多操作都会依赖于这个宏。注意,这个宏将由 moc(我们会在后面章节中介绍 moc。这里你可以将其理解为一种预处理器,是比 C++ 预处理器更早执行的预处理器。) 做特殊处理,不仅仅是宏展开这么简单。moc 会读取标记了 Q_OBJECT 的头文件,生成以 moc_ 为前缀的文件,比如 MyDlg.h 将生成 moc_MyDlg.cpp。你可以到构建目录查看这个文件,看看到底增加了什么内容。注意,由于 moc 只处理头文件中的标记了Q_OBJECT的类声明,不会处理 MyDlg.cpp 文件中的类似声明。因此,如果我们的MyDlg类位于 MyDlg.cpp 中,是无法得到 moc 的处理的。解决方法是,我们还是将其放在头文件中。

Qt Designer中自动添加
打开Qt Designer,编辑界面ui文件,然后按下图所示,添加信号槽,

在Qt Designer编辑好后,还需要将添加的槽函数,声明和定义到框架类中去,

public slots:
void OnPushButton()
{
    QMessageBox::information(this, QString::fromLocal8Bit("确认"),
        QString::fromLocal8Bit("你点击了我"), QMessageBox::Ok);
}
1
2
3
4
5
6
这种方法就不需要采用connect去连接信号和槽。

2. 添加自定义信号和槽
信号和槽实现两个对象的通讯。

//sender类给recv类发送信号,receiver类接收到信号后,执行槽函数
class sender
{
   Q_OBJECT
   signals:
   void send();   //信号只需要定义,不需要声明
}
class receiver
{
   Q_OBJECT
   public slots:
   void recv(){}   //槽函数
}
//在主框架类的构造函数中,连接信号和槽
sender* psender = new sender;
receiver* precv = new receiver;

connect(psender, SIGNAL(send()), precv, SLOT(recv()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
二、特点
信号和槽是QT自行定义的一种通信机制,它独立于标准的 C / C++ 语言,因此要正确的处理信号和槽,必须借助一个称为 moc(Meta Object Compiler)的QT工具,该工具是一个C++ 预处理程序,它为高层次的事件处理自动生成所需要的附加代码。所有从 QObject 或其子类(例如 Qwidget) 派生的类都能够包含信号和槽。主要有以下几个特点:
1、类型安全。
需要关联的信号和槽的签名必须是等同。即信号的参数类型和参数个数 同接收该信号的槽的参数类型和参数个数相同。
2. 信息封装。
激发信号的Qt对象无须知道是哪个对象的哪个槽需要接收它发出的信号,它只需要做的是在适当的时间发送适当的信号就可以了,而不需要知道也不关心它的信号有没有被接收到,更不需要知道哪个对象的哪个槽接收到了信号。同样地,对象的槽也不知道是哪些信号关联了自己,而一旦关联信号和槽,Qt就保证了适合的槽得到了调用。即使关联的对象在运行时被删除。应用程序也不会崩溃。

3.自由连接。
一个信号可以连接多个槽函数,而一个槽函数也可以连接多个信号,一个信号还可以连接另外一个信号。对于与一个信号相连的多个槽函数,他们的执行顺序是随机的。
4.效率不高。
信号和槽机制增强了对象间通信的灵活性,然而这也损失了一些性能。同回调函数相比,信号和槽机制运行速度有些慢。通常,通过传递一个信号来调用槽函数将会比直接调用直接调用非虚函数运行速度慢10倍。原因如下:

需要定位接收信号的对象。
安全地遍历所有的关联。
编组/解组传递的参数。
多线程的时候。信号可能需要排队等待。
————————————————
版权声明:本文为CSDN博主「hhhuang1991」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hhhuang1991/article/details/79829784

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!