界面编程之QT的信号与槽20180725

/*******************************************************************************************/c++

1、指定父对象数组

 

/*若是不指定父对象,对象和对象(窗口和窗口)没有关系,独立app

 * a指定b为它的父对象,a放在b的上面框架

 * 指定父对象,有2种方式:ide

 *            1)setParent函数

 *            2)经过构造函数传参this

 * 指定父对象,只须要父对象显示,上面的子对象自动显示,不须要再去手动去show子对象了spa

 */指针

 

1)setParentcode

QPushButton b;

b.setText("^_^"); //给按钮设置内容,

b.setParent(&w); //指定父对象,若是不指定父对象,这里直接show,那么显示的是两个独立的窗口

b.move(100, 100); //移动坐标,以像素点为单位,以左上角为基准点,若是没有移动,默认位置是在左上角

 

2)经过构造函数传参

QPushButton b1(&w); //经过构造函数传参

b1.setText("abc");

 

w.show();

 

//窗口也是一种控件,按钮也是一种控件,因此这种继承关系就致使了一种叫法,窗口是父对象,同时也是父控件,父对象,

//对应,按钮是子对象,同时也是子控件,子窗口

                  

/*******************************************************************************************/

2、标准信号和槽

正常新建工程都是选择application模版中的qt widgets application

一样须要去掉建立界面的勾选框,继承Qwidget类

 

1.信号和槽的介绍

信号槽是 Qt 框架引觉得豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生以后,好比,

按钮检测到本身被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,相似广播。

若是有对象对这个信号感兴趣,它就会使用链接(connect)函数,意思是,

将想要处理的信号和本身的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被链接的槽函数会自动被回调。

这就相似观察者模式:当发生了感兴趣的事件,某一个操做就会被自动触发。(这里提一句,Qt 的信号槽使用了额外的处理来实现,

并非 GoF 经典的观察者模式的实现方式。)

 

2.信号的含义

所谓信号,就是点击按钮或其余控件后,会产生软中断,产生后会广播发送出信号,若是有谁感兴趣的就会去处理这个信号,

那么如何才能表示感兴趣,就是要创建链接关系,那么这个信号发生的时候,就会自动的调用所链接的函数

也就是说若是对某信号感兴趣,就要有函数和这个信号创建联系,那么这个信号发生的时候,这个函数就会被调用        

 

3.信号相关的函数

创建链接关系函数:

connect(&b1, &QPushButton::pressed, this, &MainWidget::close);

第一个参数是发出信号的控件地址,

第二参数表示的是这个控件发出的是哪一个信号,具体这个信号填什么须要看帮助文档,

  即光标放在按钮类名上按f1就能够出现帮助文档,再f1全屏,在其中找是否有signal或者再到其父类中找signal

第三个参数是信号的接收者,表示的是谁来接收这个信号,也就是谁感兴趣,即信号的处理函数所属于的类的对象,

  当接收者接收到对应的信号(第二个参数)就会自动去调用信号的处理函数,即第四个参数 

第四个参数是该信号的处理函数(槽函数),也就是接收到信号要调用的函数,这个函数是第三个参数的成员函数      

/* &b1: 信号发出者,指针类型

 * &QPushButton::pressed:处理的信号,  &发送者的类名::信号名字

 * this: 信号接收者

 * &MainWidget::close: 槽函数,信号处理函数  &接收的类名::槽函数名字

*/

在成员函数上按f1也会有对应的帮助文档,在帮助文档中能够看到MainWidget::close函数标注了是槽函数,也就是

QPushButton::pressed是标准的信号,MainWidget::close是标准的槽函数。

 

/*******************************************************************************************/

3、自定义槽函数

 

/* 自定义槽,就是普通函数的用法

 * Qt5中,自定义槽能够是:任意的成员函数,普通全局函数,静态函数

 * 槽函数须要和信号一致(指的是参数,返回值一致),因此

 * 因为信号都是没有返回值,因此,槽函数必定没有返回值

 */

connect(b2, &QPushButton::released, this, &MainWidget::mySlot);

//一个信号能够有多个处理 /* 能够打比喻,信号:短信 槽函数:接收短信的手机,一条短信能够发给多个手机*/

connect(b2, &QPushButton::released, &b1, &QPushButton::hide);

 

/*******************************************************************************************/

4、两个独立的窗口

