#ifndef TIMESELECT_H
#define TIMESELECT_H
#include <QWidget>
#include <QPropertyAnimation>
#include <QPainter>
#include <QMouseEvent>
#include <QDebug>
class timeSelect : public QWidget
{
Q_OBJECT
public:
explicit timeSelect(QWidget *parent = nullptr);
~timeSelect();
public:
void setRange(int min, int max);//设置范围
int readValue(); //获取当前值
QString getValueStr();//获取当前值
void setFormat(QString prefix,QString suffix);//设定显示格式,前缀和后缀
void setCurValue(int Value);
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void paintEvent(QPaintEvent * m_painter);
void paintNum(QPainter &painter,int num,int deviation,bool isMiddle); //绘制数字
void paintFrame(QPainter &painter);
void paintFrame_Line(QPainter &painter);
private:
int m_minRange; //最小值
int m_maxRange; //最大值
int m_currentValue; //当前选中的值
bool isDragging; //鼠标是否按下
int m_deviation; //偏移量,记录鼠标按下后移动的垂直距离
int m_mouseSrcPos; //鼠标源Y轴座标,用来计算偏移量
int m_numSize; //计算所得的数字字符尺寸,以最长的情况为准
bool isMiddle; //是否是中间的数字
QString m_prefix;//显示格式前缀
QString m_suffix;//显示格式后缀
QPropertyAnimation *homingAni;//QPropertyAnimation 是一个控制动画效果的类
void homing(); //使选中的数字回到屏幕中间
signals:
void currentValueChanged(int value); //当前数值改变信号
void deviationChange(float deviation); //偏移量改变信号
private slots:
void slotChoseFinshed();
};
#endif // TIMESELECT_H
cpp如下:
#include "timeselect.h"
timeSelect::timeSelect(QWidget *parent) :
QWidget(parent)
{
//初始化变量
m_minRange=1;
m_maxRange=100;
m_currentValue=80;
isDragging=0;
m_deviation=0;
m_numSize=6;
isMiddle=false;
m_prefix="";
m_suffix="";
homingAni = new QPropertyAnimation(this,""); //创建一个动画类
homingAni->setDuration(300); //设置动画持续时间
homingAni->setEasingCurve(QEasingCurve::OutQuad); //设置动画的缓动曲线,这里OutQuad是二次函数缓和曲线,减速到零速度
connect(homingAni,&QPropertyAnimation::finished,this,&timeSelect::slotChoseFinshed);
}
timeSelect::~timeSelect()
{
}
//设置范围
void timeSelect::setRange(int min, int max)
{
m_minRange = min;
m_maxRange = max;
if(m_currentValue < min) //如果选中的当前值小于最小值,就等于最小值,不会再减了
{
m_currentValue = min;
}
if(m_currentValue > max) //最大值同理
{
m_currentValue = max;
}
//计算字符尺寸
m_numSize = 3;
int temp = m_maxRange;
while(temp > 0)
{
temp /= 10;
m_numSize++;
}
update(); //重绘此组件,需要进行重绘时,要使用update
}
//获取当前值
int timeSelect::readValue()
{
return m_currentValue;
}
QString timeSelect::getValueStr()
{
QString strValue;
if(m_currentValue < 10){
strValue="0";
}
strValue+=QString::number(m_currentValue);
return strValue;
}
//设置显示格式
void timeSelect::setFormat(QString prefix, QString suffix)
{
m_prefix=prefix;
m_suffix=suffix;
}
//设置当前显示
void timeSelect::setCurValue(int Value)
{
//判断是否在有效区间内
if(Value > m_maxRange)
{
Value=m_maxRange;
}
else if(Value < m_minRange)
{
Value=m_minRange;
}
m_currentValue=Value;
if(this->isVisible())
{
update();
}
}
//鼠标按下事件
void timeSelect::mousePressEvent(QMouseEvent *event)
{
isDragging = true; //鼠标是否按下的标志位,布尔类型
m_mouseSrcPos = event->pos().y(); //鼠标按下时候的y轴作标记录下来
QWidget::mousePressEvent(event);
}
//鼠标移动事件
void timeSelect::mouseMoveEvent(QMouseEvent *event)
{
if(isDragging)
{
//数值到边界时,阻止继续往对应方向移动
if((m_currentValue == m_minRange && event->pos().y() >= m_mouseSrcPos)||
(m_currentValue == m_maxRange && event->pos().y() <= m_mouseSrcPos))
return;
m_deviation = event->pos().y() - m_mouseSrcPos; //垂直偏移量 = 现在的坐标 - 开始的作标
//若移动速度过快时进行限制
if(m_deviation > (height()-1)/5)
{
m_deviation = (height()-1)/5;
}
else if(m_deviation < -(height()-1)/5)
{
m_deviation = -(height()-1)/5;
}
emit deviationChange((float)m_deviation/((height()-1)/5)); //发出偏移量改变的信号,随着偏移量改变,重绘数字,就变成数字滚动的效果
update();
}
QWidget::mouseMoveEvent(event);
}
//鼠标松开事件
void timeSelect::mouseReleaseEvent(QMouseEvent *event)
{
if(isDragging) //如果鼠标松开了,那么还原一下鼠标按下标志位
{
homing();
isDragging = false;
update();
}
QWidget::mouseReleaseEvent(event);
}
//绘图事件
void timeSelect::paintEvent(QPaintEvent *m_painter)
{
QPainter painter(this); //创建画家类,指定绘图设备,也就是在哪画
painter.setRenderHint(QPainter::Antialiasing, true); //反走样,就是抗锯齿
paintFrame(painter);
int Width = width()-1; //设置宽高
int Height = height()-1;
if(m_deviation >= Height/5 && m_currentValue > m_minRange) //偏移量大于1/5 高的时候,数字减一
{
m_mouseSrcPos += Height/5; //鼠标起始位置重新设置,即加上1/5的高度
m_deviation -= Height/5; //偏移量重新设置,即减去1/5的高度
m_currentValue--;
}
if(m_deviation <= -Height/5 && m_currentValue < m_maxRange) //同理,数字加一
{
m_mouseSrcPos -= Height/5;
m_deviation += Height/5;
m_currentValue++;
}
//中间数字
paintNum(painter,m_currentValue,m_deviation,isMiddle=1); //将选中数字画到中间
//两侧数字1
if(m_currentValue != m_minRange) //选中的数字不是最小,不是最大,那么就有两侧数字,然后画出两侧数字
paintNum(painter,m_currentValue-1,m_deviation-Height*2/10,isMiddle=0);
if(m_currentValue != m_maxRange)
paintNum(painter,m_currentValue+1,m_deviation+Height*2/10,isMiddle=0);
//两侧数字2,超出则不显示
if(m_deviation >= 0 && m_currentValue-2 >= m_minRange)
paintNum(painter,m_currentValue-2,m_deviation-Height*4/10,isMiddle=0);
if(m_deviation <= 0 && m_currentValue+2 <= m_maxRange)
paintNum(painter,m_currentValue+2,m_deviation+Height*4/10,isMiddle=0);
//画边框,中间数字两侧的边框
#if 1
painter.setPen(QPen(QColor(5,39,175,120),4));
painter.drawLine(10,Height/8*3,Width-10,Height/8*3);
painter.drawLine(10,Height/8*5,Width-10,Height/8*5);
#else
paintFrame_Line(painter);
#endif
QWidget::paintEvent(m_painter);
}
//画数字函数
void timeSelect::paintNum(QPainter &painter, int num, int deviation, bool isMiddle)
{
int Width = this->width()-1;
int Height = this->height()-1;
int size = (Height - qAbs(deviation))/m_numSize; //qAbs 返回输入参数对应类型的绝对值。
int transparency = 255-510*qAbs(deviation)/Height; //设置透明度
int height = Height/2-3*qAbs(deviation)/5;
int y = Height/2+deviation-height/2;
QFont font;
font.setPixelSize(size); //设置像素大小
painter.setFont(font); //设置字体
if(isMiddle)
{
painter.setPen(QColor(34,215,187,transparency)); //设置画笔,基色
}
else
{
painter.setPen(QColor(0,0,0,transparency)); //设置画笔,黑色
}
QString str_date;
if(num<=9)
{
str_date=QString("0")+QString::number(num);
}
else
{
str_date=QString::number(num);
}
str_date=m_prefix+str_date+m_suffix;
painter.drawText(QRectF(0,y,Width,height), //画文本,参数:QRectF参数:位置xy,长宽大小;对齐方式,中间对齐;内容
Qt::AlignCenter,
str_date);
}
//使选中的数字回到屏幕中间
void timeSelect::homing()
{
//将数字矫正到中心
if(m_deviation > height()/20)
{
homingAni->setStartValue((height()-1)/8-m_deviation);
homingAni->setEndValue(0);
m_currentValue--;
}
else if(m_deviation > -(height())/20)
{
homingAni->setStartValue(m_deviation);
homingAni->setEndValue(0);
}
else if(m_deviation < -(height())/20)
{
homingAni->setStartValue(-(height()-1)/8-m_deviation);
homingAni->setEndValue(0);
m_currentValue++;
}
m_deviation=0;
// emit currentValueChanged(m_currentValue); //发送当前数值改变信号
homingAni->start(); //开始动画
}
//动画结束
void timeSelect::slotChoseFinshed()
{
emit currentValueChanged(m_currentValue); //发送当前数值改变信号
}
//画背景色
void timeSelect::paintFrame(QPainter &painter)
{
painter.save();
painter.setPen(Qt::NoPen);
QColor color;
color.setRgb(0,255,255,255);
painter.setBrush(color);
painter.drawRect(10,10,width()-10*2,height()-10*2);
painter.restore();
}
void timeSelect::paintFrame_Line(QPainter &painter)
{
painter.save();
qint32 space = 5;
QColor color;
color.setRgb(0,122,166,199);
QPen pen;
pen.setBrush(color);
pen.setWidth(4);
pen.setCapStyle(Qt::RoundCap);
pen.setStyle(Qt::SolidLine);
painter.setPen(pen);
if(0)
{
int up_down_space=height()/10;
QPoint line_left_up=QPoint(space+(width()-space*2)/3,space+up_down_space);
QPoint line_left_down=QPoint(space+(width()-space*2)/3,height()-space-up_down_space);
QPoint line_right_up=QPoint(space+((width()-space*2)/3)*2,space+up_down_space);
QPoint line_right_down=QPoint(space+((width()-space*2)/3)*2,height()-space-up_down_space);
painter.drawLine(line_left_up,line_left_down);//绘制左侧分割线
painter.drawLine(line_right_up,line_right_down);//绘制右侧分割线
}
else
{
int up_down_space=width()/10;
QPoint line_left_up=QPoint(space+up_down_space,space+(height()-space*2)/3);
QPoint line_left_down=QPoint(space+(width()-space*2)-up_down_space,space+(height()-space*2)/3);
QPoint line_right_up=QPoint(space+up_down_space,space+((height()-space*2)/3)*2);
QPoint line_right_down=QPoint(space+(width()-space*2)-up_down_space,space+((height()-space*2)/3)*2);
painter.drawLine(line_left_up,line_left_down);//绘制上侧分割线
painter.drawLine(line_right_up,line_right_down);//绘制下侧分割线
}
painter.restore();
}