• Qt 信号与槽


    Qt Creator快速入门 第2版 第三章
    Qt5.9 c++开发指南 第二章
    QT 学习之路 2(4):信号槽
    Qt 帮助文档 Signals & Slots
    技术点:connect函数的几种写法及连接方式
    qt connect函数_Qt signal函数使用类内部类型作为参数导致connect不成功问题分析

    信号与槽机制有 Qt 的元对象系统提供,因此使用时需要在类的声明的私有区域添加 Q_OBJECT 宏。

    connect

    Qt5.9 c++开发指南 第二章
    connect 是 QQbject 类的静态函数,而 QObject 是所有 Qt 类的基类,实际调用时可忽略前面的限定符
    技术点:connect函数的几种写法及连接方式

    格式一

    [static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
    const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)

    Creates a connection of the given type from the signal in the sender object to the method in the receiver object.
    Returns a handle to the connection that can be used to disconnect it later.
    You must use the SIGNAL() and SLOT() macros when specifying the signal and the method, for example:
    Note that the signal and slots parameters must not contain any variable names, only the type.

    示例:

    //信号参数多于槽函数的参数,多余的参数被忽略,可以正常连接
    connect1 = connect(ui->pushButton_1, SIGNAL(clicked(bool)), this, SLOT(btnClicked()));
    //第二个和第一个效果相同
    connect1 = connect(ui->pushButton_1, SIGNAL(clicked()), this, SLOT(btnClicked()));
    
    • 1
    • 2
    • 3
    • 4

    注意:槽函数不能是普通的成员函数,必须在槽函数中声明。

    格式二

    1

      QLabel *label = new QLabel;
      QLineEdit *lineEdit = new QLineEdit;
      QObject::connect(lineEdit, &QLineEdit::textChanged, label,  &QLabel::setText);
    
    • 1
    • 2
    • 3

    注意:

    1. 这种格式不用写参数,但如果信号和槽有重载会出错。
    2. 与格式一不同,槽函数是普通成员函数也可以。
    3. 如果信号与槽的参数是自定义类型,也要注册类型。

    如果要加参数写法:

    class Tst : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit Tst(QWidget *parent = 0);
        void fun(void);
    
    signals:
        void sig3(const QString &str);
    
    };
    
    // Widget 中槽函数声明,可以为普通成员函数
    void slot3(const QString &str);
    
    QMetaObject::Connection connectTst;
    connectTst = connect(m_tst, static_cast<void (Tst::*)(const QString&)>(&Tst::sig3),
    					this, &Widget::slot3);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注意:

    1. 加参数后,参数类型必须写全,如上面如果只写 QString 则会提示错误。
    2. 如果信号和槽有重载,这种写法依旧提示错误,找不到槽函数。

    格式三

    [static] QMetaObject::Connection QObject::connect(const QObject *sender,
    PointerToMemberFunction signal, Functor functor)

    Lambda 表达式写法:

    connect(noteItem, static_cast<void (MyItem::*)(const int, const QString &)>
    (&MyNoteItem::sigUpdateItemName),[&](const int index,const QString &str){
            //函数实现
        });
    
    connect(width_spinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 
    		[=](int v){
            	this->show();
        	});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    连接类型

    技术点:connect函数的几种写法及连接方式

    1

    注意事项

    参数匹配

    信号与槽的参数个数和类型必须匹配,槽函数的参数个数可以少于信号中的参数,多余的参数会被忽略。

    //信号参数多于槽函数的参数,多余的参数被忽略,可以正常连接
    connect1 = connect(ui->pushButton_1, SIGNAL(clicked(bool)), this, SLOT(btnClicked()));
    //第二个和第一个效果相同
    connect1 = connect(ui->pushButton_1, SIGNAL(clicked()), this, SLOT(btnClicked()));
    
    • 1
    • 2
    • 3
    • 4

    多个相同的信号与槽

    连接多个相同的信号与槽,不同的连接各自独立。

    示例:对一个按钮建立三个相同的信号与槽的连接,并获取返回值,来判断连接是否成功。

    void Widget::connectSigSlot()
    {
        QMetaObject::Connection connect1, connect2, connect3;
        connect1 = connect(ui->pushButton_1, SIGNAL(clicked(bool)), this, SLOT(btnClicked()));
        connect2 = connect(ui->pushButton_1, SIGNAL(clicked()), this, SLOT(btnClicked()));
        connect3 = connect(ui->pushButton_1, SIGNAL(clicked(bool)), this, SLOT(btnClicked()));
    
        if (connect1)
        {
            qDebug() << "connect connect1 successully";
        }
    
        if (connect2)
        {
            qDebug() << "connect connect2 successully";
        }
    
        if (connect3)
        {
            qDebug() << "connect connect3 successully";
        }
    
        if (disconnect(connect1))
        {
            qDebug() << "disconnect connect1 successully";
        }
    
        if (connect1)
        {
            qDebug() << "connect1 is valid";
        }
    
        if (connect3)
        {
            qDebug() << "connect3 is valid";
        }
    }
    
    void Widget::btnClicked()
    {
        qDebug() << "button clicked";
    }
    
    • 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

    运行后点击一次按钮 pushButton_1 后输出的结果如下:

    1

    1. 虽然三个信号与槽相同,但均能连接成功。
    2. 将第一个连接 connect1 切断连接后,第三个相同的连接仍有效,只有第一个连接无效,三个连接独立。
    3. 点击一次按钮,进入槽函数两次,因为第二个和第三个连接仍有效,即使信号与槽相同,仍会执行两次。

    如果希望不重复连接相同的信号和槽,则用连接类型为 Qt::UniqueConnection:

    QMetaObject::Connection connectTst, connectTst1;
    
    connectTst = connect(m_tst, SIGNAL(sig3(QString)), 
    				this, SLOT(slot1(QString)), Qt::UniqueConnection);
        
    connectTst1 = connect(m_tst, SIGNAL(sig3(QString)), 
    				this, SLOT(slot1(QString)), Qt::UniqueConnection); //连接失败
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意:上面如果只在 connectTst 中设置类型,则 connectTst1 仍会连接成功,只会在 connectTst 连接时检查该连接有没有重复,因此这里第一个连接 connectTst 不用设置连接类型。

    connect 函数中槽函数参数不能包括任何变量名

    connect 函数中槽函数的参数不能包含任何变量名,只有类型。
    类型中不用指明 const 和引用,connect 会忽略它们。
    如果类型中将 const 和引用写上,要么写全,要么不写,都没问题,如果只写一部分则不能连接成功。

    QMetaObject::Connection connectTst, connectTst1, connectTst2;
    connectTst = connect(m_tst, SIGNAL(sig1(const QString&)), this, SLOT(slot1(const QString&)));
    connectTst1 = connect(m_tst, SIGNAL(sig1(QString&)), this, SLOT(slot1(QString&)));
    connectTst2 = connect(m_tst, SIGNAL(sig1(QString)), this, SLOT(slot1(QString)));
    
    if (connectTst)
    {
        qDebug() << "connect connectTst successully";
    }
    if (connectTst1)
    {
        qDebug() << "connect connectTst1 successully";
    }
    if (connectTst2)
    {
        qDebug() << "connect connectTst2 successully";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    第一个连接和第三个连接有效,第二个连接失败,会有如下提示:

    QObject::connect: No such signal Tst::sig1(QString&) in ..\QLabel\src\widget.cpp:19 
    
    • 1

    如果 connect 函数中信号或槽函数参数中有变量名会出错。

    connect 函数中信号的发送者和接收者不能为空

    connect(m_tst, SIGNAL(sig1(const QString&)), this, SLOT(slot1(const QString&)));
    m_tst = new Tst;
    
    • 1
    • 2

    上面,如果在建立信号与槽连接时, m_tst空指针,则连接建立失败,会有如下提示:

    QObject::connect: Cannot connect (nullptr)::sig1(const QString&) to 
    Widget::slot1(const QString&)
    
    • 1
    • 2

    信号与槽函数的参数类型

    如果 Qt::ConnectionType 类型为 Qt::QueuedConnection,信号与槽函数的参数必须是 QVariant,如果是自定义类型,需要注册类型。因为 Qt 需要复制参数存起来,因此要是 Qt 元对象系统能识别的类型。

    1

    测试时连接类型选择 Qt::DirectConnection 后用自定义参数不注册也不会出错,但改为 Qt::QueuedConnection 后必须注册才能连接成功。

    //头文件中
    typedef struct
    {
      int i;
      int j;
    }MyStruct;
    
    Q_DECLARE_METATYPE(MyStruct)
    
    //.cpp 文件,在连接前注册
    qRegisterMetaType<MyStruct>();
    connect(m_tst, SIGNAL(sig2(MyStruct)), this, SLOT(slot2(MyStruct)), Qt::QueuedConnection);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注册自定义类型有两步:

    1. Q_DECLARE_METATYPE(Type) 宏让该类型被元对象系统识别。
    2. 在建立连接前调用 qRegisterMetaType() 注册类型,能在运行时被解析。

    Q_DECLARE_METATYPE(Type) 介绍:

    1

    注意:如果类型在某个名称空间中,也要带上名称空间。

    namespace MyType {
        typedef struct
        {
          int i;
          int j;
        }MyStruct;
    }
    
    Q_DECLARE_METATYPE(MyType::MyStruct)
    
    qRegisterMetaType<MyType::MyStruct>();
    
    connect(m_tst, SIGNAL(sig2(MyType::MyStruct)), this, 
    		SLOT(slot2(MyType::MyStruct)), Qt::QueuedConnection);
    
    //或者写成下面形式,不用写参数
    connect(m_tst, &Tst::sig2, this, &Widget::slot2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    槽函数名字

    槽函数名字最好不要与 qt 界面中自动生成的槽函数名字相同。
    如按钮 pushButton_2clicked() 信号,在界面自动的槽函数名字为 on_pushButton_2_clicked,如果自己建立一个信号 pressed() 对应的槽函数也为 on_pushButton_2_clicked,则当按钮按下时,会执行一次槽函数,然后释放后再执行一次。

    信号无返回值

    Qt 帮助文档 Signals & Slots

    信号无返回值,也无需实现信号定义,但信号需要声明在 signals: 中。
    1

    一个信号连接多个槽函数

    槽函数执行的顺序按照建立连接的顺序执行。

    多个信号连接到同一个槽

    多个信号连可以接到同一个槽。

    一个信号连接到另一个信号

    一个信号发射时,发射另一个信号。

    槽函数的执行

    Qt 帮助文档 Signals & Slots
    技术点:connect函数的几种写法及连接方式

    通常,一个信号被发射后,槽函数立马执行,执行完槽函数后才会继续执行发射信号后面的代码。
    但是,如果连接的类型是 Qt::QueuedConnection,则会先执行 emit 发射信号后面的代码,在之后才会执行槽函数。

    1

    槽函数与普通成员函数区别

    1. 头文件中声明了一个槽函数但没有定义,构建时会报错:无法解析的外部符号;但普通的成员函数不会。
    2. 对于格式一,只有槽函数能与信号建立连接,如果 connect 中槽函数写普通成员函数,构建不会提示错误,但触发信号时,不会进入该函数执行。格式二无该要求,槽函数也可以是普通的成员函数。
    3. 对于非虚函数调用,用信号与槽来执行槽函数比直接调用槽函数要慢接近十倍。
      1

    取消信号与槽的连接

    对某个特定的信号与槽的连接取消

    利用 connect 的返回值

    QMetaObject::Connection connectTst3 = connect(m_tst, &Tst::sig2, this, &Widget::slot2);
    disconnect(connectTst3);
    
    • 1
    • 2

    屏蔽某个对象的特定信号的处理

     m_tst->disconnect(m_tst);
     //等价于下面的写法,m_tst 对象发送的所有信号,接受者为 m_tst 的所有槽函数屏蔽
    disconnect(m_tst, nullptr, m_tst, nullptr);
    
    • 1
    • 2
    • 3

    屏蔽某个对象的全部信号

     m_tst->disconnect();
     //等价于下面的写法
     disconnect(m_tst, 0, 0, 0);
    
    • 1
    • 2
    • 3

    sender() 获取发送信号的对象

    当有多个信号连接到一个槽函数时,可以通过该函数区分信号来源,如一个 buttonGroup 包含多个按钮。

    1

    QGroupBox* groupBox = dynamic_cast<QGroupBox*>(sender());
    if(groupBox->objectName().compare(ui->groupBox_1->objectName()) == 0)
    {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    基于MobileNetV2主干的DeeplabV3+语义分割实现
    交换机和路由器技术-28-OSPF的NSSA区域
    【深度学习CPU(番外篇)——初识总线】
    用户分析-AARRR模型(海盗模型)
    初次接触氛围系统架构,聊聊我这三个月的理解
    k8s svc流量转发
    题目 1096:扫雷舰
    猿创征文|当我在追光 我与光同航--我与Java的技术成长之路
    Python入门-变量定义与切片&Python引入包和引入模块
    都已过35+程序员高危高龄,我为什么还要学习python?
  • 原文地址:https://blog.csdn.net/Lee567/article/details/127009688