添加新的窗口,就须要添加一个新的类,添加的方法是选择添加c++ class 具体见图1:

 

/*******************************************************************************************/

5、自定义信号

1.自定义信号

//(自)定义信号要在signals关键字后面,signals是qt特有的关键字,编译的时候qt会把它转换到g++能够编译的

signals:

     /* 信号必须有signals关键字来声明

      * 信号没有返回值,但能够有参数

      * 信号就是函数的声明,只需声明,无需定义

      * 使用:emit mySignal();,即发送信号使用emit关键字

      * 信号能够重载

     */

    void mySignal();

    void mySignal(int, QString);

        

2.发送信号     

//发送信号使用emit关键字

    connect(&b, &QPushButton::clicked, this, &SubWidget::sendSlot);//把信号与信号对应的处理函数(槽函数)创建起链接关系

    resize(400, 300);//若是不resize,则每次切换的时候窗口大小会变

void SubWidget::sendSlot()

{

    emit mySignal();

    emit mySignal(250, "我是子窗口");

}

 

//处理子窗口的信号

   connect(&w, &SubWidget::mySignal, this, &MainWidget::dealSub);

   resize(400, 300);//若是不resize,则每次切换的时候窗口大小会变

 

3.注意

qt中用别人的代码时候,注意要去掉.user文件,不然可能编译不过

对话框的特色是不能伸缩的,只有一个x按钮

 

信号与槽:是qt对象之间通讯的接口

 

/*******************************************************************************************/

6、带参数的信号

1.定义带参数的信号

signals:

     /* 信号必须有signals关键字来声明

      * 信号没有返回值,但能够有参数

      * 信号就是函数的声明,只需声明,无需定义

      * 使用:emit mySignal();

      * 信号能够重载

     */

    void mySignal();

    void mySignal(int, QString);

 

2.发送带参数的信号     

void SubWidget::sendSlot()

{

    emit mySignal();

    emit mySignal(250, "我是子窗口");

}

3.信号的重载

1).qt5的处理方法

//当信号出现重载,为了区分具体是属于哪个,就须要用到函数指针,定义函数指针等于具体哪个信号。一样若是槽函数重载也得这么转换

    void (SubWidget::*funSignal)() = &SubWidget::mySignal;

    connect(&subWin, funSignal, this, &MainWidget::dealSub);

    void (SubWidget::*testSignal)(int, QString) = &SubWidget::mySignal;

    connect(&subWin, testSignal, this, &MainWidget::dealSlot);

2).qt4的处理方法 

//还有另一种更方便的方法,只不过容易出问题,也就是接下来要说的:Qt4信号链接。实现功能是同样的

//Qt4信号链接使用宏:SIGNAL。Qt4槽函数必须有slots关键字来修饰,而后再使用宏SLOT

         connect(&subWin, SIGNAL(mySignal()), this, SLOT(dealSub()) );

         connect(&subWin, SIGNAL(mySignal(int,QString)),

                   this, SLOT(dealSlot(int,QString)) );

// 容易出错的地方:SIGNAL SLOT 将函数名字 -> 字符串  不进行错误检查。即写错了信号名字等,编译的时候不报错。因此尽量不用这种qt4的方式

        

//Qt4槽函数必需要有的关键字  

public slots:

    void mySlot();

    void changeWin();

    void dealSub();

    void dealSlot(int, QString);      

void MainWidget::dealSlot(int a, QString str)

{

    // str.toUtf8() -> 字节数组QByteArray

    // ……data()  -> QByteArray -> char *

    qDebug() << a << str.toUtf8().data();//qDebug qt中的打印,相似cout,必须#include <QDebug>

}

 

4.信号槽的更多用法

1).一个信号能够和多个槽相连

若是是这种状况,这些槽会一个接一个的被调用,可是它们的调用顺序是不肯定的。

2).多个信号能够链接到一个槽

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

3).一个信号能够链接到另外的一个信号

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

4).槽能够被取消连接

这种状况并不常常出现,由于当一个对象delete以后,Qt自动取消全部链接到这个对象上面的槽。

具体见图2,3

/*******************************************************************************************/

7、LamDa表达式和再说信号的功能

//Lambda表达式(匿名函数对象)

//C++11增长的新特性, 由于是新增长的因此须要在项目文件(.pro文件): CONFIG += C++11

//在Qt中,配合信号一块儿使用,很是方便。方便在不用定义槽函数,不用指定接收者

