1.QObject
只有繼承了QObject類的類,才具有信號槽的能力。所以,為了使用信號槽,必須繼承QObject。凡是QObject類(不管是直接子類還是間接子類),都應該在第一行代碼寫上Q_OBJECT。不管是不是使用信號槽,都應該添加這個宏。這個宏的展開將為我們的類提供信號槽機制、國際化機制以及 Qt 提供的不基于 C++ RTTI 的反射能力。因此,如果你覺得你的類不需要使用信號槽,就不添加這個宏,就是錯誤的。其它很多操作都會依賴于這個宏。
示例:
#include <QMainWindow>
#include <QString>
class A:public QObject{
public:A(QObject* parent=NULL):QObject(parent){qInfo()<<this<<"被構造";};~A(){qInfo()<<this<<"被銷毀";};
};
int main(int argc, char* argv[]){A objA;A* pA2=new A(&objA); //將pA2掛在到objA下A* pA3 = new A(pA2);objA.dumpObjectTree();
}
這樣子就會形成一個樹結構。
QObject:: A
? ? QObject:: pA2
? ? ? ? QObject:: pA3
2.事件與信號
GUI應用程序都由事件驅動,事件主要由應用程序的用戶生成,例如點擊按鈕,控件。或者由其他接觸發生如:Internet連接,窗口管理器或計時器。當調用exec方法時,應用程序進入主循環。主循環將獲取事件并發送到對象。
信號與槽?
信號和槽用于對象之間的通信。
//signal1調用到obj2的slot1
connect(Object1,signal1,Object2,slot1);//signal1調用到obj3的slot1
connect(Object1,signal1,Object3,slot1);
?slot是普通的C++函數,當與之相連的信號發出時將調用。
連接信號和插槽的方式:
1.成員函數指針
connect(senderPtr,&QObject::destoryed,this,&MyObject::objectDestroyed);
2.仿函數或lambda表達式作為slot
connect(sender,&QObject::destoryed,this,[=](){this->m_object.remove(sender);});?
學習示例:
頭文件
#ifndef MYHEAD1_H_
#define MYHEAD1_H_
#include <QCoreApplication>
#include <QDebug>
class Sender : public QObject
{Q_OBJECT
public:explicit Sender(QObject* parent = nullptr);private:int m_age = 10;public:void incAge();
signals:// 信號函數無需定義,只需聲明,并且不能有返回參數,但可以有輸入參數void ageChanged(int value);
};class Receiver : public QObject
{Q_OBJECT
public:explicit Receiver(QObject* parent = nullptr);
public slots://槽函數為普通函數,需要定義,但也不能有返回值void ageChange(int age);
};
#endif // MYHEAD1_H_
在main函數中調用:
#include "myhead1.h"
int main(int argc, char* argv[])
{Sender senderObj;senderObj.incAge();Receiver recriverObj;//傳遞信號,通過指針的方式傳遞QObject::connect(&senderObj,&Sender::ageChanged,&recriverObj,&Receiver::ageChange);//建立連接后,每次emit發送信號都會傳遞給reciver然后調用ageChangesenderObj.incAge();senderObj.incAge();//斷開連接 QObject::disconnect(&senderObj,&Sender::ageChanged,&recriverObj,&Receiver::ageChange);senderObj.incAge();return 0;
}
當建立連接后,每次emit發送信號后,都會執行相應的槽(slots),而段凱連接后則不會繼續調用槽。
3.鼠標鍵盤響應
? 在MainWindow構造函數中注冊事件,在觸發時讓其發出信號調用對應處理槽.
頭文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();// QWidget interface
protected:void keyPressEvent(QKeyEvent *event);void mouseMoveEvent(QMouseEvent *event);
};
#endif // MAINWINDOW_H
實現文件
#include "mainwindow.h"#include <QtWidgets>
MainWindow::MainWindow(QWidget* parent): QMainWindow(parent)
{// 開啟鼠標跟蹤setMouseTracking(true);// 創建一個按鈕對象,入參為按鈕顯示名字和操作對象auto* quitBtn = new QPushButton("Quit", this);// 設置按鈕位置和大小quitBtn->setGeometry(50, 25, 100, 50);// 創建連接,當按鈕點擊事件出發時,調用循環的退出函數connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit);
}MainWindow::~MainWindow()
{
}void MainWindow::keyPressEvent(QKeyEvent* event)
{// 如果當前按鍵事件是esc鍵,則退出程序if (event->key() == Qt::Key_Escape)qApp->quit();
}void MainWindow::mouseMoveEvent(QMouseEvent* event)
{// 獲取當前鼠標X坐標int x = event->pos().x();// 獲取y坐標int y = event->pos().y();QString text = "坐標:" + QString::number(x) + "," + QString::number(y);this->statusBar()->showMessage(text);
}
在其中,使用new QPushButton創建了一個按鈕,并且在按鈕中顯示了文字,同時使用按鈕中的方法來指定按鈕的位置和大小(按照x,y軸來判斷位置和創建按鈕大小的).最后通過指針綁定按鈕的點擊事件,當按鈕被按下時觸發QPushButton::clicked,然后調用槽QApplication::quit用來退出程序。
其余的鍵盤檢測按鍵和鼠標位置是通過重寫QMainWindow類中的抽象函數來實現,當在窗口中檢測到時會自動的進行調用.
4.控件與自定義槽
QWidget是用戶界面的原子類。它接收鼠標、鍵盤和來自系統的其他事件,并在屏幕上將它們繪制出來。每個Widget都是矩形的,并按照Z-order(Z軸)進行排序。一個Widget夾在它的Parent和它前面的Widget之間。
沒有嵌入parent widget中的Widget稱為Window。通常情況下,Windows有一個Frame和標題欄(當然也可以通過window flags來取消這些項)。Qt中,QMainWindow和QDialog的多種多樣的子類是最常見的Window類型.
?
這就是一個定義好的QMainWindow,其布局已經是默認規定好的,無法再去增加布局,但是可以創建布局然后替換對應的布局,并放入組件。
頭文件:
#ifndef MAINWINDOW_H_
#define MAINWINDOW_H_
#include <QMainWindow>
class QPushButton;
class QLabel;
class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget* parent = nullptr);~MainWindow();private:QPushButton* clickBtn;QLabel* label;// QObject interface
protected:void timerEvent(QTimerEvent *event);public slots:void onClick();void onCheck(int state);
};
#endif // MAINWINDOW_H
實現文件:
#include "mainwindow.h"#include <QtWidgets>
MainWindow::MainWindow(QWidget* parent): QMainWindow(parent){// 創建一個布局QWidget* myWidget = new QWidget(this);// 替換到中心布局中setCentralWidget(myWidget);// 創建按鈕clickBtn = new QPushButton("點擊", myWidget);// 創建點擊事件QCheckBox* cb = new QCheckBox("Connect", myWidget);// 設置點擊事件默認狀態cb->setCheckState(Qt::Checked);label = new QLabel(QTime::currentTime().toString(), myWidget);//橫向的展示組件QHBoxLayout* hbox = new QHBoxLayout(myWidget);hbox->addWidget(clickBtn);hbox->addWidget(cb);hbox->addWidget(label);startTimer(1000);// 以指針的方式傳入對象和函數connect(clickBtn, &QPushButton::clicked, this, &MainWindow::onClick);connect(cb, &QCheckBox::stateChanged, this, &MainWindow::onCheck);
}MainWindow::~MainWindow()
{
}void MainWindow::timerEvent(QTimerEvent* event)
{// 標識這個形參沒有用到Q_UNUSED(event);label->setText(QTime::currentTime().toString());
}void MainWindow::onClick()
{// 在底部標題欄展示信息statusBar()->showMessage("按鈕被點擊");
}void MainWindow::onCheck(int state)
{statusBar()->showMessage("");// 根據QCheckBox狀態來執行對應函數if (state == Qt::Checked)connect(clickBtn, &QPushButton::clicked, this, &MainWindow::onClick);elsedisconnect(clickBtn, &QPushButton::clicked, this, &MainWindow::onClick);
}
?從這個程序中,我們可以將組件裝入到我們自己創建的widget布局中,然后將該布局設置為中心布局,這樣就可以在中心區域展示組件了,同時創建了自定義的槽,當觸發相應事件的時候調用了自定義槽進行響應。同時也可以根據信號的實時狀態來進行連接和斷開。
?