前言

本文讨论的qt版本只限于qt5及以上,且代码并不保证可运行,仅起示范作用。

使用

qt关键字:signals、emit、slots

下面从一个简单实例总结一些使用注意事项:

class Test : public QObject
{
Q_OBJECT

Test();
~Test(){}
private slots:
void sltTest(int);
signals:
void sglTest(int);
}
Test::Test(){}
void Test::sltTest(int v){}
int main()
{
Test t;
connect(t, &Test::sglTest, t, &Test::sltTest);
emit t->sglTest();
return 0;
}

注意事项:

  1. 类需要继承了QObject、类声明中需要Q_OBJECT宏
  2. 信号与槽需要QObject::connect 连接
  3. 通常情况下信号与槽的参数个数、类型一致
  • 信号和槽函数参数都可以有默认值
  1. 信号:
  • 返回值只能是void类型
  • 只需要声明,定义由moc生成
  • signals前不能有访问控制符,public等
  • 信号函数参数个数不能少于槽函数参数个数,槽函数的可以少于信号函数的,
  1. 关键字冲突
  • .pro添加 CONFIG += no_keywords,qt将不定义signals等关键字,可以用Q_SIGNAL替代使用


connect的几种写法

Counter a, b;
//1.推荐写法,使用函数指针可以不用写参数,编译器会检查函数签名,隐式转换参数类型,避免同名函数歧义问题
QObject::connect(&a, &Counter::valueChanged, &b, &Counter::setValue);
//2.使用SIGNAL and SLOT宏,不会检查函数签名,不允许有参数名,不能有返回类型,
QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
//3.lambda表达式
connect(&a, &a::valueChanged, this, [=](int v){ });

连接类型

AutoConnection,发送和接收在同一线程,则是DirectConnection,否则QueuedConnection

DirectConnection,直接调用槽函数,类似调用普通函数,在信号所在线程中执行

QueuedConnection,队列关联,当控制权回到接收线程的事件循环时被调用

BlockingQueuedConnection,阻塞队列关联,类似队列关联,只是信号线程会阻塞到槽函数返回,所以不可用于信号和接收者同一线程,将会导致死锁。

UniqueConnection,唯一关联,标志位,可与上类型组合使用,已存在的连接将会连接失败

疑问

  1. 同名函数,不同参数类型,connect?。编译报错:上下文不允许消除重载函数的歧义, error C2664: “QMetaObject::Connection QObject::connect(const QObject *,const char *,const QObject *,const char *,Qt::ConnectionType)”: 无法将参数 2 从“overloaded-function”转换为“const char *”

需要明确指出函数,指定函数指针参数类型即可

void (TestSlot::*newSgl)(int) = &TestSlot::sglTest;
void (TestSlot::*newSlt)(int) = &TestSlot::sltTest;
或者
static_cast<void (TestSlot:: *)(int)>(&TestSlot::sglTest)
或者typedef xx xxx
  1. 信号槽的执行顺序?

注意qt版本的不同,qt4应该是随机的,qt5之后按连接顺序执行。备注:未经详细测试

connect: signal a -> slot a signal a -> slot b signal a -> slot c, 顺序:a -> b -> c。



参考

官网:​​https://doc.qt.io/qt-5/signalsandslots.html​

使用及一些注意和原理讲解:​​https://zhuanlan.zhihu.com/p/347456731​