1、开关按钮效果图
2、自定义开关按钮的实现
首先是使用Q_PROPERTY将开关按钮的属性注册到Qt的元对象系统重,方便后续将开关控件集成到QtDesigner设计师界面中:
如下是开关控件的属性:
在绘制时,首先将上面属性中的颜色初始化:
然后就是通过paintEvent这个绘图事件来进行绘制,首先绘制开关按钮的背景,它的背景是一个圆角矩形,然后是绘制内部的小椭圆,小椭圆的位置在背景的左侧,当然不是完全贴合,小椭圆与背景之间有外部边距,如下是代码实现:
void SwitchButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
QPainterPath path;
QColor backsColor;
QColor circlesColor;
//设置选中和未被选的画笔颜色
if(this->isEnabled())
{
if(checked)
{
backsColor = pressedColor;
circlesColor = QColor(Qt::white);
}
else
{
backsColor = backgroundColor;
circlesColor = circleColor;
}
}
else
{
backsColor = backgroundColor;
circlesColor = disabledColor;
}
//绘制外部大圆
painter.setBrush(backsColor);
path.addRoundedRect(QRectF(0,0,this->width(),this->height()),qMin(this->width(),this->height())/2,qMin(this->width(),this->height())/2);
painter.drawPath(path.simplified());
//绘制内圆
painter.setBrush(circlesColor);
painter.setOpacity(1.0);
painter.drawEllipse(QRectF(margins + xPos, margins, height() - margins*2, height() - margins* 2));
}
绘制效果如下:
上面使用绘图事件只实现了静态的开关按钮,点击无反应,下面是通过鼠标点击事件和鼠标松开事件以及状态改变的函数来实现开关按钮点击后动态变化的效果:
//鼠标点击(左键点击)
void SwitchButton::mousePressEvent(QMouseEvent *event)
{
if(this->isEnabled())
{
if(event->buttons() & Qt::LeftButton)
{
event->accept();
}
else
{
event->ignore();
}
}
}
//鼠标松开
void SwitchButton::mouseReleaseEvent(QMouseEvent *event)
{
if(this->isEnabled() && this->isFinish())
{
if(event->type() == QMouseEvent::MouseButtonRelease && event->button() == Qt::LeftButton)
{
event->accept();
checked =! checked; //改变状态
changeState(checked);//状态改变后,更改动画效果
emit toggled(checked);
}
else
{
event->ignore();
}
}
}
//状态改变函数
void SwitchButton::changeState(bool state)
{
if(state)
{
proAnimation->setStartValue(0);
proAnimation->setEndValue(this->width()-this->height());
}
else
{
proAnimation->setStartValue(this->width()-this->height());
proAnimation->setEndValue(0);
}
proAnimation->start();
}
3、完整代码
SwitchButton.h如下:
#ifndef SWITCHBUTTON_H
#define SWITCHBUTTON_H
#include <QWidget>
#include<QPainter>
#include<QMouseEvent>
#include<QPropertyAnimation>
#include<QResizeEvent>
#include<QtMath>
class SwitchButton : public QWidget
{
Q_OBJECT
public:
explicit SwitchButton(QWidget *parent = nullptr);
//将属性注册到元对象系统中
Q_PROPERTY(QColor backgroundColor READ getBackColor WRITE setBackColor)
Q_PROPERTY(QColor pressedColor READ getPressedColor WRITE setPressedColor)
Q_PROPERTY(QColor disabledColor READ getDisabledColor WRITE setDisabledColor)
Q_PROPERTY(QColor circleColor READ getCircleColor WRITE setCircleColor)
Q_PROPERTY(qreal xPos READ getXPos WRITE setXPos)
public:
bool isToggled()const;
void setToggled(bool checked);
void setBackColor(QColor color);
QColor getBackColor();
void setPressedColor(QColor color);
QColor getPressedColor();
void setDisabledColor(QColor color);
QColor getDisabledColor();
void setCircleColor(QColor color);
QColor getCircleColor();
void setXPos(qreal xPos);
qreal getXPos();
void changeState(bool state);
bool isFinish() const;
protected:
virtual void paintEvent(QPaintEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
signals:
void toggled(bool checked);
private:
QColor backgroundColor; //背景颜色
QColor pressedColor; //点击时的背景颜色
QColor disabledColor; //无法选中的背景颜色
QColor circleColor; //小椭圆的颜色
qreal xPos; //小椭圆在背景上面的x轴的运动路径
bool checked; //是否选中小椭圆
int margins; //外边距
QPropertyAnimation* proAnimation; //动画对象
};
#endif // SWITCHBUTTON_H
Switch.cpp如下:
#include "switchbutton.h"
SwitchButton::SwitchButton(QWidget *parent) : QWidget(parent),
backgroundColor(247,245,241),
pressedColor(26,118,197),
disabledColor(190,190,190),
circleColor(93,93,93),
checked(false),
margins(5)
{
this->setCursor(Qt::PointingHandCursor);
this->resize(400,200);
proAnimation = new QPropertyAnimation(this,"xPos");
proAnimation->setDuration(300);
proAnimation->setLoopCount(1);
}
bool SwitchButton::isToggled() const
{
return this->checked;
}
void SwitchButton::setToggled(bool checked)
{
if(this->checked == checked)
{
return;
}
this->checked = checked;
changeState(this->checked);
}
void SwitchButton::setBackColor(QColor color)
{
this->backgroundColor = color;
}
QColor SwitchButton::getBackColor()
{
return this->backgroundColor;
}
void SwitchButton::setPressedColor(QColor color)
{
this->pressedColor = color;
}
QColor SwitchButton::getPressedColor()
{
return this->pressedColor;
}
void SwitchButton::setDisabledColor(QColor color)
{
this->disabledColor = color;
}
QColor SwitchButton::getDisabledColor()
{
return this->disabledColor;
}
void SwitchButton::setCircleColor(QColor color)
{
this->circleColor = color;
}
QColor SwitchButton::getCircleColor()
{
return this->circleColor;
}
void SwitchButton::setXPos(qreal xPos)
{
this->xPos = xPos;
update();
}
qreal SwitchButton::getXPos()
{
return this->xPos;
}
void SwitchButton::changeState(bool state)
{
if(state)
{
proAnimation->setStartValue(0);
proAnimation->setEndValue(this->width()-this->height());
}
else
{
proAnimation->setStartValue(this->width()-this->height());
proAnimation->setEndValue(0);
}
proAnimation->start();
}
bool SwitchButton::isFinish() const
{
return proAnimation->state() == QAbstractAnimation::Stopped;
}
void SwitchButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
QPainterPath path;
QColor backsColor;
QColor circlesColor;
//设置选中和未被选的画笔颜色
if(this->isEnabled())
{
if(checked)
{
backsColor = pressedColor;
circlesColor = QColor(Qt::white);
}
else
{
backsColor = backgroundColor;
circlesColor = circleColor;
}
}
else
{
backsColor = backgroundColor;
circlesColor = disabledColor;
}
//绘制外部大圆
painter.setBrush(backsColor);
path.addRoundedRect(QRectF(0,0,this->width(),this->height()),qMin(this->width(),this->height())/2,qMin(this->width(),this->height())/2);
painter.drawPath(path.simplified());
//绘制内圆
painter.setBrush(circlesColor);
painter.setOpacity(1.0);
painter.drawEllipse(QRectF(margins + xPos, margins, height() - margins*2, height() - margins* 2));
}
void SwitchButton::mousePressEvent(QMouseEvent *event)
{
if(this->isEnabled())
{
if(event->buttons() & Qt::LeftButton)
{
event->accept();
}
else
{
event->ignore();
}
}
}
void SwitchButton::mouseReleaseEvent(QMouseEvent *event)
{
if(this->isEnabled() && this->isFinish())
{
if(event->type() == QMouseEvent::MouseButtonRelease && event->button() == Qt::LeftButton)
{
event->accept();
checked =! checked; //改变状态
changeState(checked);//状态改变后,更改动画效果
emit toggled(checked);
}
else
{
event->ignore();
}
}
}
void SwitchButton::resizeEvent(QResizeEvent *event)
{
this->xPos = checked ? this->width() - this->height() : 0;
QWidget::resizeEvent(event);
}
4、将开关按钮集成到QtDesigner中
首先查看Qt基于哪种编译器:
然后选择Qt4设计师自定义控件。
然后是选择项目名称和位置,注意不要有中文路径。
选择上面的msvc2017 32bit的编译套件。
自定义控件名称,注意首字母大写。
设置开关按钮属于哪个组类。
最后点击完成即可。
Qt自动生成控件相关的文件:
将上面的开关按钮的代码拷贝到swichbutton.h和switchbutton.cpp中,使用Release模式编译生成。然后再release文件夹下找到开关按钮的dll文件:
最后将它拷贝到如下Qt的designer目录下:
最后随便创建一个qt项目,然后在ui文件中可以发现开关按钮:
它的属性也同样在ui界面可以修改(通过Q_PROPERTY将属性注册到qt元对象系统):
但点击运行项目时会出错:
原因很简单,因为我们没有包含它的头文件和源文件,将头文件和源文件复制到项目中,即可:
然后再pro文件中添加如下语句:
最后运行成功,效果如下: