> 這幾天導師下發任務,讓我一個月以內熟悉一個經過Qt Widget製做的項目,第一眼看到項目,我是懵逼的。才知道本身寫的那些幾百行的代碼算什麼玩意了。看到項目裏密密麻麻繼承QWidget的控件,而後各類重寫paintEvent方法,才發現本身徹底沒有使用過Qt的繪圖功能。好吧,開始學習吧!c++
本文將會介紹如何經過重寫QWidget類的paintEvent方法自定義一個CheckBox。固然有人問了,爲何不使用QSS來設置CheckBox樣式,搞個自定義這麼複雜幹嗎?這個嘛,,,固然是閒得蛋疼了哈哈哈哈~函數
若要使用Qt的繪圖功能,就須要建立一個繼承自QPaintDevice的類,包括QWidget、QImage、QPixmap等。這裏咱們重點說一下QWidget,不少控件都繼承自QWidget,咱們能夠操做的如QLineEdit、QAbstractButton無一例外的繼承自QWidget。這就覺得着咱們自己能夠經過重寫QWidget對象中的方法來實現注入Button的操做。本文編寫了一個CheckBox類,代碼以下:學習
#ifndef CHECKBOX_H #define CHECKBOX_H #include <qobject> #include <qwidget> class CheckBox : public QWidget { Q_OBJECT public: explicit CheckBox(QWidget *parent = nullptr); bool getIsChecked() const; protected: virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; Q_SIGNALS: void clicked(bool isChecked); public Q_SLOTS: void onClicked(); private: bool isChecked = false; QPainter *painter; }; #endif // CHECKBOX_H
這裏注意的是我重寫了paintEvent方法和鼠標點擊和釋放方法。isCheckedbool值用來儲存checkbox的選中狀態。this
構造函數以下:翻譯
CheckBox::CheckBox(QWidget *parent) : QWidget(parent) { this->connect(this, SIGNAL(clicked(bool)), this, SLOT(onClicked())); }
鼠標點擊事件以下:debug
// 沒用到 void CheckBox::mousePressEvent(QMouseEvent *event) { } void CheckBox::mouseReleaseEvent(QMouseEvent *event) { isChecked = !isChecked; // 狀態取反 qDebug() << "click status:" << isChecked; emit clicked(isChecked); // 釋放被點擊信號, }
onClicked槽函數以下:code
void CheckBox::onClicked() { this->update(); }
這裏的QWidget::update()槽函數用於刷新繪圖。繪圖刷新的具體流程下文會提到。orm
直接貼代碼:對象
void CheckBox::paintEvent(QPaintEvent *event) { Q_UNUSED(event); painter = new QPainter(); painter->begin(this); // 建立一支筆,設置各類樣式 QPen pen; pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::RoundJoin); pen.setWidth(2); pen.setStyle(Qt::SolidLine); pen.setColor("#fff"); painter->setPen(pen); // 畫一個正方形做爲checkbox的框框 painter->drawRect(4,4,10,10); // QStyleOption option; // option.initFrom(this); // this->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter, this); // 寫checkbox邊上的label QRect rect(12, -1, 50, 20); painter->drawText(rect, Qt::AlignCenter, QString::fromLocal8Bit("選擇框")); // 判斷checkbox是否被選中,則畫一個NIKE if (isChecked) { QPoint points[] = { QPoint(2, 10), QPoint(7, 15), QPoint(17, 5) }; painter->drawPolyline(points, 3); } painter->end(); }
這裏讀者會發現這幾行註釋:繼承
QStyleOption option; option.initFrom(this); this->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter, this);
QStyleOption在開發文檔中是這樣描述的: > QStyleOption and its subclasses contain all the information that QStyle functions need to draw a graphical element.
意思是QStyleOption和它的子類包含了全部QStyle須要畫一個圖形元素的函數的全部信息。
initFrom方法描述我就直接翻譯給各位看官: > QStyleOption.initFrom(const QWidget *widget)根據指定的窗口小部件初始化state,direction,rect,palette,fontMetrics和styleObject成員變量。
QStyle::drawPrimitive方法讓開發者快速繪畫出系統Qt控件圖案。官方解釋是: > 使用選項指定的樣式選項,使用提供的畫家繪製給定的基本元素。
跟我說的應該沒有什麼區別(逃)。
一個一個方法看太麻煩,因此:
#include "checkbox.h" #include <qdebug> #include <qpainter> #include <qstyle> #include <qstyleoption> CheckBox::CheckBox(QWidget *parent) : QWidget(parent) { this->connect(this, SIGNAL(clicked(bool)), this, SLOT(onClicked())); } void CheckBox::paintEvent(QPaintEvent *event) { Q_UNUSED(event); painter = new QPainter(); painter->begin(this); QPen pen; pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::RoundJoin); pen.setWidth(2); pen.setStyle(Qt::SolidLine); pen.setColor("#fff"); painter->setPen(pen); painter->drawRect(4,4,10,10); // QStyleOption option; // option.initFrom(this); // this->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter, this); QRect rect(12, -1, 50, 20); painter->drawText(rect, Qt::AlignCenter, QString::fromLocal8Bit("選擇框")); // 判斷checkbox是否被選中,則畫一個NIKE if (isChecked) { QPoint points[] = { QPoint(2, 10), QPoint(7, 15), QPoint(17, 5) }; painter->drawPolyline(points, 3); } painter->end(); } void CheckBox::mousePressEvent(QMouseEvent *event) { } void CheckBox::mouseReleaseEvent(QMouseEvent *event) { isChecked = !isChecked; qDebug() << "click status:" << isChecked; emit clicked(isChecked); } void CheckBox::onClicked() { this->update(); } bool CheckBox::getIsChecked() const { return isChecked; }
本身畫框框
使用drawPrimitive