Qt
大约 6 分钟
Qt
目录
Qt信号和槽机制**【重点】**
简概(emit、信号、槽、Connect)
- 信号槽优点 / 特点
类型安全
。需要信号和槽的参数类型和个数相同,或后者少于前者的后几个参数松散耦合
。即信号发送端换接收端本身没有关联,通过connect
连接,将两端耦合在一起- connect只是对象发送信号和处理函数这两者,本质也是类似于
js/Android
的控件对象绑定方法,但写法不同
- 使用
- 通用:
connect(信号的发送者, 发送的信号, 信号的接受者, 处理的槽函数);
- 举例:
connect(btn2, &QPushButton::clicked, this, &QWidget::close);
- 补充:发送的信号通过查手册的
Signals
,槽函数查手册的Public Slots
- 补充:如果第三个参数是this,则可以省略
- 补充:注意参数是函数名而不是调用函数
- 通用:
- 剖析
- 为什么第二个和第四个参数需要加
&
符号和作用域解析运算符::
? - connect的第二四个参数分别为
const char *signal
和const char *member
,加&以后为按引用传参,本质是传递指针! - 当然还有令一种写法——传递函数指针(详见后面的 “信号和槽的重载”)
- 为什么第二个和第四个参数需要加
- 性能
- 增强了对象间通信的灵活性,然而损失了一些性能,比直接调用非虚函数的运行速度慢10倍
- 慢的原因
- 需要定位接受信号的对象(动态使用)
- 安全地遍历所有关联(如一个信号关联多个槽的情况)
- 编组(marshal)/解组(unmarshal)传递的参数
- 在多线程时,信号可能需要排队等待
- 然而与堆对象的new和delete操作相比,其代价很小。信号和槽导致的性能损失对于实时应用程序是可以忽略的
自定义信号和槽
前提
- 继承
QObject
类或其派生类
- 继承
写法
class A : public QObject { Q_OBJECT public: // ... signals: // 自定义信号 void is_a_signal(); // 返回值是void、只需声明不需实现、可有参数、可以重载 public slots: // 早期Qt版本,必须要写到public slots,高级版本可以写到public或者全局下(即没有槽函数一说?) void is_a_slots(); // 返回值是void,需要声明并实现、可有参数、可以重载 }
触发信号:
emit a->is_a_signal();
信号特点
- 写到
signals
下 - 返回值是void
- 只需声明不需实现
- 可有参数、可以重载
- 写到
槽特点
- 写到
public slots
下,高版本还可以写public
下(private slots
/private
也行) (真的?但话说槽函数不是需要额外追踪以实现动态调用吗,本质上其实还是public slots吧) - 返回值是void
- 需要声明并实现
- 可有参数、可以重载
- 写到
信号和槽的重载
和函数重载一样,需要注意的点:
emit
触发不同版本的信号连接时,信号的参数列表数量必须大于或等于槽的,类型必须匹配。一般添加重载方法是两边都添加
connect
连接时需要使用函数指针来区分不同版本的信号和槽(如果没有重载则不需要这一步)函数指针定义方式:
函数返回类型(*指针变量名)(函数参数列表)
例如:
void(Widget:: *ptr_1)(QWidget*) = &Widget::is_a_signal; void(Widget:: *ptr_2)(QWidget*) = &Widget::is_a_slots; connect(this, ptr_1, this, ptr_2);
常用信号和槽
clicked()
,点击hover()
,悬浮triggered()
,触发(点击菜单项)
Lambda的槽函数妙用
使用
使用
通用:[=](){/**/}();
作为槽函数时不需要最后面的()
,即[=](){/**/}
即可
函数对象参数有以下形式
参数 | 说明 |
---|---|
空 | 没有使用任何函数对象参数 |
= | 函数体内可以使用Lambda范围内所有可见的局部变量,并使用值传递方式 |
& | 函数体内可以使用Lambda范围内所有可见的局部变量,并使用引用传递方式 |
this | 函数体内可以使用Lambda则在类的成员变量 |
a | 按值传递变量a |
&a | 按引用传递变量a |
a, &b | 按值传递a,按引用传递b |
=, &a, &b | 按引用传递a和b,其他参数按值传递 |
&, a, b | 按值传递a和b,其他参数按引用传递 |
mutable修饰符
通用:[=]()mutable{/**/}();
作用:可以修改按值传递进去Lambda中的拷贝(修改拷贝而不是修改值本身)
函数返回值
->
返回值类型
通用:[=]()->返回类型{/**/}();
妙用
可以让Lambda函数充当槽函数,太方便了!!!不需要另外定义信号接受者的槽函数
Qt4兼容问题
Qt4如果需要使用Lambda表达式,要在pro文件中写:CONFIG += C++11
Qt5默认加了这行代码
Qt中sender()函数的用法
你在一个槽里面,调用这个函数,返回的就是你信号来源的对象;
QPushButton *aaaa = new QPushButton(this);
connect(aaaaa, SIGNAL(Click()), this, SLOT(Onaaaaa());
void Onaaaaa(){
QPushButton *ccc = (QPushButton*) sender();
}
这个CCC就是aaaa这个对象来的
Qt中parent()的用法
这个函数是
QObject *QObject::parent() const
作用:返回指向父对象的指针
补充
信号和槽的关系与拓展
- (1) 信号可以连接信号或槽函数
- (2) 同一个信号可以连接多个槽函数
- (3) 多个信号可以连接同一个槽函数
- (4) 信号的参数个数等于或大于槽函数的参数个数,但类型必须一一对应
新旧版本的写法(Qt4旧版本,Qt5向下兼容)
Qt4
- 通用:
connect(信号发送者, 发送的信号SINGNAL(信号), 信号接受者, 槽函数SLOT(槽函数));
- 举例:
connect(zt, SINGAL(hungry(QString)), st, SLOT(treat()));
- 优点:参数直观,需要将信号和槽进行明确的指定
- 缺点:类型不做检测
- 通用:
Qt5
通用:
connect(信号的发送者, 发送的信号, 信号的接受者, 处理的槽函数);
举例:
connect(btn2, &QPushButton::clicked, this, &QWidget::close);
缺点:这里可以看到不需要指定函数参数,这意味着当存在重载的信号槽函数时,需要使用函数指针来指定。例下使用了类型转换
connect(m_pBtn, static_cast<void (MyButton::*)(bool)>(&MyButton::sigClicked), this, &Widget::onClicked);
主要:Qt4和Qt5的写法不能在同一条connect语法中混用
Lambda + 带参:
- https://blog.csdn.net/LittleLittleFish_xyg/article/details/118734081
- https://blog.csdn.net/gz9456/article/details/108776642
connect(list,&QListWidget::currentTextChanged,[&](QString test){qDebug() << "xxxx" << test;});
Qt新
- 通用:
connect(m_pBtn, QOverload<bool>::of(&MyButton::sigClicked),this,&Widget::onClicked);
- 优点:主要针对重载信号的连接做了调整
- 通用:
Lambda 函数写法
- 优点:如果槽函数中的内容比较简单的话,没必要再去单独定义一个槽来连接, 直接用Lambda 函数会更简单
报错:lnk2019无法解析的外部符号
- 原因一
- 解决方法:重新构建 > 执行qmark > 清除 > 运行
- 原因二
- 声明了槽函数但是没有实现