QT-信号与槽用法实例与注意事项及五个连接方式详解

64 篇文章 10 订阅
55 篇文章 27 订阅

1.基本用法
2.slots的函数用法
3.slots的lambda表达式
4.断开连接到对象信号的所有Object
5.断开连接到特定信号的所有Object
6.断开特定接收者
7.connect函数的第5参数Qt::ConnectionType
8.QObject::connect: Cannot queue arguments of type 'XXX'
9.QTimer的 singleShot
环境:
QT版本:5.6.2
VS版本:VS2013
系统版本:windows 7 64bit

前言
Qt 信号槽一直以来都是QT的核心之一。但是,大家真的对信号的槽非常了解吗?下面介绍的一些内容,你可能并不知道。

1.基本用法
QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, &QLineEdit::textChanged,label,  &QLabel::setText);

2.slots的函数用法
函数原型:

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

使用:

void someFunction();

QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::clicked, someFunction);

3.slots的lambda表达式
QByteArray page = ...;
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("qt-project.org", 80);
QObject::connect(socket, &QTcpSocket::connected, [=] () {socket->write("GET " + page + "\r\n");});

4.断开连接到对象信号的所有Object
disconnect(myObject, 0, 0, 0);

等同于

myObject->disconnect();

5.断开连接到特定信号的所有Object
    disconnect(myObject, SIGNAL(mySignal()), 0, 0);

等同于

myObject->disconnect(SIGNAL(mySignal()));

6.断开特定接收者
disconnect(myObject, 0, myReceiver, 0);

等同于

myObject->disconnect(myReceiver);

7.connect函数的第5参数Qt::ConnectionType
参数    值    说明
Qt::AutoConnection    0    (默认)如果接收者位于发出信号的线程中,则使用Qt :: DirectConnection。 否则,使用Qt :: QueuedConnection。 连接类型在发出信号时确定
Qt::DirectConnection    1    发出信号时立即调用插槽。 插槽在信号线程中执行。相当于槽函数直接执行,执行完之后,才继续执行信号的后续内容
Qt::QueuedConnection    2    槽函数在控制回到接收者所在线程的事件循环时被调用。 槽函数在接收者的线程中执行。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用
Qt::BlockingQueuedConnection    3    与Qt :: QueuedConnection相同,除了信号线程阻塞直到槽返回。 如果接收者与信号位于同一线程中,则不得使用此连接,否则程序将会死锁
Qt::UniqueConnection    0x80    这是一个可以使用按位OR与上述任何一种连接类型组合的标志。 当设置Qt :: UniqueConnection时,如果连接已经存在,QObject :: connect()将失败(即,如果相同的信号已连接到同一对对象的相同插槽)。 这个标志是在Qt 4.6中引入的
这里需要注意的是一般我们都是采用默认参数Qt::AutoConnection。但是,需要注意有时可能会出现问题。
例如,我们要在登录成功后关闭当前界面。实现如下:

void login()
{
...
//验证完毕,返回登录成功信号
emit loginSuccess();
this->hide();
}

如果此时我们采用Qt::AutoConnection,并且信号与槽位于同一线程,那么QT会自动为我们选择为Qt::DirectConnection方式。也就是必须等待信号对应的槽函数执行完,才能执行hide操作。这是如果槽函数是一个耗时操作,那么就会出现无法hide的情况,这种情况下,我们需要手动设置Qt::QueuedConnection或者将hide函数在emit之前执行。

总结一下就是emit函数之后的内容的执行顺序与信号与槽的连接方式有关。如果需要立即执行emit之后的函数,就需要设置为Qt::QueuedConnection,如果要立即执行槽函数,就需要设置为Qt::DirectConnection。

8.QObject::connect: Cannot queue arguments of type ‘XXX’
出现这个错误是由于对于Qt::QueuedConnection连接,参数必须是Qt的元对象系统已知的类型,因为Qt需要复制参数以将它们存储在幕后的事件中。
在建立连接之前,请调用qRegisterMetaType()注册数据类型。

9.QTimer的 singleShot
函数原型:

[static] void QTimer::singleShot(int msec, const QObject *receiver, const char *member)

QTimer::singleShot(100,this, &A::function());

QTimer::singleShot(100,[=]{qDebug() << "time out"; });

