qt信号和槽

2024-01-07 17:11:06

Qt是一个跨平台的C++图形用户界面应用框架? 91年奇趣科技开发

pro工程文件介绍? ??.pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件

QT       += core gui   //Qt包含的模块

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于4版本包含 widgets模块

TARGET = Qt_First   //生成exe文件的名称
TEMPLATE = app      //应用程序模板


SOURCES += main.cpp\    //源文件
        mywidget.cpp
	
HEADERS  += mywidget.h  //头文件

安装和创建文件方法在上一章已经解决,接下来就是说具体知识总结

其他知识:命名规范

运行 ctrl+r

编译 ctrl+b

注释 ctrl+/

字体缩放 ctrl+鼠标滚轮

整行移动 ctrl+shift+上下箭头

自动对齐 ctrl+i

同名的h和cpp切换 F4

查找关键字 ctrl+f

帮助文档 直接找qt文档,或者左边有帮助

?在Qt程序中,最常用的控件之一就是按钮了,首先我们来看下如何创建一个按钮

按钮 PushButton

QPushButton * btn = new QPushButton; 头文件 #include <QPushButton>

    //设置父亲
    //如果想显示时候依赖在当前的窗口中,需要设置父窗
    btn->setParent(this);
    //设置文字
    btn->setText("德玛西亚");
    //移动位置
    btn->move(100,100);

    //第二种创建
    QPushButton * btn2 = new QPushButton("孙悟空",this);
    //重新指定窗口大小
    this->resize(600,400);

    //设置窗口标题
    this->setWindowTitle("第一个项目");

    //限制窗口大小
    this->setFixedSize(600,400);//就不能拉伸了

QPushButton----------QAbstractButton-----------QWidget? 关系为孙父爷关系

上面代码中,一个按钮其实就是一个QPushButton类下的对象,如果只是创建出对象,是无法显示到窗口中的,所以我们需要依赖一个父窗口,也就是指定一个父亲利用setParent函数即可,如果想设置按钮上显示的文字利用setText,移动按钮位置用move

对于窗口而言,我们可以修改左上角窗口的标题setWindowTitle,重新指定窗口大小:resize,或者设置固定的窗口大小setFixedSize

对象树

如图所示,我们言简意赅? 树的创建是由上向下,释放时是由下向上,很丝滑

但你如果自己做一个析构函数,你会发现析构顺序和销毁顺序不同

析构函数,先调用父亲但是不执行(就是找到),再调用儿子,执行

Qt窗口坐标体系

坐标体系:

以左上角为原点(0,0),X向右增加,Y向下增加。

对于嵌套窗口,其坐标是相对于父窗口来说的。

?信号和槽机制

信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

举例1,人 2.摩擦 3.神灯 4.出神灯   
要做一个连接 将12与34连接起来 结果可以为人在摩擦---神灯后可以出神灯
1.信号发送者(指针)  2.发送的信号 地址 3.信号的接收者 指针 4.处理的槽函数 地址、
connect(myBtn,&MyPushButton::clicked,this,&MyWidget::close);

这个是用好封装的函数调用的地址,用qt帮助文档查找的

?

以下属于自定义信号和槽

widget.h中  
//设置老师和学生
   Teacher * zt;
???Student * st;

teacher.h中

    //自定义信号  写到signal下
    //1.返回值是void
    //2.只需要声明,不需要实现
    //3.可以有参数,可以发生重载
signals:
    void hungry();//空的
    void hungry(QString foodName);//传参
student.h中
signals: 
  //自定义
//槽函数 写到public slots下,或者public下,或者全局函数,或者lambda
//返回值 void
//需要声明 需要实现
//可以有参数 可以重载
public slots:
   void treat();
   void treat(QString foodName);
};

?student.cpp中

void Student::treat()//加作用域
{
    qDebug()<<"请老师吃饭";
}
void Student::treat(QString foodName)//有参传递
{
    //QString转为char *
    //先调用toUtf-8转为QByteArray
    //再调用data 转为 char*
    qDebug()<<"请老师吃饭,老师要吃:  "<<foodName.toUtf8().data();//消去引号
}

ps:teacher.cpp中不用添加修改什么

widget.cpp中? 这里是重点,反复看里面的代码

