跳至主要內容

Qt

LincZero大约 6 分钟

Qt

目录

Qt信号和槽机制**【重点】**

简概(emit、信号、槽、Connect)

  • 信号槽优点 / 特点
    • 类型安全。需要信号和槽的参数类型和个数相同,或后者少于前者的后几个参数
    • 松散耦合。即信号发送端换接收端本身没有关联,通过connect连接,将两端耦合在一起
    • connect只是对象发送信号和处理函数这两者,本质也是类似于js/Android的控件对象绑定方法,但写法不同
  • 使用
    • 通用:connect(信号的发送者, 发送的信号, 信号的接受者, 处理的槽函数);
    • 举例:connect(btn2, &QPushButton::clicked, this, &QWidget::close);
    • 补充:发送的信号通过查手册的Signals,槽函数查手册的Public Slots
    • 补充:如果第三个参数是this,则可以省略
    • 补充:注意参数是函数名而不是调用函数
  • 剖析
    • 为什么第二个和第四个参数需要加&符号和作用域解析运算符::
    • connect的第二四个参数分别为const char *signalconst 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向下兼容)

参考:【CSDN】Qt connect函数的几种用法open in new window

  • 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 > 清除 > 运行
  • 原因二
    • 声明了槽函数但是没有实现