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

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

注意:

  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. 加参数后,参数类型必须写全,如上面如果只写 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();
    	});

连接类型

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

1

注意事项

参数匹配

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

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

多个相同的信号与槽

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

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

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";
}

运行后点击一次按钮 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); //连接失败

注意:上面如果只在 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";
}

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

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

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

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

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

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

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

信号与槽函数的参数类型

如果 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. 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);

槽函数名字

槽函数名字最好不要与 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);

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

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

屏蔽某个对象的全部信号

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

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

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

1

QGroupBox* groupBox = dynamic_cast<QGroupBox*>(sender());
if(groupBox->objectName().compare(ui->groupBox_1->objectName()) == 0)
{

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值