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->connect(this, SIGNAL(clicked(bool)), this, SLOT(onClicked()));
鼠标点击事件如下:
// 没用到
void CheckBox::mousePressEvent(QMouseEvent *event)
void CheckBox::mouseReleaseEvent(QMouseEvent *event)
isChecked = !isChecked; // 状态取反
qDebug() << "click status:" << isChecked;
emit clicked(isChecked); // 释放被点击信号,
onClicked槽函数如下:
void CheckBox::onClicked()
this->update();
这里的QWidget::update()槽函数用于刷新绘图。绘图刷新的具体流程下文会提到。
第二步:重写QWidget::paintEvent方法
直接贴代码:
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控件图案。官方解释是: > 使用选项指定的样式选项,使用提供的画家绘制给定的基本元素。
跟我说的应该没有什么区别(逃)。
整个cpp文件放出
一个一个方法看太麻烦,所以:
#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