zoukankan      html  css  js  c++  java
  • 深入浅出信号与槽

    一个事实

    在实际的项目开发中,大多数时候是直接将组件中预定义的信号连接到槽函数,信号发射时槽函数被调用。

    深度的思考

    信号是怎么来的?又是如何发射的?

    Qt中信号(SIGNAL)的本质

    信号只是一个特殊的成员函数声明

      函数的返回值是void类型

      函数只能声明不能定义

    信号必须使用signals关键字进行声明

      函数的访问属性自动被设置为protected

      只能通过emit关键字调用函数(发射信号)

    信号定义示例

    class Test : public QObject    //只有Qt类才能定义信号
    {
    Q_OBJECT         //必须使用宏Q_OBJECT
    
    signals:        //使用signals声明信号函数,访问级别为protected
        void testSignal(int v);  //信号只能声明不能定义
    public:
        void test(int i)
        {
            emit testSignal(i);  //通过emit发射信号
        }
    };
    #ifndef TESTSIGNAL_H
    #define TESTSIGNAL_H
    
    #include <QObject>
    
    class TestSignal : public QObject
    {
    Q_OBJECT
    
    public:
        void sendSignal(int i)
        {
            emit testSignal(i);
        }
    signals:
        void testSignal(int v);
    };
    
    #endif // TESTSIGNAL_H
    #ifndef RXSIGNAL_H
    #define RXSIGNAL_H
    
    #include <QObject>
    #include <QDebug>
    
    class RxSignal : public QObject
    {
        Q_OBJECT
    
    public:
    
    protected slots:
        void mySlot(int v)
        {
            qDebug() << "void mySlot(int v)";
            qDebug() << "sender:" << sender()->objectName();
            qDebug() << "receiver:" << this->objectName();
            qDebug() << "Value :" << v;
            qDebug() << endl;
        }
    };
    
    #endif // RXSIGNAL_H
    #include <QCoreApplication>
    #include <QDebug>
    #include "RxSignal.h"
    #include "TestSignal.h"
    
    void emit_signal()
    {
        qDebug() << "emit_signal() " << endl;
        RxSignal rx;
        TestSignal tx;
    
        rx.setObjectName("rx");
        tx.setObjectName("tx");
    
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
    
        for(int i=0; i<3; i++)
        {
            tx.sendSignal(i);
        }
    
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        emit_signal();
        return a.exec();
    }

     

    信号与槽的对应关系
    一个信号可以连接到多个槽(一对多)
    多个信号可以连接到一个槽(多对一)
    一个信号可以连接到另一个信号(转嫁)
    连接可以被disconnect函数删除(移除)

    实验一:一个信号被映射到两个槽函数上面

    #include <QCoreApplication>
    #include <QDebug>
    #include "RxSignal.h"
    #include "TestSignal.h"
    
    void emit_signal()
    {
        qDebug() << "emit_signal() " << endl;
        RxSignal rx;
        TestSignal tx;
    
        rx.setObjectName("rx");
        tx.setObjectName("tx");
    
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
    
        for(int i=0; i<3; i++)
        {
            tx.sendSignal(i);
        }
    
    }
    //将一个信号连接到两个槽函数上面
    void one_to_multiple()
    {
        qDebug() << "one_to_multiple() " << endl;
        RxSignal rx1;
        RxSignal rx2;
        TestSignal tx;
    
        rx1.setObjectName("rx1");
        rx2.setObjectName("rx2");
        tx.setObjectName("tx");
    
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx1, SLOT(mySlot(int)));
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx2, SLOT(mySlot(int)));
    
        tx.sendSignal(100);
    
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        //emit_signal();
        one_to_multiple();
        return a.exec();
    }
    View Code

    实验二:将两个信号映射到一个槽函数上面

    #include <QCoreApplication>
    #include <QDebug>
    #include "RxSignal.h"
    #include "TestSignal.h"
    
    void emit_signal()
    {
        qDebug() << "emit_signal() " << endl;
        RxSignal rx;
        TestSignal tx;
    
        rx.setObjectName("rx");
        tx.setObjectName("tx");
    
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
    
        for(int i=0; i<3; i++)
        {
            tx.sendSignal(i);
        }
    
    }
    //将一个信号连接到两个槽函数上面
    void one_to_multiple()
    {
        qDebug() << "one_to_multiple() " << endl;
        RxSignal rx1;
        RxSignal rx2;
        TestSignal tx;
    
        rx1.setObjectName("rx1");
        rx2.setObjectName("rx2");
        tx.setObjectName("tx");
    
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx1, SLOT(mySlot(int)));
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx2, SLOT(mySlot(int)));
    
        tx.sendSignal(100);
    
    }
    
    //将两个信号连接到一个槽函数上面
    void multiple_to_one()
    {
        qDebug() << "multiple_to_one() " << endl;
        RxSignal rx;
        TestSignal tx1;
        TestSignal tx2;
    
        rx.setObjectName("rx");
        tx1.setObjectName("tx1");
        tx2.setObjectName("tx2");
    
        QObject::connect(&tx1, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
        QObject::connect(&tx2, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
    
        tx1.sendSignal(100);
        tx2.sendSignal(200);
    
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        multiple_to_one();
        return a.exec();
    }
    View Code

     实验三:信号到信号的映射

    #include <QCoreApplication>
    #include <QDebug>
    #include "RxSignal.h"
    #include "TestSignal.h"
    
    void emit_signal()
    {
        qDebug() << "emit_signal() " << endl;
        RxSignal rx;
        TestSignal tx;
    
        rx.setObjectName("rx");
        tx.setObjectName("tx");
    
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
    
        for(int i=0; i<3; i++)
        {
            tx.sendSignal(i);
        }
    
    }
    //将一个信号连接到两个槽函数上面
    void one_to_multiple()
    {
        qDebug() << "one_to_multiple() " << endl;
        RxSignal rx1;
        RxSignal rx2;
        TestSignal tx;
    
        rx1.setObjectName("rx1");
        rx2.setObjectName("rx2");
        tx.setObjectName("tx");
    
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx1, SLOT(mySlot(int)));
        QObject::connect(&tx, SIGNAL(testSignal(int)), &rx2, SLOT(mySlot(int)));
    
        tx.sendSignal(100);
    
    }
    
    //将两个信号连接到一个槽函数上面
    void multiple_to_one()
    {
        qDebug() << "multiple_to_one() " << endl;
        RxSignal rx;
        TestSignal tx1;
        TestSignal tx2;
    
        rx.setObjectName("rx");
        tx1.setObjectName("tx1");
        tx2.setObjectName("tx2");
    
        QObject::connect(&tx1, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
        QObject::connect(&tx2, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
    
        tx1.sendSignal(100);
        tx2.sendSignal(200);
    
    }
    
    //信号到信号的映射
    void signal_to_signal()
    {
        qDebug() << "signal_to_signal() " << endl;
        RxSignal rx;
        TestSignal tx1;
        TestSignal tx2;
    
        rx.setObjectName("rx");
        tx1.setObjectName("tx1");
        tx2.setObjectName("tx2");
    
        QObject::connect(&tx1, SIGNAL(testSignal(int)), &tx2, SIGNAL(testSignal(int)));
        QObject::connect(&tx2, SIGNAL(testSignal(int)), &rx, SLOT(mySlot(int)));
    
        tx1.sendSignal(100);
        tx2.sendSignal(200);
    
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        signal_to_signal();
        return a.exec();
    }
    View Code

    从打印结果看,当tx1发送信号的时候,就会转嫁到tx2上面。信号参数会被原封不动的搬过来,将从tx1中得到的参数包裹到它自己的参数里面,,最后tx2就将它作为自己的信号发送出去了。

    不可忽视的军规
    1.Qt类只能在头文件中声明
    2.信号与槽的原型应该完全相同
    3.信号参数多于槽参数时,多余的参数被忽略(这只是个例外,将这条规则忘记吧,只记住第2条军规就行了)
    4.槽函数的返回值必须是void类型
    5.槽函数可以像普通成员函数一样被调用
    6.信号与槽的访问属性对于connect/disconnect无效(在类的外部可以直接调用类的成员函数,破坏了面向的封装性。但是它所带来的好处,远大于它的破坏。)

    信号与槽的意义
    最大限度的弱化了类之间的耦合关系
    在设计阶段,可以减少不必要的接口类(抽象类)
    在开发阶段,对象间的交互通过信号与槽动态绑定

    小结:

    信号只是一个特殊的成员函数声明
    信号必须使用signals关键字进行声明
    信号与槽可以存在多种对应关系
    信号与槽机制使得类间关系松散,提高类的可复用性

     

  • 相关阅读:
    Servlet设置Cookie无效
    IOS即时通讯XMPP搭建openfire服务器
    IOS之富文本编辑
    unittest单元测试框架总结
    杀死future处理的阻塞线程
    APP的UI设计原则
    如何降低一个程序的耦合性
    Hyperopt中文文档导读
    Hyperopt中文文档导读
    AdaBoost算法特性
  • 原文地址:https://www.cnblogs.com/-glb/p/13457841.html
Copyright © 2011-2022 走看看