自定義信號和槽
信號和槽的本質都是函數。
區別:
信號必須由signal關鍵字來聲明
信號可以重載
信號沒有返回值,但可以有參數。由於信號都是沒有返回值,所以,槽函數一定沒有返回值
信號就是函數的聲明,只需聲明,無需定義。槽既要函數聲明也要完成定義。
使用方式:emit MySignal();
例子說明:本例創建兩個獨立的窗口,分別在窗口中放置一個按鈕,由此來進行兩個窗口的切換。如:“切換到子窗口”,即子窗口顯示,主窗口隱藏。反之亦然。
按照之前的方式創建程序,另外在項目中“添加新文件”->“C++ class”,名為csubwnd。即創建兩個類,作為主窗口(widget)和子窗口(csubwnd)。
思路:
創建兩個類,主窗口類來處理窗口的切換,本來只顯示窗口,當收到子窗口發出的信號后,立即顯示子窗口並隱藏主窗口。而子窗口類只負責發出信號。
自定義信號
一定要在Signal中聲明(頭文件中)。
signals:
void MySignal();
使用方式:
connect(&subPush,&QPushButton::released,this,&CSubWnd::MySlot);
void CSubWnd::MySlot()
{
emit MySignal();
}
其中MySlot()是自定義槽函數(具體見上一節內容)。emit MySignal()代表發出信號。
解析:當按鈕subPush被釋放時,this(本窗口)立即調用槽函數(MySlot)發出信號(MySignal)。由此可以看到,信號只需聲明無需定義,槽函數既要聲明也要定義。
主窗口的信號處理:
void Widget::Base()
{
//主窗口隱藏
this->hide();
//子窗口顯示
subWnd.show();
}
void Widget::Sub()
{
//主窗口顯示
this->show();
//子窗口隱藏
subWnd.hide();
}
{
//激活槽函數,切換到子窗口
connect(&push,&QPushButton::released,this,&Widget::Base);
//接受子窗口的信號,切換到主窗口
connect(&subWnd,&CSubWnd::MySignal,this,&Widget::Sub);
}
其中,push代表“切換到子窗口”按鈕,subWnd代表“切換到主窗口”的按鈕,MySignal是子窗口發出的信號。Base和Sub是兩個用於切換窗口的槽函數。
帶參數的信號
前面說過信號啊可以重載,下面重載一個帶參數的信號。
信號聲明:
signals:
//自擬信號
void MySignal();
void MySignal(int ,QString);
調用信號:
void CSubWnd::MySlot()
{
//發出自擬信號
emit MySignal();
emit MySignal(2018,"I am learnning Qt,我學習QT");
}
槽函數定義:
void Widget::TextSignal(int nNum,QString strChar)
{
qDebug()<<nNum<<strChar;
}
注意:信號和槽函數的參數必須一致,如信號void MySignal(int ,QString),即槽函數TextSignal也必須兩個參數,且為類型為int和Qstring。
補充:
qDebug()是輸出函數,類似於C++中的cout。qDebug()的頭文件是<QDebug>,但使用時要注意qDebug(),q是小寫,並且帶上括號。
信號與槽函數的使用:
//Qt5版本帶參數的信號
void (CSubWnd::*NoSub)()=&CSubWnd::MySignal;
connect(&subWnd,NoSub,this,&Widget::Sub);
void (CSubWnd::*Sub)(int,QString)=&CSubWnd::MySignal;
connect(&subWnd,Sub,this,&Widget::TextSignal);
問題:為什么要使用函數指針,而不是直接信號本身,如下:
connect(&subWnd, &CSubWnd::MySignal,this,&Widget::Sub);
connect(&subWnd, &CSubWnd::MySignal,this,&Widget:: TextSignal);
原因:
在前面的信號聲明。 void MySignal();void MySignal(int ,QString);使用的是重載,如果直接信號名,會產生二義性,編譯器無法獲取發出的信號是哪一個?帶參數還是不帶參數。
當然,也有一種簡便方式.
//Qt4版本,與Qt5使用函數指針效果等價
connect(&subWnd,SIGNAL(MySignal()),this,SLOT(Sub()));
connect(&subWnd,SIGNAL(MySignal(int,QString)),this,SLOT(TextSignal(int,QString)));
注意:如果使用Qt4版本。SINAL,SLOT都是宏。
- SINAL,SLOT將函數名字轉換成字符串,不進行錯誤檢查,如:SIGNAL(MySigl()),拼寫錯誤了,編譯器也會通過,但是會在執行的過程中才中斷報錯。
- 槽函數必須使用slots關鍵字來聲明,否則無法識別,會報出無法尋查該函數。
public slots:
void Base(); //切換到子窗口
void Sub(); //切換到主窗口
void TextSignal(int ,QString); //測試帶參數信號
補充:輸出編碼問題
qDebug()<<nNum<<strChar;
當string=“我學習QT",可能會輸出\u6211\u662F\u5B32\u3291.二進制編碼
解決方法:
qDebug()<<nNum<<strChar.toUtf8().data();
解析
strChar.toUtf8()->字節數組QbyteArray
…data()->QbyteArray->char*.
此時就可以輸出中文了。也可以是其他類型的編碼。可以在qt編譯器->工具->選項->文本編輯器.可以查看當前自己的編碼類型。
源代碼:
csubwnd.h
#ifndef CSUBWND_H
#define CSUBWND_H
#include <QWidget>
#include <QPushButton>
class CSubWnd : public QWidget
{
Q_OBJECT
public:
explicit CSubWnd(QWidget *parent = 0);
//發出信號的槽函數
void MySlot();
signals:
//自擬信號
void MySignal();
void MySignal(int ,QString);
public slots:
private:
//按鈕
QPushButton subPush;
};
#endif // CSUBWND_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPushButton>
#include "csubwnd.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
public slots:
void Base(); //切換到子窗口
void Sub(); //切換到主窗口
void TextSignal(int ,QString); //測試帶參數信號
private:
Ui::Widget *ui;
QPushButton push;
//子窗口類
CSubWnd subWnd;
};
#endif // WIDGET_H
csubwnd.cpp
#include "csubwnd.h"
CSubWnd::CSubWnd(QWidget *parent) :
QWidget(parent)
{
this->setWindowTitle("子窗口");
subPush.setParent(this);
subPush.setText("切換到主窗口");
this->show();
//點擊按鈕后,利用槽函數發出信號
connect(&subPush,&QPushButton::released,this,&CSubWnd::MySlot);
resize(400,300);
}
void CSubWnd::MySlot()
{
//發出自擬信號
emit MySignal();
emit MySignal(2018,"I am learnning Qt,我學習QT");
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("主窗口");
push.setText("切換到子窗口");
push.setParent(this);
//激活槽函數,切換到子窗口
connect(&push,&QPushButton::released,this,&Widget::Base);
//接受子窗口的信號,切換到主窗口
// connect(&subWnd,&CSubWnd::MySignal,this,&Widget::Sub);
//Qt5版本帶參數的信號
//void (CSubWnd::*NoSub)()=&CSubWnd::MySignal;
//connect(&subWnd,NoSub,this,&Widget::Sub);
//void (CSubWnd::*Sub)(int,QString)=&CSubWnd::MySignal;
//connect(&subWnd,Sub,this,&Widget::TextSignal);
//Qt4版本,與Qt5使用函數指針效果等價
connect(&subWnd,SIGNAL(MySignal()),this,SLOT(Sub())); connect(&subWnd,SIGNAL(MySignal(int,QString)),this,SLOT(TextSignal(int,QString)));
//設置窗口大小
resize(400,300);
}
Widget::~Widget()
{
delete ui;
}
void Widget::Base()
{
//主窗口隱藏
this->hide();
//子窗口顯示
subWnd.show();
}
void Widget::Sub()
{
//主窗口顯示
this->show();
//子窗口隱藏
subWnd.hide();
}
void Widget::TextSignal(int nNum,QString strChar)
{
qDebug()<<nNum<<strChar.toUtf8().data();
}