文章目錄
- 對象樹
- 字符集
- 信號槽
- QT坐標系
- 信號與槽
- connect
- 自定義槽
- 自定義信號
- disconnect
對象樹
#ifndef MYLABEL_H
#define MYLABEL_H#include<QLabel>
class MyLabel : public QLabel
{
public:// 構造函數使用帶 QWidget* 版本的.// 確保對象能夠加到對象樹上MyLabel(QWidget * parent);~MyLabel() ;
};#endif // MYLABEL_H
上述代碼,在 Qt 中不會產生內存泄露。
label 對象會在合適的時候被析構釋放~~(雖然沒有手動寫 delete,確實能釋放)
之所以能夠把對象釋放掉,主要是因為把這個對象是掛到了對象樹上。
使用對象樹,把這些內容組織起來,最主要的目的,就是為了能夠在合適的時機 (窗口關閉/銷毀)
把這些對象統一進行釋放.
如果對象樹的某個對象提前銷毀,此時就會導致對應的控件就在界面上不存在了
#include "mylabel.h"
#include<iostream>
#include<QDebug>
MyLabel::MyLabel(QWidget * parent):QLabel(parent)
{}MyLabel::~MyLabel()
{//打印日志//會出現亂碼//std::cout << "MyLabel 被銷毀!" << std::endl;//QDebug 是 Qt 中的類。又不會直接使用這個類。這個宏,封裝了 QDebug 對象。//直接使用 qDebug() 可以當做 cout來使用qDebug() << "MyLabel 被銷毀!";
}
析構函數是執行了,雖然沒有手動 delete,但是由于把 MyLabel 掛到了對象樹上。此時窗口被銷毀的時候,就會自動銷毀對象樹中的所有對象。MyLabel 的析構是執行了
解決亂碼方案:
Qt 中提供了一個 qDebug() 工具,可以完成打印日志的過程。很好的處理字符編碼
QDebug 是 Qt 中的類。又不會直接使用這個類。這個宏,封裝了 QDebug 對象。
直接使用 qDebug() 這個東西就可以當做 cout 來使用。
使用 qDebug,打印的調試日志,是可以統一進行關閉的,qDebug 可以通過編譯開關,來實現一鍵式關閉
字符集
出現亂碼, 就是編碼方式不匹配( 不局限于 C++)
如果字符串本身是 utf8 編碼的,但是終端(控制臺)是按照 gbk 的方式來解析顯示的(拿著 utf8 這里的數值,去查詢 gbk 的碼表),就會出現亂碼
用記事本方式來打開文件,將文件另存為,并查看文件字符集
顯示的是 UTF-8,說明這個文件就是 UTF-8 編碼
顯示的是 ANSI,說明這個文件就是 GBK 編碼
信號槽
Qt 中的 connect 是 QObject 這個類提供的靜態函數。這個函數的作用就是 “連接信號和槽”
在 Qt Designer 中創建一個控件時,此時就會給這個控件分配一個 objectName 屬性。這個屬性的值,要求是在界面中得是唯一的(不能重復)
QPushButton :
qmake 在預處理 .ui 文件的時候,就會根據這里的 objectName 生成對應的 C++ 代碼。C++ 代碼中該 QPushButton 對象的變量名字就是這里的 objectName。這個變量就是 ui 屬性中的成員變量。
widget.cpp文件中通過connect連接信號與槽函數
通過圖形化界面的方式,實現的按鈕版 hello world
此時按鈕對象,不需要new , new 對象的操作已經是被 Qt 自動生成了
而且這個按鈕對象,已經作為 ui 對象里的一個成員變量了。
也無需作為 Widget 的成員
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(ui->myButton ,&QPushButton::clicked ,this , &Widget::handleClick );
}Widget::~Widget()
{delete ui;
}void Widget::handleClick()
{//獲取文本是hello Worldif(ui->myButton->text() == QString("hello World")){// 當按鈕被點擊之后,就把按鈕中的文本,進行切換ui->myButton->setText("hello qt");}//獲取文本不是hello Worldelse{// 當按鈕被點擊之后,就把按鈕中的文本,進行切換ui->myButton->setText("hello World");}
}
widget.cpp
純代碼方式實現Hello World
純代碼版本:
myButton按鈕對象是new 的
為了保證其他函數中能夠訪問到這個變量,就需要把myButton按鈕對象設定為 Widget 類的成員變量
#include "widget.h"
#include "ui_widget.h"
#include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);myButton = new QPushButton(this) ;myButton->setText("hello world") ;//將信號與槽函數關聯connect(ui->pushButton ,&QPushButton::clicked ,this , &Widget::handleClick );
}Widget::~Widget()
{delete ui;
}
//槽函數void Widget::handleClick (){//檢查myButton按鈕的當前文本 是否為"hello world"if(myButton->text() == QString("hello world")){myButton->setText("hellp qt") ;}else{myButton->setText("hellp world") ; }}
QT坐標系
坐標系的原點 (0, 0) 就是屏幕的左上角 / 窗口的左上角
給 Qt 的某個控件,設置位置,就需要指定坐標。對于這個控件來說,坐標系原點就是相對于父窗口/控件的。
例如:
QPushButton 的父元素/父控件/父窗口 是 QWidget
QWidget 沒有父元素 (NULL),就相當于父元素就是整個顯示器桌面了~~
信號與槽
Qt 中,談到信號,也是涉及到三個要素
信號源:由哪個控件發出的信號。
信號的類型:用戶進行不同的操作,就可能觸發不同的信號。
例如:點擊按鈕,觸發點擊信號。在輸入框中移動光標,觸發移動光標的信號。勾選一個復選框,選擇一個下拉框,都會觸發出不同的信號。
信號的處理方式:槽(slot),就是函數
Qt 中可以使用 connect 這樣的函數,把一個信號和一個槽關聯起來。后續只要信號觸發了,Qt 就會自動的執行槽函數
槽函數,本質上也是一種"回調函數" (callback)
一定是先把信號的處理方式準備好,再觸發信號
Qt 中,一定是先關聯號信號和槽,然后再觸發這個信號,順序不能顛倒,否則信號就不知道如何處理了
connect
connect,這個函數和 Linux 中TCP的socket 中建立連接的函數,沒有任何關系,只是名字恰巧一樣了。
connect是 QObject 提供的靜態的成員函數。
connect(const QObject *sender,const char *signal,const QObject *receiver,const char *method,Qt::ConnectionType type = Qt::AutoConnection);
sender : 當前信號是哪個控件發出來的
signal : 信號的類型
receiver : 哪個對象(控件)負責處理
method: 這個對象如何處理 (要處理信號的對象提供的成員函數)
type : 很少使用,暫時不考慮
例如:
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton * button = new QPushButton(this) ;button->setText("關閉") ;button->move(200,300);//close是QWidget內置的槽函數. Widget繼承自QWidget,也就繼承了QWidget的槽函數.//close:關閉當前的窗口/控件connect(button ,&QPushButton::clicked , this , &Widget::close) ;
}
Widget::~Widget()
{delete ui;
}
自定義槽
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//設置按鈕//點擊按鈕就修改窗口標題QPushButton * button = new QPushButton (this) ; //this,掛到對象樹上button->setText("按鈕");button->move(100,100); //移動坐標connect(button , &QPushButton::clicked , this , &Widget::handleClicked) ;}Widget::~Widget()
{delete ui;
}void Widget::handleClicked()
{//點擊按鈕就修改窗口標題setWindowTitle("按鈕已經按下");}
自定義槽函數 ,用ui界面
在Qt 中,除了通過connect來連接信號槽之外,還可以通過函數名字的方式來自動連接
選擇clicked() ,就會在widget.cpp 生成on_pushButton_clicked 函數的定義
? 當函數名符合上述規則之后,Qt就能自動的把信號和槽給建立上聯系
自定義信號
Qt 的信號,本質上也就是一個“函數”
Qt 5 以及更高版本中,槽函數和普通的成員函數之間,基本沒有差別
但是信號,則是一類非常特殊的函數。
程序員只要寫出函數聲明,并且告訴 Qt,這是一個“信號”即可。
這個函數的定義,是 Qt 在編譯過程中自動生成的。(自動生成的過程,程序員無法干預)
作為信號函數,這個函數的返回值,必須是void ,有沒有參數都可以, 甚至也可以支持重載
signals : 是 Qt 自己擴展出來的關鍵字
qmake 的時候,調用一些代碼的分析/生成工具,
掃描到類中包含signals 這個關鍵字的時候.此時,就會自動的把下面的函數聲明認為是信號,并且給這些信號函數自動的生成函數定義
widget.h:
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();//自定義信號
signals:void mysignal() ;//自定義槽函數
public slots: //qt5以上 ,public slots可以省略void handleMySignal() ; private:Ui::Widget *ui;
};
#endif // WIDGET_H
如何才能觸發出自定義的信號呢?
Qt 內置的信號,不需要手動通過代碼來觸發
用戶在 GUI,進行某些操作,就會自動觸發對應信號。(發射信號的代碼已經內置到 Qt 框架中了)
例:
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//將自定義信號與槽函數綁定connect (this, &Widget::mysignal , this, &Widget::handleMySignal) ;}Widget::~Widget()
{delete ui;
}void Widget::handleMySignal()
{setWindowTitle("處理自定義信號");
}void Widget::on_pushButton_clicked()
{//發送信號的操作,也可以在任意合適的代碼中.不一定非得在構造函數里//此時就是點擊按鈕的時候,發送自定義信號了emit mysignal();// 發出自定義的信號//emit可以省略
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();//自定義信號
signals:void mysignal() ;//自定義槽函數
public slots:void handleMySignal() ;//槽函數
private slots:void on_pushButton_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H
信號和槽也可以帶參數
當信號帶有參數的時候,槽的參數必須和信號的參數一致~~
此時發射信號的時候,就可以給信號函數傳遞實參。與之對應的這個參數就會被傳遞到對應的槽函數中。
這里的參數類型必須要一致
個數可以不一致,但是 信號的參數的個數必須要比槽的參數個數要更多。
個數可以不一致,但是 信號的參數的個數必須要比槽的參數個數要更多 ,原因:
一個槽函數,有可能會綁定多個信號,如果我們嚴格要求參數個數一致,就意味著信號綁定到槽的要求就變高了。換而言之,當下這樣的規則,就允許信號和槽之間的綁定更靈活了。更多的信號可以綁定到這個槽函數上了
例如:
參數類型必須要一致 ,個數一致
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//將自定義信號與槽函數綁定connect (this, &Widget::mysignal , this, &Widget::handleMySignal) ;}Widget::~Widget()
{delete ui;
}void Widget::handleMySignal(const QString & text )
{setWindowTitle(text);
}void Widget::on_pushButton_clicked()
{//發送信號的操作,也可以在任意合適的代碼中.不一定非得在構造函數里//此時就是點擊按鈕的時候,發送自定義信號了emit mysignal("把標題設置為標題1");// 發出自定義的信號
}void Widget::on_pushButton_2_clicked()
{emit mysignal("把標題設置為標題2");// 發出自定義的信號
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();//自定義信號
signals:void mysignal(const QString & text) ;//自定義槽函數
public slots:void handleMySignal(const QString & text) ;//槽函數
private slots:void on_pushButton_clicked();void on_pushButton_2_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H
參數類型必須要一致 ,信號的參數的個數必須要比槽的參數個數要更多
例如:
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//將自定義信號與槽函數綁定connect (this, &Widget::mysignal , this, &Widget::handleMySignal) ;}Widget::~Widget()
{delete ui;
}void Widget::handleMySignal(const QString & text)
{setWindowTitle(text);
}void Widget::on_pushButton_clicked()
{//發送信號的操作,也可以在任意合適的代碼中.不一定非得在構造函數里//此時就是點擊按鈕的時候,發送自定義信號了emit mysignal("把標題設置為標題1","");// 發出自定義的信號
}void Widget::on_pushButton_2_clicked()
{emit mysignal("把標題設置為標題2","");// 發出自定義的信號
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();//自定義信號
signals:void mysignal(const QString & text,const QString & text2) ;//自定義槽函數
public slots:void handleMySignal(const QString & text) ;//槽函數
private slots:void on_pushButton_clicked();void on_pushButton_2_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H
Q_OBJECT:
Qt硬性規定:Qt中如果要讓某個類能夠使用信號槽(可以在類中定義信號和槽函數)
必須要在類最開始的地方,寫下 Q_OBJECT 宏。
一個信號,可以 connect到多個槽函數上
一個槽函數,也可以被多個信號 connect
例如:
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();
signals://自定義信號void mySignal1() ;void mySignal2() ;void mySignal3() ;
public slots://自定義槽函數void mySlot1();void mySlot2();void mySlot3();private:Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//信號與槽函數 ,形成多對多的關系connect(this, &Widget::mySignal1, this, &Widget::mySlot1);connect(this, &Widget::mySignal1, this, &Widget::mySlot2);connect(this, &Widget::mySignal2, this, &Widget::mySlot1);connect(this, &Widget::mySignal2, this, &Widget::mySlot3);
}Widget::~Widget()
{delete ui;
}void Widget::mySlot1()
{qDebug() << "mySlots1" ;
}
void Widget::mySlot2()
{qDebug() << "mySlots2" ;
}
void Widget::mySlot3()
{qDebug() << "mySlots3" ;
}
disconnect
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();
public slots :void handClick() ;
public slots :void handClicked2();
private slots:void on_pushButton_2_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//將信號與槽函數關聯connect(ui->pushButton ,& QPushButton::clicked, this , &Widget::handClick) ;
}Widget::~Widget()
{delete ui;
}void Widget::handClick()
{setWindowTitle("修改窗口的標題");qDebug() <<"handClick";
}void Widget::handClicked2()
{setWindowTitle("修改窗口的標題2");qDebug() <<"handClicked2";
}
void Widget::on_pushButton_2_clicked()
{//1、先斷開pushButton 的信號槽disconnect(ui->pushButton ,&QPushButton::clicked, this, &Widget::handClick );//2、重新綁定信號槽connect(ui->pushButton ,&QPushButton::clicked, this , &Widget::handClicked2) ;
}
定義槽函數使用lambda
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//設置一個按鈕,并將按鈕移動位置QPushButton * button= new QPushButton(this) ;button->setText("按鈕" ) ;button->move(200,200) ;//關聯信號與槽函數//lambda就是槽函數connect(button ,&QPushButton::clicked , this ,[button,this](){qDebug()<<"lambda被執行了";button->move(500,500) ;this->move(500,500);} );}Widget::~Widget()
{delete ui;
}