记录上位机的Qt开发(二):自定义滑动开关控件
前言
需求:在开发的上位机上,有一“保存文件”的功能,要求点击“保存文件”QAction,弹出一个对话框,对话框包含一个开关选择是否保存文件、一个按钮弹出文件选择器选择存储路径、一对“OK”、“Cancel”选择本次选择的确定或取消。
一、自定义滑动开关类
介绍一下重要的函数和源码。
1. 设置开关状态
(1)状态切换:将赋予的参数(bool类型表示开或关的状态)以类中私有成员的形式保存下来。
(2)将开关背景颜色属性动画化:
定义QPropertyAnimation动画化对象的自定义属性——名为“pBackColor”的自定义背景颜色属性,其由Q_PROPERTY宏在类的元对象中定义的,即相当于为该类注册了一个自定义属性。
设置动画持续时间,设置动画的默认(起始)值,即打开时开关的背景色,再根据开关的值设置动画的结束颜色。
最后启动动画。
(3)将开关按钮位置动画化:
定义QVariantAnimation动画化对象的按钮位置。
设置动画持续时间,设置动画的默认(起始)值,即打开开关时的位置,再根据开关的值设置动画的结束位置。
通过信号和槽,在动画值变化时,更新按钮位置,重绘界面。
启动动画。
函数源码
void SwitchButton::setSwitch(bool onoff){
//状态切换
mOnOff = onoff;
/// 动画-背景颜色
//用于动画化 SwitchButton 对象的背景颜色属性
QPropertyAnimation * colorAnimation = new QPropertyAnimation(this, "pBackColor");
colorAnimation->setDuration(mAnimationPeriod); //设置动画的持续时间
colorAnimation->setStartValue(mBackColor); //设置动画的起始值,表示当前背景颜色
//根据mOnOff的值设置动画的结束颜色。如果mOnOff为true(即开关打开),则结束颜色为mBackOnColor;否则为mBackOffColor。
colorAnimation->setEndValue(mOnOff ? mBackOnColor : mBackOffColor);
//启动动画,并设置当动画停止时自动删除colorAnimation对象
colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
/// 动画-开关按钮位置 动画用于改变一个按钮(或某个元素)的位置
QVariantAnimation* posAnimation = new QVariantAnimation(this);
posAnimation->setDuration(mAnimationPeriod); //设置了动画的持续时间,单位是毫秒
posAnimation->setStartValue(mButtonRect.topLeft()); //起始值是按钮的当前位置,左侧
//结束值取决于 mOnOff 的值。如果 mOnOff 为真(开),则结束值为 mRightPos;否则,为 mLeftPos
posAnimation->setEndValue(mOnOff ? mRightPos : mLeftPos);
//信号在动画的属性值发生变化时被发射
connect(posAnimation, &QPropertyAnimation::valueChanged, [=](const QVariant &value){
mButtonRect.moveTo(value.toPointF()); //更新按钮的位置
update(); //重绘界面
});
//启动动画。动画结束后,对象会被自动删除
posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
}
2. 重写绘制事件
通过QPainter绘制背景颜色、圆形按钮、按钮阴影等。
函数源码
void SwitchButton::paintEvent(QPaintEvent *event){
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true); //启用抗锯齿
painter.setPen(Qt::NoPen); //意味着接下来的绘制操作不会绘制线条或轮廓
/// 绘制背景颜色
painter.setBrush(mBackColor);
//绘制一个圆角矩形,填充颜色为mBackColor,边界与SwitchButton控件相同,圆角大小为mRadius
painter.drawRoundedRect(this->rect(), mRadius, mRadius);
/// 绘制圆形按钮
painter.setBrush(mButtonColor);
//绘制一个椭圆,填充颜色为mButtonColor,这个椭圆的边界由mButtonRect确定
painter.drawEllipse(mButtonRect);
/// 绘制按钮阴影
painter.setBrush(Qt::NoBrush);
QColor color(Qt::black);
//控件高度和按钮高度差的一半,用于后面的循环中计算阴影的大小
int count = (this->height() - mButtonRect.height()) / 2;
float stepColor = (0.15 - 0.0) / count; //用于在后面的循环中逐渐改变阴影的透明度
//循环,从按钮高度的一半开始,到控件高度的一半结束
for (int i = mButtonRect.height() / 2 + 1; i < this->height() / 2; i++){
//根据循环的迭代次数i计算color的透明度
color.setAlphaF(0.15 - stepColor * (i - mButtonRect.height() / 2));
painter.setPen(color);
//在按钮的中心绘制一个椭圆,椭圆的半径随着循环的迭代而逐渐增大,形成阴影效果
painter.drawEllipse(mButtonRect.center(), i, i);
}
}
3. 重写控件大小改变时事件
直接上函数源码
void SwitchButton::resizeEvent(QResizeEvent *event){
Q_UNUSED(event)
/// 更新按钮大小、圆角大小、动画两个位置
int size = qMin(this->width(), this->height()); //计算控件的宽度和高度中的最小值
mRadius = size / 2; //设置圆角半径mRadius为size的一半
float width = size * 3 / 4; //计算按钮的宽度为size的四分之三
float border = (size - width) / 2; //计算按钮边缘的宽度,为size与按钮宽度的差的一半
mLeftPos = QPoint(border, border); //设置按钮在左侧时的起始位置
mRightPos = QPoint(this->width() - border - width, border); //设置按钮在右侧时的起始位置
mButtonRect.setWidth(width); //设置按钮矩形的宽度和高度
mButtonRect.setHeight(width);
mButtonRect.moveTo(mOnOff ? mRightPos : mLeftPos); //根据mOnOff的值(开关的状态)将按钮移动到左侧或右侧位置
mBackColor = mOnOff ? mBackOnColor : mBackOffColor; //设置背景颜色,根据开关状态选择
update(); //触发控件的重绘
}
二、自定义对话框类
这个类就简单了,继承自QDialog类,包含弹出文件选择器的按钮、显示和保存存储路径的行编辑器、代表“确定”、“取消”的OK/Cancel按钮,以及自定义滑动开关控件,将这些控件进行设计、布局,最终在需要的地方创建自定义对话框类的实例即可使用。
总结
本篇博客根据网上资料 + 个人使用总结,若有错误请指正,大家一起讨论。