/*********************************************************************

DisConnect:

bool QObject::disconnect(const QObject * sender, const char * signal, const QObject * receiver, const char *method) [static]

1. Disconnect everything connected to an object's signals:

取消某个对像的所有的信号连接:

disconnect(myObject,0,0,0);

equivalent to the non-static overloaded function

等价于:

myObject->disconnect();

2. Disconnect everything connected to a specific signal:

取消某个信号与它对应槽的所有连接:

disconnect(myObject, SIGNAL(mySignal()),0,0);

equivalent to the non-static overloaded function

等价于:

myObject->disconnect(SIGNAL(mySignal()));

3. Disconnect a specific receiver:

断开某个接收对象的连接:

disconnect(myObject,0, myReceiver,0);

equivalent to the non-static overloaded function

等价于:

myObject->disconnect(myReceiver);

/************************************

Qt信号的处理 (connect和disconnect和blockSignals的使用方法)

信号槽机制是Qt的非常重要的机制,本文不剖析信号槽机制的原理,只讲信号的主要使用方法。

信号:当某个事件发生后,比如按钮检测到自己被点击了一下,按钮会发出一个信号,这种发出是没有目的的,类似广播。

1. 信号连接槽  connect函数
connect函数最常用的一般形式:

connect(sender, signal, receiver, slot);
2. 解除信号和连接到信号的槽的连接 disconnect函数
disconect函数的原型如下:

bool QObject::disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
一般有四种用法:

2.1  解除myObject对象的所有信号连接,调用后myObject对象发出的所有信号都得不到响应。

    disconnect(myObject, 0, 0, 0);
//等同于
    myObject->disconnect();
2.2  解除myObject对象的mySignal信号的所有连接,调用后myObject对象发出的mySignal信号得不到响应。

    disconnect(myObject, SIGNAL(mySignal()), 0, 0);
//等同于
    myObject->disconnect(SIGNAL(mySignal()));
2.3  解除myObject对象与myReceiver对象的信号连接,调用后myObject对象发出的所有信号得不到myReceiver对象的响应。

    disconnect(myObject, 0, myReceiver, 0);
//等同于
    myObject->disconnect(myReceiver);
2.4  解除myObject对象的mySignal信号与myReceiver对象的mySlot槽的连接,调用后myObject对象发出的mySignal信号得不到myReceiver对象的mySlot槽的响应。

    disconnect(myObject, SIGNAL(mySignal()), myReceiver, SLOT(mySlot()));
//等同于
    myObject->disconnect(SIGNAL(mySignal(), myReceiver, SLOT(mySlot())));
ps:上述4种用法中,0是通配符,它表示任一信号、任一接收对象、任一槽。

3. 阻塞信号  blockSignals函数
blockSignals的函数原型如下:

bool QObject::blockSignals(bool block)
用法:

//object发出的信号被阻塞,系统不会调用任何连接到object的处理。
object->blockSignals(true);
 
//解除信号阻塞
object->blockSignals(false);
4.案例
//widget.h

#ifndef WIDGET_H
#define WIDGET_H
 
#include <QWidget>
 
class Widget : public QWidget
{
    Q_OBJECT
 
public:
    Widget(QWidget *parent = 0);
    ~Widget(){}
};
 
#endif // WIDGET_H
//widget.cpp

#include "widget.h"
 
#include <QPushButton>
#include <QGridLayout>
#include <QDebug>
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    /* 创建控件 */
    QPushButton *btnClick      = new QPushButton("click me", this);
    QPushButton *btnBlock      = new QPushButton("block", this);
    QPushButton *btnUnblock    = new QPushButton("unblock", this);
    QPushButton *btnDisconnect = new QPushButton("disconnect", this);
 
    QGridLayout *pLayout = new QGridLayout();
    pLayout->addWidget(btnClick, 0, 0);
    pLayout->addWidget(btnBlock, 0, 1);
    pLayout->addWidget(btnUnblock, 0, 2);
    pLayout->addWidget(btnDisconnect, 0, 3);
    this->setLayout(pLayout);
 
    /* 信号槽 */
    connect(btnClick, &QPushButton::clicked, this, [=]()
    {
        qDebug() << "点击按钮";
    });
 
    connect(btnBlock, &QPushButton::clicked, this, [=]()
    {
        btnClick->blockSignals(true);
        btnBlock->setEnabled(false);
        btnUnblock->setEnabled(true);
    });
 
    connect(btnUnblock, &QPushButton::clicked, this, [=]()
    {
        btnClick->blockSignals(false);
        btnBlock->setEnabled(true);
        btnUnblock->setEnabled(false);
    });
 
    connect(btnDisconnect, &QPushButton::clicked, this, [=]()
    {
        disconnect(btnClick, &QPushButton::clicked, this, 0);
    });
 
}
//main.c

#include "widget.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
 
    return a.exec();
}
结果:

1.按下"click me"按钮,窗口接收到"click me"按钮发出的clicked信号,打印"点击按钮"。

2.然后再按下"block",再次按下"click me"按钮,没有打印"点击按钮"。这是因为 "click me"按钮发出的信号被阻塞了,没有得到响应。

3.然后再按下"unblock"按钮,再次按下"click me"按钮,打印"点击按钮"。这是因为解除了"click me"按钮的信号阻塞。

4.最后按下"disconnect"按钮,此时按下"click me"按钮没有反应。这是因为解除了本窗口与"click me"按钮的clicked信号的连接。 
 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,我们可以得知QT信号的第五个参数是连接类型。连接类型有五种,分别是: 1. Qt::AutoConnection:自动连接方式,如果信号在同一线程中,则采用直接连接方式,否则采用队列连接方式。 2. Qt::DirectConnection:直接连接方式,当信号发射时,函数会立即执行,无论信号在哪个线程中。 3. Qt::QueuedConnection:队列连接方式,当信号发射时,函数会被放入接收者对象所在的线程的事件队列中,等待事件循环处理。 4. Qt::BlockingQueuedConnection:阻塞队列连接方式,当信号发射时,函数会被放入接收者对象所在的线程的事件队列中,但是发射信号的线程会被阻塞,直到函数执行完毕。 5. Qt::UniqueConnection:唯一连接方式,如果信号已经连接到了某个函数,再次连接时会失败。 下面是一个例子,演示了如何使用第五个参数来连接信号: ```cpp // 定义一个信号 class Sender : public QObject { Q_OBJECT signals: void mySignal(int value); }; // 定义一个函数 class Receiver : public QObject { Q_OBJECT public slots: void mySlot(int value) { qDebug() << "Received value: " << value; } }; // 在main函数中连接信号 int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Sender sender; Receiver receiver; // 使用Qt::QueuedConnection连接信号 QObject::connect(&sender, SIGNAL(mySignal(int)), &receiver, SLOT(mySlot(int)), Qt::QueuedConnection); // 发射信号 emit sender.mySignal(100); return a.exec(); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值