QT信号和槽_lambda表达式

信号和槽

connect(sender,signal,receiver,slot)参数均为指针

  1. sender:发出者
  2. signal:信号
  3. receiver:接受者
  4. slot:执行的任务(槽函数)

常见信号槽的查阅

在帮助文档检索对应类,找到signal/slot查询
image.png
没有signal可能在其基类中
image.png

自定义信号和槽

自定义信号

在 Qt 中信号的本质是事件,但是在框架中也是以函数的形式存在的,只不过信号对应的函数只有声明,没有定义。如果 Qt 中的标准信号不能满足我们的需求,可以在程序中进行信号的自定义,当自定义信号对应的事件产生之后,认为的将这个信号发射出去即可(其实就是调用一下这个信号函数)。

自定义信号的要求和注意事项:

  1. 信号是类的成员函数
  2. 返回值必须是 void 类型
  3. 信号的名字可以根据实际情况进行指定
  4. 参数可以随意指定, 信号也支持重载
  5. 信号需要使用 signals 关键字进行声明, 使用方法类似于public等关键字 直接在.h文件中写声明即可
  6. 信号函数只需要声明, 不需要定义(没有函数体实现)
  7. 在程序中发射自定义信号: 发送信号的本质就是调用信号函数
  8. 习惯性在信号函数前加关键字: emit, 但是可以省略不写
  9. emit只是显示的声明一下信号要被发射了, 没有特殊含义
  10. 底层 emit == #define emit
class Test : public QObject
{
    Q_OBJECT
signals:
    void testsignal();
	// 参数的作用是数据传递, 谁调用信号函数谁就指定实参
	// 实参最终会被传递给槽函数
    void testsignal(int a);
};

自定义槽

槽函数就是信号的处理动作,在 Qt 中槽函数可以作为普通的成员函数来使用。如果标准槽函数提供的功能满足不了需求,可以自己定义槽函数进行某些特殊功能的实现。自定义槽函数和自定义的普通函数写法是一样的。

下边给大家阐述一下, 自定义槽的要求和注意事项:

  1. 返回值必须是 void 类型
  2. 槽也是函数, 因此也支持重载
  3. 槽函数需要指定多少个参数, 需要看连接的信号的参数个数
  4. 槽函数的参数是用来接收信号传递的数据的, 信号传递的数据就是信号的参数

举例:
信号函数: void testsig (int a, double b);
槽函数: void testslot (int a, double b);

总结:
槽函数的参数应该和对应的信号的参数个数,从左到右类型依次对应
信号的参数可以大于等于槽函数的参数个数 == 信号传递的数据被忽略了
信号函数: void testsig (int a, double b);
槽函数: void testslot (int a);
Qt中槽函数的类型是多样的:
Qt 中的槽函数可以是类的成员函数、全局函数、静态函数、Lambda表达式(匿名函数)
槽函数可以使用关键字进行声明: slots (Qt5中slots可以省略不写)
public slots:
private slots: –> 这样的槽函数不能在类外部被调用
protected slots: –> 这样的槽函数不能在类外部被调用

connect第五个参数

  • Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。
    • 接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。
    • 接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
  • **Qt::DirectConnection:**槽函数会在信号发送的时候直接被调用,槽函数和信号发送者在同一线程。效果看上去就像是直接在信号发送位置调用了槽函数,效果上看起来像函数调用,同步执行。
    • emit语句后面的代码将在与信号关联的所有槽函数执行完毕后才被执行。
    • 无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。
  • **Qt::QueuedConnection:**信号发出后,信号会暂时被放到一个消息队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作。
    • emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕
    • 槽函数在接收者所依附线程执行。
  • **Qt::BlockingQueuedConnection:**槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。而且接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
  • **Qt::UniqueConnection:**这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是为了避免重复连接。

一般qt connect的第五个参数会在多线程中运用到,需要注意的是:
QThread是用来管理线程的,QThread对象所依附的线程和所管理的线程并不是同一个概念。QThread所依附的线程,就是创建QThread对象的线程;QThread 所管理的线程,就是run启动的线程,也就是新建线程。
例如:QThread对象依附在主线程中,QThread对象的slot函数会在主线程中执行,而不是次线程。除非QThread对象依附到次线程中(通过movetoThread)。

拓展

  1. 一个信号可以连接多个槽函数,发送一个信号有多个处理动作
    1. 需要写多个 connect()连接
    2. 槽函数的执行顺序是随机的,和 connect 函数的调用顺序没有关系
    3. 信号的接收者可以是一个对象,也可以是多个对象
  2. 一个槽函数可以连接多个信号,多个不同的信号,处理动作是相同的
    1. 需要写多个 connect()连接
  3. 信号可以连接信号
connect(const QObject *sender, &QObject::signal, 
        const QObject *receiver, &QObject::signal-new);

即sender发出的signal信号可以让receiver发出signal-new信号。

  1. 信号可以断开

disconnect(const QObject *sender, &QObject::signal, const QObject *receiver, &QObject::method);

  1. 信号和槽函数如果发生重载,那么connect函数参数传递函数地址就不知道绑定哪个函数的地址,有两种解决办法