1.Lambda表达式(匿名函数对象)

Lambda表达式:

[]()

{

}

//相似于函数只不过没有函数名与返回值,而后多了个[],表示函数的开始

//其中[]内部能够放Lambda表达式外面的变量,这样Lambda表达式里的函数体内才可使用,例如:

         QPushButton *b4 = new QPushButton(this);

         int a = 10, b = 100;

         [b4,a,b]()

         {//这样内部才可使用b4,a,b,不过对于{}内部来讲是只读的,{}内部不能修改这些变量

         }

//若是要把外部全部局部变量、类中全部成员都以值传递方式(传递进来是只读的,表达式内不能修改,除非

  在()后面加关键字mutable : [=]() mutable{})传递到Lambda表达式里的函数体内,则在[]中加入等号=

//若是要把类中全部成员以值传递方式传递到Lambda表达式里的函数体内,则在[]中加入this

//若是要把外部全部局部变量传递到Lambda表达式里的函数体内,则在[]中加入引用符号& 。

  注意&尽可能少用,以下:

  int a = 10, b = 100;

  connect(b4, &QPushButton::clicked,

            [&]()

            {

                qDebug() << a<< b;//内存还没释放(a,b,b4之类的内存还在使用,可能相互影响),引用的内存不是局部变量的,就会出错

                                     a=11;

            }

            );

 

2.Lambda表达式在信号中的使用

Lambda表达式直接代替掉接收者和槽函数,信号产生后,直接执行Lambda表达式

若是信号有参数的情形:

//clicked 信号有参数 bool checked =false,即点击后值变为false

connect(b4, &QPushButton::clicked,

                   // = :把外部全部局部变量、类中全部成员以值传递方式

                   // this: 类中全部成员以值传递方式

                   // & : 把外部全部局部变量, 引用符号

                   [=](bool isCheck)

                   {

                            qDebug() << isCheck;//这样就接收到了信号发出的参数

                   }

                   );

                  

 

根据前面信号与槽的实现,点击按钮后窗口发生变化,窗口发生变化是按钮触发的,可是实质上

按钮只是发送了信号,而后槽函数被调用,真正引发的变化是槽函数作的,也就是成员函数作的。

一样点击按钮切换窗口时,按钮只是触发了软中断让槽函数被调用,而后是槽函数里面发出了信号。

也就是说具体作什么事情由槽函数决定

        

        

信号与槽相关的代码,具体见《SignalAndSlot》:

 1 #ifndef MAINWIDGET_H  2 #define MAINWIDGET_H
 3 
 4 #include <QWidget>
 5 #include <QPushButton>
 6 #include "subwidget.h" //子窗口头文件
 7 
 8 class MainWidget : public QWidget  9 { 10  Q_OBJECT 11 
12 public: 13     MainWidget(QWidget *parent = 0); 14     ~MainWidget(); 15 
16 public slots: 17     void mySlot(); 18     void changeWin(); 19     void dealSub(); 20     void dealSlot(int, QString); 21 
22 private: 23  QPushButton b1; 24     QPushButton *b2; 25  QPushButton b3; 26 
27  SubWidget subWin; 28 }; 29 
30 #endif // MAINWIDGET_H
mainwidget.h
 1 #include "mainwidget.h"
 2 #include <QPushButton>
 3 #include <QDebug> //打印
 4 
 5 MainWidget::MainWidget(QWidget *parent)  6  : QWidget(parent)  7 {  8     b1.setParent(this);  9     b1.setText("close");  10     b1.move(100, 100);  11 
 12     b2 = new QPushButton(this);  13     b2->setText("abc");  14 
 15     connect(&b1, &QPushButton::pressed, this, &MainWidget::close);  16     /* &b1: 信号发出者,指针类型  17  * &QPushButton::pressed:处理的信号, &发送者的类名::信号名字  18  * this: 信号接收者  19  * &MainWidget::close: 槽函数,信号处理函数 &接收的类名::槽函数名字  20     */
 21 
 22     /* 自定义槽,普通函数的用法  23  * Qt5:任意的成员函数,普通全局函数,静态函数  24  * 槽函数须要和信号一致(参数,返回值)  25  * 因为信号都是没有返回值,因此,槽函数必定没有返回值  26      */
 27     connect(b2, &QPushButton::released, this, &MainWidget::mySlot);  28 
 29     connect(b2, &QPushButton::released, &b1, &QPushButton::hide);  30 
 31     /* 信号:短信  32  * 槽函数:接收短信的手机  33      */
 34 
 35     setWindowTitle("老大");  36     //this->setWindowTitle("老大");
 37 
 38     b3.setParent(this);  39     b3.setText("切换到子窗口");  40     b3.move(50, 50);  41 
 42     //显示子窗口  43     //subWin.show();
 44 
 45     connect(&b3, &QPushButton::released, this, &MainWidget::changeWin);  46 
 47 
 48     //处理子窗口的信号  49 // void (SubWidget::*funSignal)() = &SubWidget::mySignal;  50 // connect(&subWin, funSignal, this, &MainWidget::dealSub);  51 
 52 // void (SubWidget::*testSignal)(int, QString) = &SubWidget::mySignal;  53 // connect(&subWin, testSignal, this, &MainWidget::dealSlot);  54 
 55     //Qt4信号链接  56     //Qt4槽函数必须有slots关键字来修饰
 57     connect(&subWin, SIGNAL(mySignal()), this, SLOT(dealSub()) );  58 
 59     connect(&subWin, SIGNAL(mySignal(int,QString)),  60             this, SLOT(dealSlot(int,QString)) );  61     // SIGNAL SLOT 将函数名字 -> 字符串 不进行错误检查  62 
 63     //Lambda表达式, 匿名函数对象  64     //C++11增长的新特性, 项目文件: CONFIG += C++11  65     //Qt配合信号一块儿使用,很是方便
 66 
 67     QPushButton *b4 = new QPushButton(this);  68     b4->setText("Lambda表达式");  69     b4->move(150, 150);  70     int a = 10, b = 100;  71     connect(b4, &QPushButton::clicked,  72             // = :把外部全部局部变量、类中全部成员以值传递方式  73             // this: 类中全部成员以值传递方式  74             // & : 把外部全部局部变量, 引用符号
 75             [=](bool isCheck)  76  {  77                 qDebug() << isCheck;  78  }  79 
 80 
 81  );  82 
 83 
 84     resize(400, 300);  85 }  86 
 87 void MainWidget::dealSlot(int a, QString str)  88 {  89     // str.toUtf8() -> 字节数组QByteArray  90     // ……data() -> QByteArray -> char *
 91     qDebug() << a << str.toUtf8().data();  92 }  93 
 94 void MainWidget::mySlot()  95 {  96     b2->setText("123");  97 }  98 
 99 void MainWidget::changeWin() 100 { 101     //子窗口显示
102  subWin.show(); 103     //本窗口隐藏
104     this->hide(); 105 } 106 
107 
108 void MainWidget::dealSub() 109 { 110     //子窗口隐藏
111  subWin.hide(); 112     //本窗口显示
113  show(); 114 } 115 
116 MainWidget::~MainWidget() 117 { 118 
119 }
mainwidget.cpp
 1 #ifndef SUBWIDGET_H  2 #define SUBWIDGET_H
 3 
 4 #include <QWidget>
 5 #include <QPushButton>
 6 
 7 class SubWidget : public QWidget  8 {  9  Q_OBJECT 10 public: 11     explicit SubWidget(QWidget *parent = 0); 12 
13     void sendSlot(); 14 
15 signals: 16      /* 信号必须有signals关键字来声明 17  * 信号没有返回值,但能够有参数 18  * 信号就是函数的声明,只需声明,无需定义 19  * 使用:emit mySignal(); 20  * 信号能够重载 21      */
22 
23     void mySignal(); 24     void mySignal(int, QString); 25 
26 public slots: 27 
28 private: 29  QPushButton b; 30 }; 31 
32 #endif // SUBWIDGET_H
subwidget.h
 1 #include "subwidget.h"
 2 
 3 SubWidget::SubWidget(QWidget *parent) : QWidget(parent)  4 {  5     this->setWindowTitle("小弟");  6     b.setParent(this);  7     b.setText("切换到主窗口");  8 
 9     connect(&b, &QPushButton::clicked, this, &SubWidget::sendSlot); 10 
11     resize(400, 300); 12 } 13 
14 void SubWidget::sendSlot() 15 { 16  emit mySignal(); 17     emit mySignal(250, "我是子窗口"); 18 }
subwidget.cpp
相关文章
相关标签/搜索