Qt通过重写paintEvent方法写一个自定义选择框

> 这几天导师下发任务,让我一个月之内熟悉一个通过Qt Widget制作的项目,第一眼看到项目,我是懵逼的。才知道自己写的那些几百行的代码算什么玩意了。看到项目里密密麻麻继承QWidget的控件,然后各种重写paintEvent方法,才发现自己完全没有使用过Qt的绘图功能。好吧,开始学习吧!

本文将会介绍如何通过重写QWidget类的paintEvent方法自定义一个CheckBox。当然有人问了,为什么不使用QSS来设置CheckBox样式,搞个自定义这么复杂干嘛?这个嘛,,,当然是闲得蛋疼了哈哈哈哈~

第一步:创建一个继承QWidget的对象

若要使用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的选中状态。

构造函数如下:

CheckBox::CheckBox(QWidget *parent) : QWidget(parent)
    this-&gt;connect(this, SIGNAL(clicked(bool)), this, SLOT(onClicked()));

鼠标点击事件如下:

// 没用到
void CheckBox::mousePressEvent(QMouseEvent *event)
void CheckBox::mouseReleaseEvent(QMouseEvent *event)
    isChecked = !isChecked; // 状态取反
    qDebug() &lt;&lt; "click status:" &lt;&lt; isChecked;
    emit clicked(isChecked); // 释放被点击信号,

onClicked槽函数如下:

void CheckBox::onClicked()
    this-&gt;update();

这里的QWidget::update()槽函数用于刷新绘图。绘图刷新的具体流程下文会提到。

第二步:重写QWidget::paintEvent方法

直接贴代码:

void CheckBox::paintEvent(QPaintEvent *event)
    Q_UNUSED(event);
    painter = new QPainter();
    painter-&gt;begin(this);
    // 创建一支笔,设置各种样式
    QPen pen;
    pen.setCapStyle(Qt::RoundCap);
    pen.setJoinStyle(Qt::RoundJoin);
    pen.setWidth(2);
    pen.setStyle(Qt::SolidLine);
    pen.setColor("#fff");
    painter-&gt;setPen(pen);
    // 画一个正方形作为checkbox的框框
    painter-&gt;drawRect(4,4,10,10);
//    QStyleOption option;
//    option.initFrom(this);
//    this-&gt;style()-&gt;drawPrimitive(QStyle::PE_IndicatorCheckBox, &amp;option, painter, this);
    // 写checkbox边上的label
    QRect rect(12, -1, 50, 20);
    painter-&gt;drawText(rect, Qt::AlignCenter, QString::fromLocal8Bit("选择框"));
    // 判断checkbox是否被选中,则画一个NIKE
    if (isChecked) {
        QPoint points[] = {
            QPoint(2, 10),
            QPoint(7, 15),
            QPoint(17, 5)
        painter-&gt;drawPolyline(points, 3);
    painter-&gt;end();

这里读者会发现这几行注释:

    QStyleOption option;
    option.initFrom(this);
    this-&gt;style()-&gt;drawPrimitive(QStyle::PE_IndicatorCheckBox, &amp;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控件图案。官方解释是: > 使用选项指定的样式选项,使用提供的画家绘制给定的基本元素。

跟我说的应该没有什么区别(逃)。

整个cpp文件放出

一个一个方法看太麻烦,所以:

#include "checkbox.h"
#include <qdebug>
#include <qpainter>
#include <qstyle>
#include <qstyleoption>
CheckBox::CheckBox(QWidget *parent) : QWidget(parent)
    this-&gt;connect(this, SIGNAL(clicked(bool)), this, SLOT(onClicked()));
void CheckBox::paintEvent(QPaintEvent *event)
    Q_UNUSED(event);
    painter = new QPainter();
    painter-&gt;begin(this);
    QPen pen;
    pen.setCapStyle(Qt::RoundCap);
    pen.setJoinStyle(Qt::RoundJoin);
    pen.setWidth(2);
    pen.setStyle(Qt::SolidLine);
    pen.setColor("#fff");
    painter-&gt;setPen(pen);
    painter-&gt;drawRect(4,4,10,10);
//    QStyleOption option;
//    option.initFrom(this);
//    this-&gt;style()-&gt;drawPrimitive(QStyle::PE_IndicatorCheckBox, &amp;option, painter, this);
    QRect rect(12, -1, 50, 20);
    painter-&gt;drawText(rect, Qt::AlignCenter, QString::fromLocal8Bit("选择框"));
    // 判断checkbox是否被选中,则画一个NIKE
    if (isChecked) {
        QPoint points[] = {
            QPoint(2, 10),
            QPoint(7, 15),
            QPoint(17, 5)
        painter-&gt;drawPolyline(points, 3);
    painter-&gt;end();
void CheckBox::mousePressEvent(QMouseEvent *event)
void CheckBox::mouseReleaseEvent(QMouseEvent *event)
    isChecked = !isChecked;
    qDebug() &lt;&lt; "click status:" &lt;&lt; isChecked;
    emit clicked(isChecked);
void CheckBox::onClicked()
    this->update();
bool CheckBox::getIsChecked() const
    return isChecked;

自己画框框

使用drawPrimitive