本章主要內容如下:
- 1) 窗口組件(QWidget)
- 2) QT坐標系統
- 3) 消息處理(信號與槽)
窗口組件(QWidget)
介紹
- Qt以組件對象的方式構建圖形用戶界面
- Qt中沒有父組件的頂級組件,則被叫做窗口
- 組件的類型分為:
- 容器類(父組件) : 用於包含功能的界面組件
- 功能類(子組件) : 用於實現特定的交互功能
如下圖所示:
比如上面的QgroupBox ,即屬於頂級組件的功能類(子組件),又是3個功能類組件的父組件(容器)
組件繼承
Qt中所有窗口組件都繼承於QWidget類,而QWidget類又繼承於QObject類和QPaintDevice類.
如下圖所示(只舉例了3個窗口組件類):
QWidget組件介紹
- QWidget能夠繪制自己(因為繼承了QPaintDevice類),也能夠處理用戶的輸入,比如點擊按鈕
- QWidget是Qt窗口組件類的父類
- Qt中每個窗口組件都可以當做一個QWidget (因為子類可以初始化父類)
- QWidget類對象常作為父組件或頂級組件使用
初探QWidget
1)新建工程,選擇Qt Gui應用,設置類信息:
2)生成QWidget模板
運行模板:
可以看到生成了一個窗口,然后我們來看看模板代碼,是如何生成的.
3)模板代碼如下所示
#include <QtGui/QApplication> #include "widget.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget w; //創建QWidget類對象 w.show(); //顯示QWidget類對象 return a.exec(); }
根據之前講的內容可以發現,由於上面的QWidget w對象沒有父組件,所以QWidget w便成為了沒有父組件的頂級組件,從而生成了窗口.
Qt坐標系統
介紹
- Qt使用統一的坐標系統定位窗口部件的位置和大小
- QWidget類為組件類提供了窗口部件所需的坐標系統成員函數
在Qt里,坐標類型分為
- 頂級窗口部件的定位
- 窗口內部件的定位
- 窗口部件的大小設置
坐標位置示意圖,如下所示
QWidget類提供的常用坐標系統成員函數有:
- resize() : 設置窗口內部的寬高( width()和height()值)
- move() : 設置整個窗口的x,y坐標( x()和y()值)
- setGeometry() : 設置窗口內部的x,y,w,h(不包括標題和窗口邊框)
- size() : 獲取窗口部件的大小
- pos() : 獲取窗口部件的位置
- x() : 獲取整個窗口x坐標
- y() : 獲取整個窗口y坐標
- width() : 獲取窗口內部的寬度(不包括外邊框的寬度)
- height() : 獲取窗口內部的高度(不包括窗口標題欄的高度)
- const QRect& geometry () : 獲取窗口內部的x,y,w,h(不包括標題和窗口邊框)
- const QRect& framgeometry () : 獲取整個窗口的x,y,w,h
可以參考下圖所示
注意: 在代碼里,執行show()后, 再獲取 x,y,w,h坐標 才有效
接下來我們通過3組不同的獲取坐標函數,來打印(x,y,w,h)坐標信息
代碼如下所示:
#include <QtGui> #include "widget.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; QPushButton b("button",&w); //生成 QPushButton對象, 其父組件為 QWidget
/*設置窗口大小位置*/ w.resize(200,300); w.move(300,300);
/*設置按鈕大小位置*/ b.resize(100,50); b.move(50,100); w.show();
qDebug()<<"QWidget:"; qDebug()<<"x()="<<w.x(); qDebug()<<"y()="<<w.y(); qDebug()<<"width()="<<w.width(); qDebug()<<"height()="<<w.height(); qDebug()<<"QWidget::geometry()"; qDebug()<<"x="<<w.geometry().x(); qDebug()<<"y="<<w.geometry().y(); qDebug()<<"w="<<w.geometry().width(); qDebug()<<"h="<<w.geometry().height(); qDebug()<<"QWidget::frameGeometry()"; qDebug()<<"x="<<w.frameGeometry().x(); qDebug()<<"y="<<w.frameGeometry().y(); qDebug()<<"w="<<w.frameGeometry().width(); qDebug()<<"h="<<w.frameGeometry().height(); return a.exec(); }
運行打印:
QWidget: x()= 300 y()= 300 width()= 200 height()= 300 QWidget::geometry() x= 308 y= 330 w= 200
h= 300 QWidget::frameGeometry() x= 300 y= 300 w= 216 h= 338
可以看到,獲取的窗內坐標(x,y)永遠比窗外坐標大,窗外大小(w,h)永遠比窗內大小大
初探消息處理(信號與槽)
QT封裝了具體操作系統的消息機制,如下圖所示:
Qt中定義了與系統信息相關的概念
信號(signal)
- 由操作系統產生的消息,比如按鍵消息
槽(slot)
- 程序中的消息處理函數,用來處理信號,比如處理按鍵點擊信號
連接(Connect)
- 將系統信息綁定到信息處理函數(信號到槽的連接),通過connect()函數實現,且必須發生在兩個Qt類對象之間,如下圖所示:
connect()函數原型
bool QObject::connect ( const QObject * sender, //發送對象 const char * signal, //消息名(信息) const QObject * receiver, //接收對象 const char * method, //接收對象的成員函數(槽) Qt::ConnectionType type = Qt::AutoConnection ) ; //正常情況不需要設置 //當出現sender對象的signal信號,則會自動調用receiver對象的method
//連接成功,則返回true;否則返回false。
在信號與槽里,Qt引進了幾個新的關鍵字:
- SIGNAL :指定消息名(信號),用於connect()函數里
- SLOT : 指定消息處理函數名(槽),用於connect()函數里
- Q_OBJECT : 指定該類擁有槽(消息處理),在類聲明的內部開始處加上Q_OBJECT即可
- slots : 用於在類中聲明消息處理函數,比如:
private slots: void buttonCliked();
初探信號與槽
通過點擊按鈕,使程序自動退出,代碼如下所示:
#include <QtGui> #include <QApplication> #include <QPushButton> int main(int argc,char * argv[]) { QApplication app(argc,argv); QPushButton *quitButton = new QPushButton("Quit"); QObject::connect(quitButton, SIGNAL(clicked()), &app, SLOT(quit())); //*quitButton(發送對象), &app(接收對象) //quit()作用是退出程序, QApplication的成員函數 //clicked()作用是鼠標點擊, 很多常用組件的成員函數 quitButton->show(); return app.exec(); }
其中上面的quit() 和clicked()都是系統預定義好的,接下來我們自定義槽
首先需要注意
- 類中聲明槽(處理信號的成員函數)時,需要slots聲明
- 槽和信號的函數參數必須一致,比如clicked()和quit()都是無參數的
- SIGNAL和SLOT指定的函數(信號和槽)只能包含參數類型,不能包含參數名
開始試驗,通過不同按鈕點擊,來打印不同的信息
寫QButtonDebug.h:
#ifndef QBUTTONDEBUG_H #define QBUTTONDEBUG_H
#include <QWidget> #include <QPushButton> class QButtonDebug : public QWidget { Q_OBJECT //指定該類擁有slots(槽) private: QPushButton *mbton1; QPushButton *mbton2; private slots: //通過slots 聲明 槽 void buttonCliked(); public: explicit QButtonDebug(QWidget *parent=0,Qt::WindowFlags f=0); }; #endif
寫QButtonDebug.cpp:
#include "QButtonDebug.h" #include <QDebug>
QButtonDebug:: QButtonDebug(QWidget *parent,Qt::WindowFlags f) : QWidget(parent,f) //顯示初始化父類 { mbton1 = new QPushButton("button1",this); mbton2 = new QPushButton("button2",this); /*設置按鈕坐標*/ mbton1->resize(100,50); mbton1->move(50,50); mbton2->resize(100,50); mbton2->move(50,100); /*設置連接*/ QObject::connect(mbton1,SIGNAL(clicked()),this,SLOT(buttonCliked())); QObject::connect(mbton2,SIGNAL(clicked()),this,SLOT(buttonCliked())); QWidget::show(); }
void QButtonDebug:: buttonCliked() //消息處理函數 { QPushButton* p_buton =dynamic_cast<QPushButton*>(sender()); //獲取發送信號的對象 使用 qDebug()<< p_buton->text(); //更據不同的按鈕 打印不同信息 }
寫main.cpp
#include <QtGui> #include <QApplication> #include "QButtonDebug.h" int main(int argc,char * argv[]) { QApplication a(argc, argv); QButtonDebug b(NULL,Qt::WindowCloseButtonHint); // Qt::WindowCloseButtonHint:去掉標題按鈕提示 return a.exec(); }
運行測試
如下圖所示,可以看到通過點擊不同的按鈕,便能打印不同的信息出來
深入信號槽-自定義信號
介紹
- 只有Qt類才能定義信號,且該類必須在頭文件中聲明
- 信號函數只能通過signals關鍵字進行聲明,不能定義,且返回值必須是void類型
- 信號函數的屬性會被自動設置為protected類型
- 發送信號時,只需要通過emit關鍵字調用信號函數即可
- 如果信號函數的參數多於槽函數時,多於的參數將被忽略
- 槽函數的返回值必須是void類型,且可以被其它普通成員函數調用
自定義信號示例:
class MySignal : public QObject { Q_OBJECT signals: //自定義信號函數 void SendSignal(int i); public: void send(int i) { emit SendSignal(i); //調用信號函數,發送信號 } };
自定義槽函數示例:
class MySlot : public QObject { Q_OBJECT
protected slots: void RecvSlot(int i) { qDebug()<<"Send:"<<sender()->objectName(); //打印發送對象名 qDebug()<<"Recv:"<<i; qDebug()<<endl; } };
信號與槽的組合
- 信號函數可以連接多個槽函數
- 多個信號函數可以連接一個槽函數
- 一個信號就可以連接到另一個信號
- 通過connect函數進行連接,也可以通過disconnect函數取消連接
示例1-多個信號連接一個槽:
MySignal s1; MySignal s2; MySlot t; s1.setObjectName("Signal1"); s2.setObjectName("Signal2");
QObject::connect(&s1,SIGNAL(SendSignal(int)),&t,SLOT(RecvSlot(int))); QObject::connect(&s2,SIGNAL(SendSignal(int)),&t,SLOT(RecvSlot(int))); s1.send(10); s2.send(12);
打印:
Send: "Signal1" Recv: 10 Send: "Signal2" Recv: 12
示例2-信號1連接信號2,信號2連接槽:
MySignal s1; MySignal s2; MySlot t; s1.setObjectName("Signal1"); s2.setObjectName("Signal2"); QObject::connect(&s1,SIGNAL(SendSignal(int)),&s2,SIGNAL(SendSignal(int))); QObject::connect(&s2,SIGNAL(SendSignal(int)),&t,SLOT(RecvSlot(int))); s1.send(10); s2.send(12);
打印:
Send: "Signal2" Recv: 10
Send: "Signal2" Recv: 12