*最终效果:
*
一共三个文件: main.cpp , FancyCheckbox.h , FancyCheckbox.cpp
main.cpp
#include <QApplication>
#include "FancyCheckbox.h"
#include <QGridLayout>
int main(int argc, char *argv[])
{QApplication a(argc, argv);QWidget* w = new QWidget;w->setWindowTitle("cool checkbox");w->resize(600,400);auto* layout = new QGridLayout;layout->addWidget(new CheckboxSimple,0,0);layout->addWidget(new CheckboxLollipop,0,1);layout->addWidget(new CheckboxNike,0,2);layout->addWidget(new CheckboxTransform,1,0);layout->addWidget(new CheckboxBanner,1,1);layout->addWidget(new CheckboxMark,1,2);w->setLayout(layout);w->show();return a.exec();
}
FancyCheckbox.h
#ifndef CHECKBOXSIMPLE_H
#define CHECKBOXSIMPLE_H
#include <QPropertyAnimation>
#include <QWidget>
#include <QPaintEvent>
#include <QMouseEvent>class CheckboxBase:public QWidget
{Q_OBJECTQ_PROPERTY(qreal angle READ angle WRITE setAngle)
public:CheckboxBase(QWidget* parent=nullptr);virtual ~CheckboxBase();qreal angle()const;void setAngle(qreal an);protected:void enterEvent(QEvent* e);void leaveEvent(QEvent* e);QPropertyAnimation mAnim;qreal mAngle;bool mChecked;
};class CheckboxSimple : public CheckboxBase
{Q_OBJECT
public:explicit CheckboxSimple(QWidget *parent = nullptr);protected:void paintEvent(QPaintEvent* e);void mouseReleaseEvent(QMouseEvent* e);
};
class CheckboxLollipop : public CheckboxBase
{Q_OBJECT
public:explicit CheckboxLollipop(QWidget *parent = nullptr);protected:void paintEvent(QPaintEvent* e);void mouseReleaseEvent(QMouseEvent* e);
};
class CheckboxNike: public CheckboxBase{Q_OBJECT
public:explicit CheckboxNike(QWidget *parent = nullptr);protected:void paintEvent(QPaintEvent* e);void mouseReleaseEvent(QMouseEvent* e);
};
class CheckboxTransform: public CheckboxBase{Q_OBJECT
public:explicit CheckboxTransform(QWidget *parent = nullptr);protected:void paintEvent(QPaintEvent* e);void mouseReleaseEvent(QMouseEvent* e);
};class CheckboxBanner: public CheckboxBase{Q_OBJECT
public:explicit CheckboxBanner(QWidget *parent = nullptr);protected:void paintEvent(QPaintEvent* e);void mouseReleaseEvent(QMouseEvent* e);
};class CheckboxMark: public CheckboxBase{Q_OBJECT
public:explicit CheckboxMark(QWidget *parent = nullptr);protected:void paintEvent(QPaintEvent* e);void mouseReleaseEvent(QMouseEvent* e);
};#endif // CHECKBOXSIMPLE_H
FancyCheckbox.cpp
#include "FancyCheckbox.h"
#include <QPainter>
#include <QPainterPath>
#include <QPalette>
#include <math.h>
CheckboxBase::CheckboxBase(QWidget* parent):QWidget(parent),mAngle(0),mChecked(false)
{mAnim.setPropertyName("angle");mAnim.setTargetObject(this);mAnim.setDuration(2000);mAnim.setLoopCount(1);mAnim.setEasingCurve(QEasingCurve::Linear);setMouseTracking(true);
}
CheckboxBase::~CheckboxBase(){}qreal CheckboxBase::angle()const{ return mAngle;}
void CheckboxBase::setAngle(qreal an){mAngle = an;update();
}
void CheckboxBase::enterEvent(QEvent* e){setCursor(Qt::PointingHandCursor);
}
void CheckboxBase::leaveEvent(QEvent* e){setCursor(Qt::ArrowCursor);
}CheckboxSimple::CheckboxSimple(QWidget *parent) : CheckboxBase(parent)
{setFixedSize(40,24);mAnim.setDuration(100);mAngle = 360;
}void CheckboxSimple::paintEvent(QPaintEvent* e){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);int w = width();int h = height();painter.setPen(Qt::NoPen);if(mChecked){painter.setBrush(QBrush(QColor("lightgreen")));}else{painter.setBrush(QBrush(QColor("lightgray")));}painter.drawRoundedRect(rect(),h/2,h/2);painter.setBrush(QBrush(QColor("white")));const int gap = 3;if(mChecked){painter.translate((w-h)*mAngle/360+h/2,h/2);}else{painter.translate(w-h/2-(w-h)*mAngle/360 , h/2);}painter.drawEllipse(QPoint(0,0),h/2-gap,h/2-gap);
}
void CheckboxSimple::mouseReleaseEvent(QMouseEvent* e){if(mAnim.state() == QAbstractAnimation::Running) return;mChecked = !mChecked;mAnim.setStartValue(0);mAnim.setEndValue(360);mAnim.start();
}
CheckboxLollipop::CheckboxLollipop(QWidget *parent) : CheckboxBase(parent)
{setFixedSize(80,72);mAnim.setDuration(160);mAngle = 360;
}void CheckboxLollipop::paintEvent(QPaintEvent* e){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);int w = width();int h = height();painter.setPen(Qt::NoPen);if(mChecked){painter.setBrush(QBrush(QColor("cornflowerblue")));}else{painter.setBrush(QBrush(QColor("gray")));}const auto r = 0.16 * h;const auto R = r + 0.25*w;painter.translate(0.25*w,0.4*h);if(mChecked){if(mAngle <= 180){//move to rightsidepainter.drawRoundedRect(QRectF(0,0,0.5*w,0.2*h),0.1*h,0.1*h);painter.setBrush(QBrush(QColor("blueviolet")));painter.translate((0.5*w - 2*r) * mAngle / 180 + r , 0.1*h);painter.drawEllipse(QPointF(0,0),r,r);}else{//boom!painter.translate(0.5*w - r,0.1*h);QRadialGradient rad(QPointF(0,0),R,QPointF(0,0));QColor base("blueviolet");base.setAlphaF(0.3);rad.setColorAt(0,base);rad.setColorAt(1,"transparent");QColor eraseColor("white");QWidget * pa = dynamic_cast<QWidget*>( parent() );if(pa){eraseColor = pa->palette().color(QPalette::Window);}painter.setBrush(rad);painter.drawEllipse(QPointF(0,0) , R , R);//画一个固定的渐变圈painter.setBrush(QBrush(eraseColor));auto out = r + (R -r) * ( mAngle - 180) / 180;painter.drawEllipse(QPointF(0,0), out,out);//画一个逐渐变大的圈把渐变圈覆盖掉painter.resetTransform();painter.translate(0.25*w,0.4*h);painter.setBrush(QBrush(QColor("cornflowerblue")));painter.drawRoundedRect(QRectF(0,0,0.5*w,0.2*h),0.1*h,0.1*h);painter.setBrush(QBrush(QColor("blueviolet")));painter.translate((0.5*w - r) , 0.1*h);painter.drawEllipse(QPointF(0,0),r,r);}}else{painter.drawRoundedRect(QRectF(0,0,0.5*w,0.2*h),0.1*h,0.1*h);painter.setBrush(QBrush(QColor("white")));painter.translate(0.5*w-r-((0.5*w-2*r) * mAngle/360),0.1*h);painter.drawEllipse(QPointF(0,0),r,r);}}
void CheckboxLollipop::mouseReleaseEvent(QMouseEvent* e){if(mAnim.state() == QAbstractAnimation::Running) return;mChecked = !mChecked;mAnim.setStartValue(0);mAnim.setEndValue(360);mAnim.start();
}CheckboxNike::CheckboxNike(QWidget *parent){setFixedSize(32,32);mAngle = 360;
}void CheckboxNike::paintEvent(QPaintEvent* e){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setPen(Qt::NoPen);const int w = width();const int h = height();const int r = 8;if(mChecked){painter.setBrush(QBrush(QColor("lightblue")));painter.drawRoundedRect(rect() , r,r);QPainterPath cover;cover.addRect(QRectF(0,0,w*mAngle/360,h));painter.setClipPath(cover);painter.setPen(Qt::white);painter.setFont(QFont("Microsoft YaHei",12));painter.drawText(rect() , Qt::AlignCenter , "✔");}else{QColor baseBack("lightblue");QColor baseFront("white");baseBack.setAlphaF(1 - mAngle / 360);baseFront.setAlphaF(1 - mAngle / 360);painter.setBrush(baseBack);painter.drawRoundedRect(rect() , r,r);painter.setPen(baseFront);painter.setFont(QFont("Microsoft YaHei",12));painter.drawText(rect() , Qt::AlignCenter , "✔");QPen pen(QBrush("lightblue"),4);painter.setPen(pen);painter.setBrush(Qt::NoBrush);painter.drawRoundedRect(rect().adjusted(2,2,-2,-2) , r,r);}
}
void CheckboxNike::mouseReleaseEvent(QMouseEvent* e){if(mAnim.state() == QAbstractAnimation::Running) return;mChecked = !mChecked;mAnim.setStartValue(0);mAnim.setEndValue(360);if(mChecked){mAnim.setDuration(200);}else{mAnim.setDuration(100);}mAnim.start();
}CheckboxTransform::CheckboxTransform(QWidget *parent){setFixedSize(40,40);mAngle = 360;mAnim.setDuration(200);
}void CheckboxTransform::paintEvent(QPaintEvent* e){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);QPen pen("black");pen.setWidth(2);painter.setPen(pen);painter.setBrush(Qt::NoBrush);const double w = width();const double h = height();painter.translate(0.25*w,0.75*h);static constexpr int N = 20;if(mChecked){double theta = -M_PI_4 * mAngle / 360;//在数学中,逆时针角度是正,在计算机中,y轴朝下,逆时针的角度为负数double scale = 1 - (2-sqrt(2)/2)/2 / 360 * mAngle;for(int i = 0 ;i <= N;++i){double x = 0;double y = -h/2/N * i;auto x2 = scale * ( cos(theta) * x - sin(theta) * y);auto y2 = scale * ( sin(theta) * x + cos(theta) * y);painter.drawPoint(QPointF(x2,y2));}theta *= -1;for(int i = 0;i <= N;++i){double x = w/2/N*i;double y = 0;auto x2 = scale * ( cos(theta) * x - sin(theta) * y);auto y2 = scale * ( sin(theta) * x + cos(theta) * y);painter.drawPoint(QPointF(x2,y2));}painter.resetTransform();painter.translate(0.75*w, 0.25*h);theta = atan(2.0/3) * mAngle / 360;scale = 1- (2-sqrt(3.25))/2 * mAngle / 360;double dx = -0.125*w * mAngle / 360;double dy = 0.25*h * mAngle / 360;for(int i = 0;i <= N;i++){double x = 0;double y = 0.5*h/N * i;auto x2 = scale * ( cos(theta) * x - sin(theta) * y) + dx;auto y2 = scale * ( sin(theta) * x + cos(theta) * y) + dy;painter.drawPoint(QPointF(x2,y2));}theta = -atan(1.5) * mAngle / 360;dx *= -1;dy = -0.125*h * mAngle / 360;for(int i = 0;i <= N;i++){double x = -0.5*w * i / N;double y = 0;auto x2 = scale * ( cos(theta) * x - sin(theta) * y) + dx;auto y2 = scale * ( sin(theta) * x + cos(theta) * y) + dy;painter.drawPoint(QPointF(x2,y2));}}else{double theta = M_PI_4 * mAngle / 360;double scale = 1+ (2*sqrt(2) - 1) * mAngle / 360;for(int i = 0 ;i <= N;++i){double x = -0.125*w/N * i;double y = -0.125*h/N * i;auto x2 = scale * ( cos(theta) * x - sin(theta) * y);auto y2 = scale * ( sin(theta) * x + cos(theta) * y);painter.drawPoint(QPointF(x2,y2));}theta *= -1;for(int i = 0 ;i <= N;++i){double x = 0.125*w/N * i;double y = 0.125*h/N * i;auto x2 = scale * ( cos(theta) * x - sin(theta) * y);auto y2 = scale * ( sin(theta) * x + cos(theta) * y);painter.drawPoint(QPointF(x2,y2));}painter.resetTransform();painter.translate(0.625*w, 0.5*h);theta = -atan(2.0/3) * mAngle / 360;scale = 1 + (2.0/sqrt(3.25) - 1) * mAngle / 360;double dx = 0.125*w * mAngle / 360;double dy = -0.25*h * mAngle / 360;for(int i = 0;i <= N;i++){double x = -0.25*w * i/N;double y = 0.375*h * i / N;auto x2 = scale * ( cos(theta) * x - sin(theta) * y) + dx;auto y2 = scale * ( sin(theta) * x + cos(theta) * y) + dy;painter.drawPoint(QPointF(x2,y2));}theta = atan(1.5) * mAngle / 360;dx= -0.375*w * mAngle / 360;dy = -0.25*h * mAngle / 360;for(int i = 0;i <= N;i++){double x = 0.25*w * i / N;double y = -0.375*h * i / N;auto x2 = scale * ( cos(theta) * x - sin(theta) * y) + dx;auto y2 = scale * ( sin(theta) * x + cos(theta) * y) + dy;painter.drawPoint(QPointF(x2,y2));}}
}
void CheckboxTransform::mouseReleaseEvent(QMouseEvent* e){if(mAnim.state() == QAbstractAnimation::Running) return;mChecked = !mChecked;mAnim.setStartValue(0);mAnim.setEndValue(360);mAnim.start();
}CheckboxBanner::CheckboxBanner(QWidget *parent){setFixedSize(75,30);mAnim.setDuration(300);mAngle = 360;
}void CheckboxBanner::paintEvent(QPaintEvent* e){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setPen(Qt::NoPen);const int w = width();const int h = height();if(mChecked){painter.setBrush(QBrush("lightgreen"));}else{painter.setBrush(QBrush("gray"));}QPainterPath pp;pp.moveTo(w/6,0);pp.lineTo(w,0);pp.lineTo(5.0/6*w,h);pp.lineTo(0,h);pp.lineTo(w/6,0);pp.closeSubpath();painter.drawPath(pp);QPen pen("white");pen.setWidth(4);painter.setPen(pen);painter.setFont(QFont("Microsoft YaHei",12));if(mChecked){//move to leftpainter.translate(w*(1 - sin(M_PI_2 * mAngle/360)),0);painter.drawText(QRect(0,0,w,h),Qt::AlignCenter, "ON");}else{painter.translate(w*(sin(M_PI_2 * mAngle/360) -1),0);painter.drawText(QRect(0,0,w,h),Qt::AlignCenter, "OFF");}
}
void CheckboxBanner::mouseReleaseEvent(QMouseEvent* e){if(mAnim.state() == QAbstractAnimation::Running) return;mChecked = !mChecked;mAnim.setStartValue(0);mAnim.setEndValue(360);mAnim.start();
}CheckboxMark::CheckboxMark(QWidget *parent){setFixedSize(40,40);mAnim.setDuration(300);mAngle = 360;
}void CheckboxMark::paintEvent(QPaintEvent* e){QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setBrush(Qt::NoBrush);const int w = width();const int h = height();QPen pen;pen.setWidth(2);if(mChecked){pen.setColor("lightgreen");}else{pen.setColor("gray");}painter.setPen(pen);painter.translate(0,0.2*h);painter.drawRoundedRect(QRectF(0,0,0.8*w,0.8*h).adjusted(2,2,-2,-2),4,4);painter.resetTransform();painter.translate(0.125*w,0.5*h);if(mChecked){QPen pen("lightgreen");pen.setWidth(6);pen.setCapStyle(Qt::RoundCap);pen.setJoinStyle(Qt::RoundJoin);painter.setPen(pen);if(mAngle <= 120){int dw = 0.25*w * mAngle / 120;int dh = 0.25*h * mAngle / 120;painter.drawLine(0,0,dw,dh);}else{painter.drawLine(0,0,0.25*w,0.25*h);auto rat = (mAngle - 120) / 240.0;painter.resetTransform();painter.translate(0.375*w,0.75*h);int dw = 0.5*w * rat;int dh = 0.5*h * rat;painter.drawLine(0,0,dw,-dh);}}else{}}
void CheckboxMark::mouseReleaseEvent(QMouseEvent* e){if(mAnim.state() == QAbstractAnimation::Running) return;mChecked = !mChecked;mAnim.setStartValue(0);mAnim.setEndValue(360);mAnim.start();
}