zoukankan      html  css  js  c++  java
  • 界面编程之QT的信号与槽20180725

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

    一、指定父对象

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

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

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

     *            1)setParent

     *            2)通过构造函数传参

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

     */

    1)setParent

    QPushButton b;

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

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

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

    2)通过构造函数传参

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

    b1.setText("abc");

    w.show();

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

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

                      

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

    二、标准信号和槽

    正常新建工程都是选择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是标准的槽函数。

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

    三、自定义槽函数

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

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

     * 槽函数需要和信号一致(指的是参数,返回值一致),所以

     * 由于信号都是没有返回值,所以,槽函数一定没有返回值

     */

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

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

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

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

    四、两个独立的窗口

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

     

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

    五、自定义信号

    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对象之间通信的接口

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

    六、带参数的信号

    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

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

    七、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
  • 相关阅读:
    OpenFire源码学习之十九:在openfire中使用redis插件(上)
    OpenFire源码学习之十八:IOS离线推送
    OpenFire源码学习之十七:HTTP Service插件
    OpenFire源码学习之十六:wildfire
    OpenFire源码学习之十五:插件开发
    OpenFire源码学习之十四:插件管理
    OpenFire源码学习之十三:消息处理
    数据挖掘入门
    iOS小技巧
    图片加载完成之前对图片高度侦测
  • 原文地址:https://www.cnblogs.com/yuweifeng/p/9362644.html
Copyright © 2011-2022 走看看