#include "widget.h"
#include "QPushButton"
#include "QDebug"
//Teacher老师类  Student学生类
//下课后,老师会发出饿了的信号,学生进行响应,请老师吃饭

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->zt=new Teacher(this);//在窗口中连接信号槽
    this->st=new Student(this);

    //连接信号和槽
//    connect(this->zt,&Teacher::hungry,this->st,&Student::treat);

    //连接有参的信号和槽
    //函数指针指向函数地址
//    void(Teacher:: * teacherSignal)(QString)=&Teacher::hungry;
//    void(Student:: * studentSlot)(QString)=&Student::treat;
//    connect(this->zt,teacherSignal,this->st,studentSlot);

    //按键
    QPushButton *btn=new QPushButton;
    btn->setParent(this);
    btn->setText("下课");

    //无参
        void(Teacher:: * teacherSignal2)()=&Teacher::hungry;
        void(Student:: * studentSlot2)()=&Student::treat;
        connect(this->zt,teacherSignal2,this->st,studentSlot2);
//      老师饿了(--学生请客)

     //信号和槽拓展
     //1.信号连接信号                                //无参
     connect(btn,&QPushButton::clicked,this->zt,teacherSignal2);
//                              bool        >=       0
     //连接方式  点击按钮--老师饿了(--学生请客)

     //2.一个信号可以连接多个槽函数
     //3.多个信号可以绑定同一个槽函数
    //4.信号和槽的参数类型,必须是一一对应  传的信号是宫保鸡丁,接受的也必须是宫保鸡丁QString--QSt
     //5.信号的参数个数可以多于槽函数,反之不可以   例如(QString,int)---(QString)正确
     //6.相同个数的参数类型也要一一对应
     //7.可以利用disconnect断开信号槽的连接
//  disconnect(btn,&QPushButton::clicked,this->zt,teacherSignal2);断前面
//  disconnect(this->zt,teacherSignal2,this->st,studentSlot2);断后面
    //下课函数调用
//    classISOver();//连接作用


     //lanbda表达式
     QPushButton *btn2=new QPushButton;
     btn2->move(100,0);
     btn2->setParent(this);
     btn2->setText("aaa");
    //函数体内可以使用lambda所在范围内所有可见的局部变量
     [=](){
         btn2->setText("bbb");
     }();
     //->返回值类型
     int num=[]()->int{return 1000;}();
     qDebug()<<"num"<<num;
     //[=](){}推荐使用

     QPushButton * btn3=new QPushButton;
     btn3->setParent(this);
     btn3->move(200,200);
     btn3->setText("aaa");

     connect(btn3,&QPushButton::clicked,this,[=]()//优化了上面信号连接信号的有参
     {
         btn3->setText("ccc");
        this->st->treat("鱼香肉丝");//直接调槽函数   在connect中调另一个函数
         this->close();

     });//用=正确(复制一个,间接改)用&错误!(直接修改)


}
void Widget::classISOver(){
    //触发自定义信号
    emit this->zt->hungry();

    emit this->zt->hungry("宫保鸡丁");
}
Widget::~Widget()
{
}

挑里面两个重点代码说说

这里是无参信号连接槽
  this->zt=new Teacher(this);//在窗口中连接信号槽
    this->st=new Student(this);

    //连接信号和槽
 connect(this->zt,&Teacher::hungry,this->st,&Student::treat);

这里是有参信号连接槽

//连接有参的信号和槽
    //函数指针指向函数地址
 void(Teacher:: * teacherSignal)(QString)=&Teacher::hungry;
  void(Student:: * studentSlot)(QString)=&Student::treat;
  connect(this->zt,teacherSignal,this->st,studentSlot);

注意:题目前提是

下课后,老师会发出饿了的信号,学生进行响应,请老师吃饭

所以添加一个触发信号,下课了,老师肚子饿了,学生请客吃饭,符合逻辑。

void Widget::classISOver(){
    //触发自定义信号
    emit this->zt->hungry();

    emit this->zt->hungry("宫保鸡丁");
}

信号连接信号? ? connect代码,点一下按钮,连接老师饿了,回到上面几行,老师饿了,学生请客

    //无参
        void(Teacher:: * teacherSignal2)()=&Teacher::hungry;
        void(Student:: * studentSlot2)()=&Student::treat;
        connect(this->zt,teacherSignal2,this->st,studentSlot2);
