控制元件的基本使用
為了更好地學習Qt控制元件的使用,建議建立專案時先不要生成ui檔案。
開啟mainwindow.cpp,在MainWindow的建構函式中編寫介面的初始化程式碼。
視窗設定
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// 設定視窗標題
setWindowTitle("主視窗");
// 設定視窗大小
// 視窗可以通過拖拽邊緣進行自由伸縮
// resize(400, 400);
// 設定視窗的固定大小
// 視窗不能通過拖拽邊緣進行自由伸縮
setFixedSize(400, 400);
// 設定視窗的位置
// 以父控制元件的左上角為座標原點
// 沒有父控制元件,就以螢幕的左上角作為座標原點
move(100, 100);
}
Qt座標系如下圖所示。
新增子控制元件
#include <QPushButton>
// 建立按鈕
QPushButton *btn = new QPushButton;
// 設定按鈕的文字
btn->setText("登入");
// 設定父控制元件為當前視窗
btn->setParent(this);
// 設定按鈕的位置和大小
btn->move(50, 50);
btn->resize(100, 50);
// 建立第2個按鈕
new QPushButton("註冊", this);
new出來的Qt控制元件是不需要程式設計師手動delete的,Qt內部會自動管理記憶體:當父控制元件銷燬時,會順帶銷燬子控制元件。
訊號與槽
基本使用
- 訊號(Signal):比如點選按鈕就會發出一個點選訊號
- 槽(Slot):一般也叫槽函式,是用來處理訊號的函式
- 官方文件參考:Signals & Slots
上圖中的效果是:
- Object1發出訊號signal1,交給Object2的槽slot1、slot2去處理
- Object1是訊號的傳送者,Object2是訊號的接收者
- Object1發出訊號signal2,交給Object4的槽slot1去處理
- Object1是訊號的傳送者,Object4是訊號的接收者
- Object3發出訊號signal1,交給Object4的槽slot3去處理
- Object3是訊號的傳送者,Object4是訊號的接收者
- 1個訊號可以由多個槽進行處理,1個槽可以處理多個訊號
通過connect函式可以將訊號的傳送者、訊號、訊號的接收者、槽連線在一起。
connect(訊號的傳送者, 訊號, 訊號的接收者, 槽);
// 比如點選按鈕,關閉當前視窗
// btn發出clicked訊號,就會呼叫this的close函式
connect(btn, &QAbstractButton::clicked, this, &QWidget::close);
// 可以通過disconnect斷開連線
disconnect(btn, &QAbstractButton::clicked, this, &QWidget::close);
自定義訊號與槽
訊號的傳送者和接收者都必須繼承自QObject,Qt中的控制元件最終都是繼承自QObject,比如QMainWindow、QPushButton等。
訊號的傳送者
- sender.h
- Q_OBJECT用以支援自定義訊號和槽
- 自定義的訊號需要寫在signals:下面
- 自定義的訊號只需要宣告,不需要實現
#ifndef SENDER_H
#define SENDER_H
#include <QObject>
class Sender : public QObject
{
Q_OBJECT
public:
explicit Sender(QObject *parent = nullptr);
// 自定義訊號
signals:
void exit();
};
#endif // SENDER_H
- sender.cpp
#include "sender.h"
Sender::Sender(QObject *parent) : QObject(parent)
{
}
訊號的接收者
- receiver.h
- 自定義的槽建議寫在public slots:下面
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
class Receiver : public QObject
{
Q_OBJECT
public:
explicit Receiver(QObject *parent = nullptr);
// 自定義槽
public slots:
void handleExit();
};
#endif // RECEIVER_H
- receiver.cpp
#include "receiver.h"
#include <QDebug>
Receiver::Receiver(QObject *parent) : QObject(parent)
{
}
// 實現槽函式,編寫處理訊號的程式碼
void Receiver::handleExit()
{
qDebug() << "Receiver::handleExit()";
}
連線
- mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "sender.h"
#include "receiver.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
Sender *_sender;
Receiver *_receiver;
void test1();
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
};
#endif // MAINWINDOW_H
- mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
this->_sender = new Sender;
this->_receiver = new Receiver;
// 連線
connect(this->_sender,
&Sender::exit,
this->_receiver,
&Receiver::handleExit);
// 發出訊號
// 最終會呼叫Receiver::handleExit函式
emit this->_sender->exit();
}
MainWindow::~MainWindow()
{
}
引數和返回值
訊號與槽都可以有引數和返回值:
- 發訊號時的引數會傳遞給槽
- 槽的返回值會返回到發訊號的位置
// 自定義訊號
signals:
int exit(int a, int b);
// 自定義槽
public slots:
int handleExit(int a, int b);
int Receiver::handleExit(int a, int b)
{
// Receiver::handleExit() 10 20
qDebug() << "Receiver::handleExit()" << a << b;
return a + b;
}
// 發出訊號
int a = emit this->_sender->exit(10, 20);
// 30
qDebug() << a;
需要注意的是:訊號的引數個數必須大於等於槽的引數個數。比如下面的寫法是錯誤的:
// 自定義訊號
signals:
void exit(int a);
// 自定義槽
public slots:
void handleExit(int a, int b);
連線2個訊號
比如下圖,連線了Object 1的Signal 1A和Object 2的Signal 2A
- 當Object 1發出Signal 1A時,會觸發Slot X、Slot Y
- 當Object 2發出Signal 2A時,只會觸發Slot Y,而不會觸發Slot X
可以利用connect函式連線2個訊號
- 當_sender發出exit訊號時,_sender2會發出exit2訊號
- 當_sender2發出exit2訊號時,_sender並不會發出exit訊號
connect(this->_sender,
&Sender::exit,
this->_sender2,
&Sender2::exit2);
Lambda
也可以直接使用Lambda處理訊號。
connect(this->_sender, &Sender::exit, []() {
qDebug() << "lambda handle exit";
});