假设Teacher的hungry方法重载两个,其中有一个是QString类型参数:

//1.函数指针赋值,让编译器挑选符合类型的函数
void (Teacher::*teacher_qstring)(QString) = &Teacher:hungry; //定义一个带参类型返回值为void的函数指针
connect(teacher,teacher_qstring,stu,Student::treat);  //传入函数指针

//2.使用static_cast强制转换,也就是让编译器自动挑选
connect(teacher,  /
		static_cast<void (Teacher::*teacher_qstring)(QString)>(&Teacher:hungry),/
		stu,/
		Student::treat);/
// buttonClicked信号重载,选择参数带int的那个
connect(buttonGroup,QOverload<int>::of(&QButtonGroup::buttonClicked),
        this,&MainWindow::buttonGroupClicked);

lambda表达式

[capture](params) opt -> ret {body;};
    - capture: 捕获列表
    - params: 参数列表
    - opt: 函数选项
    - ret: 返回值类型
    - body: 函数体

  1. 捕获列表:捕获一定范围内的变量
    1. [] - 不捕捉任何变量
    2. [&] - 捕获外部作用域中所有变量,并作为引用在函数体内使用 (按引用捕获)
    3. [=] - 捕获外部作用域中所有变量,并作为副本在函数体内使用 (按值捕获) 建议使用
      1. 拷贝的副本在匿名函数体内部是只读的,除非在opt部分写mutable
    4. [=, &foo] - 按值捕获外部作用域中所有变量,并按照引用捕获外部变量 foo
    5. [bar] - 按值捕获 bar 变量,同时不捕获其他变量
    6. [&bar] - 按引用捕获 bar 变量,同时不捕获其他变量
    7. [this] - 捕获当前类中的 this 指针
      1. 让 lambda 表达式拥有和当前类成员函数同样的访问权限
      2. 如果已经使用了 & 或者 =, 默认添加此选项
  2. 参数列表:和普通函数的参数列表一样
  3. opt 选项 –> 可以省略
    1. mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
    2. exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw ();
  4. 返回值类型:
    1. 标识函数返回值的类型,当返回值为 void,或者函数体中只有一处 return 的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略,除非多次指定。
  5. 函数体:
    1. 函数的实现,这部分不能省略,但函数体可以为空

QTimer自动发出信号

在进行窗口程序的处理过程中,经常要周期性的执行某些操作,或者制作一些动画效果,看似比较复杂的问题使用定时器就可以完美的解决这些问题, Qt 中提供了两种定时器方式一种是使用 Qt 中的事件处理函数这个在后续章节会给大家做细致的讲解,本节主要给大家介绍一下 Qt 中的定时器类 QTimer 的使用方法。

要使用它,只需创建一个 QTimer 类对象,然后调用其 start() 函数开启定时器,此后 QTimer 对象就会周期性的发出 timeout() 信号。我们先来了解一下这个类的相关 API。

// 构造函数
// 如果指定了父对象, 创建的堆内存可以自动析构
QTimer::QTimer(QObject *parent = nullptr);

// 设置定时器时间间隔为 msec 毫秒
// 默认值是0,一旦窗口系统事件队列中的所有事件都已经被处理完,一个时间间隔为0的QTimer就会触发
void QTimer::setInterval(int msec);
// 获取定时器的时间间隔, 返回值单位: 毫秒
int QTimer::interval() const;

// 根据指定的时间间隔启动或者重启定时器, 需要调用 setInterval() 设置时间间隔
[slot] void QTimer::start();
// 启动或重新启动定时器,超时间隔为msec毫秒,这里参数可以直接设置interval
[slot] void QTimer::start(int msec);
// 停止定时器。
[slot] void QTimer::stop();

// 设置定时器精度
/*
参数: 
    - Qt::PreciseTimer -> 精确的精度, 毫秒级
    - Qt::CoarseTimer  -> 粗糙的精度, 和1毫秒的误差在5%的范围内, 默认精度
    - Qt::VeryCoarseTimer -> 非常粗糙的精度, 精度在1秒左右
*/
void QTimer::setTimerType(Qt::TimerType atype);
Qt::TimerType QTimer::timerType() const;	// 获取当前定时器的精度

// 如果定时器正在运行,返回true; 否则返回false。
bool QTimer::isActive() const;

// 判断定时器是否只触发一次
bool QTimer::isSingleShot() const;
// 设置定时器是否只触发一次, 参数为true定时器只触发一次, 为false定时器重复触发, 默认为false
void QTimer::setSingleShot(bool singleShot);

这个类的信号只有一个,当定时器超时时,该信号就会被发射出来。给这个信号通过 conect() 关联一个槽函数,就可以在槽函数中处理超时事件了。
[signal] void QTimer::timeout();

一次性定时器

// 其他同名重载函数可以自己查阅帮助文档
/*
功能: 在msec毫秒后发射一次信号, 并且只发射一次
参数:
	- msec:     在msec毫秒后发射信号
	- receiver: 接收信号的对象地址
	- method:   槽函数地址
*/
[static] void QTimer::singleShot(
        int msec, const QObject *receiver, 
        PointerToMemberFunction method);
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值