界面編程之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
相關文章
相關標籤/搜索