//      老师饿了(--学生请客)

     //信号和槽拓展
     //1.信号连接信号                                //无参
     connect(btn,&QPushButton::clicked,this->zt,teacherSignal2);
//                              bool        >      0
     //连接方式  点击按钮--老师饿了(--学生请客)

这里只能是无参调用,因为clicked为bool类型,如果后面teacherSignal2得到了参数? ?如:

emit this->zt->hungry("宫保鸡丁");

connect(btn,&QPushButton::clicked,this->zt,teacherSignal2);将此代码理解为clicked是bool类型为1,是信号,teacherSingnal2为槽函数,当传入参数时QString(宫保鸡丁)就为失败

因为

   1一个信号可以连接多个槽函数    
?  2.多个信号可以绑定同一个槽函数  
 3.信号和槽的参数类型,必须是一一对应  传的信号是宫保鸡丁,接受的也必须是宫保鸡丁QString--QString
 4.信号的参数个数可以多于槽函数,反之不可以   例如(QString,int)---(QString)正确              5.相同个数的参数类型也要一一对应     
6.可以利用disconnect断开信号槽的连接

自定义信号槽需要注意的事项

  1. 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
  2. 信号和槽函数返回值是 void
  3. 信号只需要声明,不需要实现
  4. 槽函数需要声明也需要实现
  5. 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
  6. 使用 emit 在恰当的位置发送信号;
  7. 使用connect()函数连接信号和槽。
  8. 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
  9. 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
  10. 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。

信号槽的拓展

  1. 一个信号可以和多个槽相连

如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。

  1. 多个信号可以连接到一个槽

只要任意一个信号发出,这个槽就会被调用

  1. 一个信号可以连接到另外的一个信号

当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。

  1. 槽可以被取消链接

这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽

  1. 信号槽可以断开

利用disconnect关键字是可以断开信号槽的

  1. 使用Lambda 表达式

在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。

在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。后面我们会详细介绍什么是Lambda表达式

?

那人问了,那该怎么办呢?

lambda函数?

?引入lambda函数? ? ? ? ?Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。

[capture](parameters) mutable ->return-type//Lambda表达式的基本构成

{

statement

}

[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}

函数对象参数;

[],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式? ? ? ? ??

  1. 空。没有使用任何函数对象参数。
  2. =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。

????????3.&。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。

?那么如何实现lambda? ?实现信号连接信号有参呢?

     //lanbda表达式
     QPushButton *btn2=new QPushButton;
     btn2->move(100,0);
     btn2->setParent(this);
     btn2->setText("aaa");
    //函数体内可以使用lambda所在范围内所有可见的局部变量
     [=](){
         btn2->setText("bbb");
     }();
     //->返回值类型
     int num=[]()->int{return 1000;}();
     qDebug()<<"num"<<num;
     //[=](){}推荐使用

     QPushButton * btn3=new QPushButton;
     btn3->setParent(this);
     btn3->move(200,200);
     btn3->setText("aaa");

     connect(btn3,&QPushButton::clicked,this,[=]()//优化了上面信号连接信号的有参
     {
         btn3->setText("ccc");
        this->st->treat("鱼香肉丝");//直接调槽函数   在connect中调另一个函数
         this->close();

     });//用=正确(复制一个,间接改)用&错误!(直接修改)


}

精华代码在这


? ? ?connect(btn3,&QPushButton::clicked,this,[=]()//优化了上面信号连接信号的有参
? ? ?{
? ? ? ? ?btn3->setText("ccc");
? ? ? ? this->st->treat("鱼香肉丝");//直接调槽函数 ? 在connect中调另一个函数
? ? ? ? ?this->close();

? ? ?});//用=正确(复制一个,间接改)用&错误!(直接修改)

直接调槽函数 ? 在connect中调另一个函数 ,槽函数也是函数,所以在connect中直接调用

connect中加了一个lambda函数

另外有一个重点:

在lambda[]用=正确(复制一个,间接改)用&错误!(直接修改)

完结,收工?

文章来源:https://blog.csdn.net/weixin_61847358/article/details/135393565
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。