繼承,對於學習C++的每一個人來說,都不會陌生。在Qt的開發中,如果你需要對一個無邊框的界面支持move操作,那麼你就得通過繼承重寫虛函數來實現,這並不難,但如果我還需要對一個按鈕支持移動,一般情況,當然是Crtl + c 、Crtl + v搞定,但我們不難發現,對於move這個操作來說,其實代碼完全一模一樣,那麼有沒有什麼辦法可以簡化,可以一勞永逸呢?
答案是肯定的,這裡我們就需要用到C++的模板來實現了,即本文要介紹的鏈式繼承。
前面有實現過move操作,這裡將它抽取出來,代碼如下:
1: #include <QWidget>2: #include <QMouseEvent>3: #include <QPoint>4:5: //T 為基類,繼續擁有一個QWidget *parent參數的構造函數6: template <typename T>7: class WidgetMove : public T8: {9: public:10: WidgetMove(QWidget *parent = 0):T(parent)11: {12: }13: protected:14: void mousePressEvent(QMouseEvent *event)15: {16: //當鼠標左鍵按下時,記錄當前位置17: if(event->button() == Qt::LeftButton)18: {19: m_CurrentPos = event->globalPos() - T::frameGeometry().topLeft();20: event->accept();21: }22: T::mousePressEvent(event);23: }24:25: void mouseMoveEvent(QMouseEvent *event)26: {27: //支持窗體移動28: if (event->buttons() & Qt::LeftButton)29: {30: T::move(event->globalPos() - m_CurrentPos);31: event->accept();32: }33: T::mouseMoveEvent(event);34: }35: private:36: QPoint m_CurrentPos;37: };
接下來我們實現一個圖片按鈕:
1: #include <QPushButton>2: #include "widgetmove.h"3:4: //間接繼承QPushButton,讓按鈕支持移動5: class ImgButton : public QPushButton6: {7: Q_OBJECT8: public:9: explicit ImgButton(QWidget *parent = 0);10: void paintEvent(QPaintEvent *event);11: QPixmap m_Pixmap;12: };13:14:15: #include "imgbutton.h"16: #include <QIcon>17: #include <QBitmap>18:19: ImgButton::ImgButton(QWidget *parent) :20: QPushButton(parent)21: {22: //必須設置為無邊框,否則可見區域和圖片繪制區域將出現不重疊23: setWindowFlags( Qt::FramelessWindowHint );24: m_Pixmap.load("close.png");25: resize(100,100);26: m_Pixmap = m_Pixmap.scaled(this->size(),Qt::IgnoreAspectRatio);27: }28:29: void ImgButton::paintEvent(QPaintEvent *event)30: {31: //繪制背景圖片32: QIcon icon(m_Pixmap);33: this->setIcon(icon);34: this->setIconSize(size());35: //將png圖片透明部分設置為穿透36: this->setMask(m_Pixmap.mask());37: //繪制38: QPushButton::paintEvent(event);39: }
然後我們把異形窗體重新實現了:
1: #include <QString>2: #include <QBitmap>3: #include <QPaintEvent>4: #include <QPalette>5: //異形窗體實現6:7: //T 為基類,繼續擁有一個QWidget *parent參數的構造函數8: template <typename T>9: class WidgetRuleless : public T10: {11: public:12: WidgetRuleless(QWidget *parent = 0):T(parent)13: {14: //設置為無邊框15: T::setWindowFlags( Qt::FramelessWindowHint );16: }17: void SetBackgroundImg(const QString &imgFile)18: {19: m_Pixmap.load(imgFile);20:21: //保持圖片跟界面一樣大小22: m_Pixmap = m_Pixmap.scaled(T::size());23:24: T::setAutoFillBackground(true);25:26: //不規則窗口的關鍵,將圖片透明的地方設為穿透27: T::setMask( m_Pixmap.mask() );28: }29: protected:30: void paintEvent(QPaintEvent *event)31: {32: if(!m_Pixmap.isNull())33: {34: //繪制背景圖片35: QPalette bgPalette = this->palette();36: bgPalette.setBrush(QPalette::Background,m_Pixmap);37: T::setPalette(bgPalette);38: }39: }40:41: private:42: QPixmap m_Pixmap;43: };
組件准備好後,我們就可以輕松的使用了
1: //創建一個異形窗體,支持move操作,基類為QWidget2: WidgetRuleless< WidgetMove<QWidget> > wid;3: wid.SetBackgroundImg("hudie.png");4: wid.resize(640, 480);5: //創建一個Button類,並且支持move操作6: WidgetMove<ImgButton> btn(&wid);7: btn.move(300,300);8: wid.show();
簡單幾行代碼,我們就可以得到以下效果:
效果圖: 蝴蝶為異形窗體,小樹為異形按鈕,並且都支持move動作。
這裡我們將經常用的小功能(主要是需要通過繼承實現的),分解成若干小零件,在日常項目開發中,我們只需要進行簡單的組合,就可以得到一個功能強大的控件。
後記:C++是一門異常強大的語言,模板的注入更為C++添加了無窮的潛力,十余年來,他的潛力不斷被挖掘出來,但依舊有存在巨大的潛力等待我們去探索,去發現。
注:模板類中不支持Qt信號和槽的機制。