Qt滚动日期选择器

滚动日期选择器
最近产品经理提了一个需求,要实现一个滚动日期选择器。这个功能看似简单,实际实现起来不容易,里面有一些细节需要注意。

目前网上很难找到类似的控件,只能找到单独的一条控件
这意味着又要开始造轮子了 ( ・᷄д・᷅ )

我先实现了单条的纵向控件,分别实现年、月、日独自的上下拖拽,最后把它们整合在一起。代码比较繁琐,不够优雅,不过功能还是实现了的 ✌ ・︵・

年份滚动条
rollingboxy.h

//时间滚动条-年份

#ifndef ROLLINGBOXY_H
#define ROLLINGBOXY_H

#include <QWidget>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QTimer>
#include <QDateTime>

class RollingBoxY : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(int deviation READ readDeviation WRITE setDeviation)

public:
    explicit RollingBoxY(QWidget *parent = 0);

    void setRange(int min,int max); //设置范围
    int y_value;

public slots:
    int readValue();                //获取当前值

private:
    QTimer *r_time;

protected:
    int m_minRange;
    int m_maxRange;
    int m_currentValueY = QDateTime::currentDateTime().toString("yyyy").toInt();
    bool isDragging;
    int m_deviation;
    int m_mouseSrcPos;
    int m_numSize;//计算所得的数字字符尺寸,以最长的情况为准
    QPropertyAnimation *homingAni;

    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
    void mouseReleaseEvent(QMouseEvent *);
//    void wheelEvent(QWheelEvent *);
    void paintEvent(QPaintEvent *);
    void paintNum(QPainter &painter,int num,int deviation);     //绘制数字
    void homing();                                              //使选中的数字回到屏幕中间
    int readDeviation();                                        //获取鼠标移动偏移量,默认为0
    void setDeviation(int n);                                   //设置偏移量
signals:
    void currentValueChanged(int value);                        //当前数值改变信号
    void deviationChange(float deviation);                      //偏移量改变信号
    int sendYear(int);
};

#endif // ROLLINGBOXY_H

rollingboxy.cpp

//时间滚动条-年份

#include <rollingboxy.h>
#include <QTimer>

RollingBoxY::RollingBoxY(QWidget *parent) :
    QWidget(parent),
    m_minRange(1950),         //最小值
    m_maxRange(2050),         //最大值
//    m_currentValueY(2020),      //当前选中的值
    isDragging(0),          //鼠标是否按下
    m_deviation(0),         //偏移量,记录鼠标按下后移动的垂直距离
    m_numSize(6)            //计算所得的数字字符尺寸,以最长的情况为准
{
    this -> setWindowFlags(windowFlags() &~Qt::WindowMinMaxButtonsHint);
    this -> setStyleSheet("background:white;");
    homingAni = new QPropertyAnimation(this,"deviation");      //一个控制动画效果的类
    homingAni->setDuration(300);                                //设置动画持续时间
    homingAni->setEasingCurve(QEasingCurve::OutQuad);           //设置动画的缓动曲线,这里OutQuad是二次函数缓和曲线,减速到零
}

//设置范围
void RollingBoxY::setRange(int min,int max)
{
    m_minRange = min;
    m_maxRange = max;

    if(m_currentValueY < min)        //如果选中的当前值小于最小值,就等于最小值
        m_currentValueY = min;
    if(m_currentValueY > max)        //最大值同理
        m_currentValueY = max;

    //计算字符尺寸
    m_numSize = 3;
    int temp = m_maxRange;
    while(temp > 0)
    {
        temp /= 10;
        m_numSize++;
    }

    repaint();      //重绘此组件,需要进行重绘时,要使用repaint
}

//获取当前值
int RollingBoxY::readValue()
{
    return m_currentValueY;
}

//鼠标按下事件
void RollingBoxY::mousePressEvent(QMouseEvent *e)
{
    homingAni->stop();      //动画停止
    isDragging = 1;         //鼠标是否按下的标志位,布尔类型
    m_mouseSrcPos = e->pos().y();   //鼠标按下时候的y轴坐标记录下来
}

//鼠标移动事件
void RollingBoxY::mouseMoveEvent(QMouseEvent *e)
{
    if(isDragging)
    {
        //数值到边界时,阻止继续往对应方向移动
        if((m_currentValueY == m_minRange && e->pos().y() >= m_mouseSrcPos)||
          (m_currentValueY == m_maxRange && e->pos().y() <= m_mouseSrcPos))
            return;

        m_deviation = e->pos().y() - m_mouseSrcPos;     //垂直偏移量 = 现在的坐标 - 开始的坐标

        //若移动速度过快时进行限制
        if(m_deviation > (height()-1)/4)
            m_deviation = (height()-1)/4;
        else if(m_deviation < -(height()-1)/4)
            m_deviation = -(height()-1)/4;

        emit deviationChange((float)m_deviation/((height()-1)/5));      //发出偏移量改变的信号,随着偏移量改变,重绘数字,就变成了数字滚动的效果

        repaint();
    }
}

//鼠标松开事件
void RollingBoxY::mouseReleaseEvent(QMouseEvent *)
{
    if(isDragging)      //如果鼠标松开了,那么还原鼠标按下标志位
    {
        isDragging = 0;
        homing();       //使选中的数字回到屏幕中间
    }
}

//void RollingBoxM::wheelEvent(QWheelEvent *e)
//{
//    if(e->delta()>0)
//    {
//        m_deviation = (this->height() -1)/4;
//    }
//    else{
//        m_deviation = -(this->height() -1)/4;
//    }
//    homing();
//    repaint();
//}

//绘图事件
void RollingBoxY::paintEvent(QPaintEvent *)
{
    QPainter painter(this);     //创建画家类,指定绘图设备
    painter.setRenderHint(QPainter::Antialiasing, true);    //抗锯齿

    int Width = width()-1;
    int Height = height()-1;

    if(m_deviation >= Height/4 && m_currentValueY > m_minRange)  //偏移量大于1/4的时候,数字减一
    {
        m_mouseSrcPos += Height/4;  //鼠标起始位置重新设置,即加上1/4的高度
        m_deviation -= Height/4;    //偏移量重新设置,即减去1/4的高度
        m_currentValueY -= 1;
    }
    if(m_deviation <= -Height/4 && m_currentValueY < m_maxRange) //同理,数字加一
    {
        m_mouseSrcPos -= Height/4;
        m_deviation += Height/4;
        m_currentValueY += 1;
    }

    //中间数字
    paintNum(painter,m_currentValueY,m_deviation);   //将选中数字画到中间

    //两侧数字1
    if(m_currentValueY != m_minRange)    //选中的数字不是最小,不是最大,那么就有两侧数字,然后画出两侧数字
        paintNum(painter,m_currentValueY-1,m_deviation-Height/4);
    if(m_currentValueY != m_maxRange)
        paintNum(painter,m_currentValueY+1,m_deviation+Height/4);

    //两侧数字2,超出则不显示
    if(m_deviation >= 0 && m_currentValueY-2 >= m_minRange)
        paintNum(painter,m_currentValueY-2,m_deviation-Height/2);
    if(m_deviation <= 0 && m_currentValueY+2 <= m_maxRange)
        paintNum(painter,m_currentValueY+2,m_deviation+Height/2);

    //中间数字两侧的边框
    painter.setPen(QPen(QColor(70,144,249),2));
    painter.drawLine(0,Height/8*3,Width,Height/8*3);
    painter.drawLine(0,Height/8*5,Width,Height/8*5);
}

//画数字
void RollingBoxY::paintNum(QPainter &painter,int num,int deviation)
{
    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);      //设置字体
    painter.setPen(QColor(0,0,0,transparency));     //设置画笔
    painter.drawText(QRectF(0,y,Width,height),      //画文本,参数:QRectF参数:位置xy,长宽大小;对齐方式,居中对齐;内容
                     Qt::AlignCenter,
                     (QString::number(num)+tr("年")));
}

void RollingBoxY::homing()
{
    //将数字矫正到中心
    if(m_deviation > height()/8)
    {
        homingAni->setStartValue((height()-1)/8-m_deviation);
        homingAni->setEndValue(0);
        m_currentValueY--;
    }
    else if(m_deviation > -height()/8)
    {
        homingAni->setStartValue(m_deviation);
        homingAni->setEndValue(0);
    }
    else if(m_deviation < -height()/8)
    {
        homingAni->setStartValue(-(height()-1)/8-m_deviation);
        homingAni->setEndValue(0);
        m_currentValueY++;
    }
    emit currentValueChanged(m_currentValueY);   //发送当前数值改变信号
    homingAni->start();     //开始动画
}

//获取鼠标移动偏移量
int RollingBoxY::readDeviation()
{
    return m_deviation;
}

//设置偏移量
void RollingBoxY::setDeviation(int n)
{
    m_deviation = n;
    repaint();
}

月份滚动条
rollingboxm.h

//时间滚动条-月份

#ifndef ROLLINGBOXM_H
#define ROLLINGBOXM_H

#include <QWidget>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QTimer>
#include <QDateTime>

class RollingBoxM : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(int deviation READ readDeviation WRITE setDeviation)

public:
    explicit RollingBoxM(QWidget *parent = 0);

    void setRange(int min,int max); //设置范围

public slots:
    int readValue();                //获取当前值

private:
    QTimer *time;

protected:
    int m_minRange;
    int m_maxRange;
    int m_currentValueM = QDateTime::currentDateTime().toString("MM").toInt();
    bool isDragging;
    int m_deviation;
    int m_mouseSrcPos;
    int m_numSize;//计算所得的数字字符尺寸,以最长的情况为准
    QPropertyAnimation *homingAni;

    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
    void mouseReleaseEvent(QMouseEvent *);
//    void wheelEvent(QWheelEvent *);
    void paintEvent(QPaintEvent *);
    void paintNum(QPainter &painter,int num,int deviation);     //绘制数字
    void homing();                                              //使选中的数字回到屏幕中间
    int readDeviation();                                        //获取鼠标移动偏移量,默认为0
    void setDeviation(int n);                                   //设置偏移量
signals:
    void currentValueChanged(int value);                        //当前数值改变信号
    void deviationChange(float deviation);                      //偏移量改变信号
    int sendMonth(int);
};

#endif // ROLLINGBOXM_H

rollingboxm.cpp

//时间滚动条-月份

#include <rollingboxm.h>
#include <QTimer>

RollingBoxM::RollingBoxM(QWidget *parent) :
    QWidget(parent),
    m_minRange(1),          //最小值
    m_maxRange(12),         //最大值
//    m_currentValueM(10),    //
    isDragging(0),          //鼠标是否按下
    m_deviation(0),         //偏移量,记录鼠标按下后移动的垂直距离
    m_numSize(6)            //计算所得的数字字符尺寸,以最长的情况为准
{
    this -> setWindowFlags(windowFlags() &~Qt::WindowMinMaxButtonsHint);
    this -> setStyleSheet("background:white;");
    homingAni  = new QPropertyAnimation(this,"deviation");      //一个控制动画效果的类
    homingAni->setDuration(300);                                //设置动画持续时间
    homingAni->setEasingCurve(QEasingCurve::OutQuad);           //设置动画的缓动曲线,这里OutQuad是二次函数缓和曲线,减速到零
}

//设置范围
void RollingBoxM::setRange(int min,int max)
{
    m_minRange = min;
    m_maxRange = max;

    if(m_currentValueM < min)        //如果选中的当前值小于最小值,就等于最小值
        m_currentValueM = min;
    if(m_currentValueM > max)        //最大值同理
        m_currentValueM = max;

    //计算字符尺寸
    m_numSize = 3;
    int temp = m_maxRange;
    while(temp > 0)
    {
        temp /= 10;
        m_numSize++;
    }

    repaint();      //重绘此组件,需要进行重绘时,要使用repaint
}

//获取当前值
int RollingBoxM::readValue()
{
    return m_currentValueM;
}

//鼠标按下事件
void RollingBoxM::mousePressEvent(QMouseEvent *e)
{
    homingAni->stop();      //动画停止
    isDragging = 1;         //鼠标是否按下的标志位,布尔类型
    m_mouseSrcPos = e->pos().y();   //鼠标按下时候的y轴坐标记录下来
}

//鼠标移动事件
void RollingBoxM::mouseMoveEvent(QMouseEvent *e)
{
    if(isDragging)
    {
        //数值到边界时,阻止继续往对应方向移动
        if((m_currentValueM == m_minRange && e->pos().y() >= m_mouseSrcPos)||
          (m_currentValueM == m_maxRange && e->pos().y() <= m_mouseSrcPos))
            return;

        m_deviation = e->pos().y() - m_mouseSrcPos;     //垂直偏移量 = 现在的坐标 - 开始的坐标

        //若移动速度过快时进行限制
        if(m_deviation > (height()-1)/4)
            m_deviation = (height()-1)/4;
        else if(m_deviation < -(height()-1)/4)
            m_deviation = -(height()-1)/4;

        emit deviationChange((float)m_deviation/((height()-1)/5));      //发出偏移量改变的信号,随着偏移量改变,重绘数字,就变成了数字滚动的效果

        repaint();
    }
}

//鼠标松开事件
void RollingBoxM::mouseReleaseEvent(QMouseEvent *)
{
    if(isDragging)      //如果鼠标松开了,那么还原鼠标按下标志位
    {
        isDragging = 0;
        homing();       //使选中的数字回到屏幕中间
    }
}

//void RollingBoxM::wheelEvent(QWheelEvent *e)
//{
//    if(e->delta()>0)
//    {
//        m_deviation = (this->height() -1)/4;
//    }
//    else{
//        m_deviation = -(this->height() -1)/4;
//    }
//    homing();
//    repaint();
//}

//绘图事件
void RollingBoxM::paintEvent(QPaintEvent *)
{
    QPainter painter(this);     //创建画家类,指定绘图设备
    painter.setRenderHint(QPainter::Antialiasing, true);    //抗锯齿

    int Width = width()-1;
    int Height = height()-1;

    if(m_deviation >= Height/4 && m_currentValueM > m_minRange)  //偏移量大于1/4的时候,数字减一
    {
        m_mouseSrcPos += Height/4;  //鼠标起始位置重新设置,即加上1/4的高度
        m_deviation -= Height/4;    //偏移量重新设置,即减去1/4的高度
        m_currentValueM -= 1;
    }
    if(m_deviation <= -Height/4 && m_currentValueM < m_maxRange) //同理,数字加一
    {
        m_mouseSrcPos -= Height/4;
        m_deviation += Height/4;
        m_currentValueM += 1;
    }

    //中间数字
    paintNum(painter,m_currentValueM,m_deviation);   //将选中数字画到中间

    //两侧数字1
    if(m_currentValueM != m_minRange)    //选中的数字不是最小,不是最大,那么就有两侧数字,然后画出两侧数字
        paintNum(painter,m_currentValueM-1,m_deviation-Height/4);
    if(m_currentValueM != m_maxRange)
        paintNum(painter,m_currentValueM+1,m_deviation+Height/4);

    //两侧数字2,超出则不显示
    if(m_deviation >= 0 && m_currentValueM-2 >= m_minRange)
        paintNum(painter,m_currentValueM-2,m_deviation-Height/2);
    if(m_deviation <= 0 && m_currentValueM+2 <= m_maxRange)
        paintNum(painter,m_currentValueM+2,m_deviation+Height/2);

    //中间数字两侧的边框
    painter.setPen(QPen(QColor(70,144,249),2));
    painter.drawLine(0,Height/8*3,Width,Height/8*3);
    painter.drawLine(0,Height/8*5,Width,Height/8*5);
}

//画数字
void RollingBoxM::paintNum(QPainter &painter,int num,int deviation)
{
    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);      //设置字体
    painter.setPen(QColor(0,0,0,transparency));     //设置画笔
    painter.drawText(QRectF(0,y,Width,height),      //画文本,参数:QRectF参数:位置xy,长宽大小;对齐方式,居中对齐;内容
                     Qt::AlignCenter,
                     (QString::number(num)+tr("月")));
}

void RollingBoxM::homing()
{
    //将数字矫正到中心
    if(m_deviation > height()/8)
    {
        homingAni->setStartValue((height()-1)/8-m_deviation);
        homingAni->setEndValue(0);
        m_currentValueM--;
    }
    else if(m_deviation > -height()/8)
    {
        homingAni->setStartValue(m_deviation);
        homingAni->setEndValue(0);
    }
    else if(m_deviation < -height()/8)
    {
        homingAni->setStartValue(-(height()-1)/8-m_deviation);
        homingAni->setEndValue(0);
        m_currentValueM++;
    }
    emit currentValueChanged(m_currentValueM);   //发送当前数值改变信号
    homingAni->start();     //开始动画
}

//获取鼠标移动偏移量
int RollingBoxM::readDeviation()
{
    return m_deviation;
}

//设置偏移量
void RollingBoxM::setDeviation(int n)
{
    m_deviation = n;
    repaint();
}

日期滚动条
rollingboxd.h

//时间滚动条-日期

#ifndef ROLLINGBOXD_H
#define ROLLINGBOXD_H

#include <QTimer>
#include <QWidget>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QDateTime>
#include <rollingboxy.h>
#include <rollingboxm.h>

class RollingBoxD : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(int deviation READ readDeviation WRITE setDeviation)

public:
    explicit RollingBoxD(QWidget *parent = 0);

//    void setRange(int min,int max); //设置范围
    int readValue();                //获取当前值
    void setRange(int min,int max);                             //设置范围

public slots:
//    void receiveValue();

private:
    int m_currYear;
    int m_currMonth;
    QTimer *time;
    RollingBoxY *rby;
    RollingBoxM *rbm;

protected:
    int m_minRange;
    int m_maxRange;
    int m_currentValue = QDateTime::currentDateTime().toString("dd").toInt();
    bool isDragging;
    int m_deviation;
    int m_mouseSrcPos;
    int m_numSize;//计算所得的数字字符尺寸,以最长的情况为准
    QPropertyAnimation *homingAni;

    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
    void mouseReleaseEvent(QMouseEvent *);
//    void wheelEvent(QWheelEvent *);

    void paintEvent(QPaintEvent *);
    void paintNum(QPainter &painter,int num,int deviation);     //绘制数字
    void homing();                                              //使选中的数字回到屏幕中间
    int readDeviation();                                        //获取鼠标移动偏移量,默认为0
    void setDeviation(int n);                                   //设置偏移量
    int maxValue();

signals:
    void currentValueChanged(int value);                        //当前数值改变信号
    void deviationChange(float deviation);                      //偏移量改变信号
};


#endif // ROLLINGBOXD_H

rollingboxd.cpp

//时间滚动条-日期

#include <QTimer>
#include <QDialog>
#include <rollingboxd.h>

RollingBoxD::RollingBoxD(QWidget *parent) :
    QWidget(parent),
    m_minRange(1),          //最小值
//    m_currentValue(20),     //当前选中的值
    isDragging(0),          //鼠标是否按下
    m_deviation(0),         //偏移量,记录鼠标按下后移动的垂直距离
    m_numSize(6)            //计算所得的数字字符尺寸,以最长的情况为准
{
    this -> setWindowFlags(windowFlags() &~Qt::WindowMinMaxButtonsHint);
    this -> setStyleSheet("background:white;");
    homingAni  = new QPropertyAnimation(this,"deviation");      //一个控制动画效果的类
    homingAni->setDuration(300);                                //设置动画持续时间
    homingAni->setEasingCurve(QEasingCurve::OutQuad);           //设置动画的缓动曲线,这里OutQuad是二次函数缓和曲线,减速到零
}

//设置范围
void RollingBoxD::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++;
    }

    repaint();      //重绘此组件,需要进行重绘时,要使用repaint
}

//获取当前值
int RollingBoxD::readValue()
{
    return m_currentValue;
}

//鼠标按下事件
void RollingBoxD::mousePressEvent(QMouseEvent *e)
{
    homingAni->stop();      //动画停止
    isDragging = 1;         //鼠标是否按下的标志位,布尔类型
    m_mouseSrcPos = e->pos().y();   //鼠标按下时候的y轴坐标记录下来
}

//鼠标移动事件
void RollingBoxD::mouseMoveEvent(QMouseEvent *e)
{
    if(isDragging)
    {
        //数值到边界时,阻止继续往对应方向移动
        if((m_currentValue == m_minRange && e->pos().y() >= m_mouseSrcPos)||
          (m_currentValue == m_maxRange && e->pos().y() <= m_mouseSrcPos))
            return;

        m_deviation = e->pos().y() - m_mouseSrcPos;     //垂直偏移量 = 现在的坐标 - 开始的坐标

        //若移动速度过快时进行限制
        if(m_deviation > (height()-1)/4)
            m_deviation = (height()-1)/4;
        else if(m_deviation < -(height()-1)/4)
            m_deviation = -(height()-1)/4;

        emit deviationChange((float)m_deviation/((height()-1)/5));      //发出偏移量改变的信号,随着偏移量改变,重绘数字,就变成了数字滚动的效果

        repaint();
    }
}

//鼠标松开事件
void RollingBoxD::mouseReleaseEvent(QMouseEvent *)
{
    if(isDragging)      //如果鼠标松开了,那么还原鼠标按下标志位
    {
        isDragging = 0;
        homing();       //使选中的数字回到屏幕中间
    }
}

//void RollingBoxD::wheelEvent(QWheelEvent *e)
//{
//    if(e->delta()>0)
//    {
//        m_deviation = (this->height() -1)/4;
//    }
//    else{
//        m_deviation = -(this->height() -1)/4;
//    }
//    homing();
//    repaint();
//}

//绘图事件
void RollingBoxD::paintEvent(QPaintEvent *)
{
    QPainter painter(this);     //创建画家类,指定绘图设备
    painter.setRenderHint(QPainter::Antialiasing, true);    //抗锯齿

    int Width = width()-1;
    int Height = height()-1;

    if(m_deviation >= Height/4 && m_currentValue > m_minRange)  //偏移量大于1/4的时候,数字减一
    {
        m_mouseSrcPos += Height/4;  //鼠标起始位置重新设置,即加上1/4的高度
        m_deviation -= Height/4;    //偏移量重新设置,即减去1/4的高度
        m_currentValue -= 1;
    }
    if(m_deviation <= -Height/4 && m_currentValue < m_maxRange) //同理,数字加一
    {
        m_mouseSrcPos -= Height/4;
        m_deviation += Height/4;
        m_currentValue += 1;
    }

    //中间数字
    paintNum(painter,m_currentValue,m_deviation);   //将选中数字画到中间

    //两侧数字1
    if(m_currentValue != m_minRange)    //选中的数字不是最小,不是最大,那么就有两侧数字,然后画出两侧数字
        paintNum(painter,m_currentValue-1,m_deviation-Height/4);
    if(m_currentValue != m_maxRange)
        paintNum(painter,m_currentValue+1,m_deviation+Height/4);

    //两侧数字2,超出则不显示
    if(m_deviation >= 0 && m_currentValue-2 >= m_minRange)
        paintNum(painter,m_currentValue-2,m_deviation-Height/2);
    if(m_deviation <= 0 && m_currentValue+2 <= m_maxRange)
        paintNum(painter,m_currentValue+2,m_deviation+Height/2);

    //中间数字两侧的边框
    painter.setPen(QPen(QColor(70,144,249),2));
    painter.drawLine(0,Height/8*3,Width,Height/8*3);
    painter.drawLine(0,Height/8*5,Width,Height/8*5);
}

//画数字
void RollingBoxD::paintNum(QPainter &painter,int num,int deviation)
{
    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);      //设置字体
    painter.setPen(QColor(0,0,0,transparency));     //设置画笔
    painter.drawText(QRectF(0,y,Width,height),      //画文本,参数:QRectF参数:位置xy,长宽大小;对齐方式,居中对齐;内容
                     Qt::AlignCenter,
                     (QString::number(num)+tr("日")));
}

void RollingBoxD::homing()
{
    //将数字矫正到中心
    if(m_deviation > height()/8)
    {
        homingAni->setStartValue((height()-1)/8-m_deviation);
        homingAni->setEndValue(0);
        m_currentValue--;
    }
    else if(m_deviation > -height()/8)
    {
        homingAni->setStartValue(m_deviation);
        homingAni->setEndValue(0);
    }
    else if(m_deviation < -height()/8)
    {
        homingAni->setStartValue(-(height()-1)/8-m_deviation);
        homingAni->setEndValue(0);
        m_currentValue++;
    }
    emit currentValueChanged(m_currentValue);   //发送当前数值改变信号
    homingAni->start();     //开始动画
}

//获取鼠标移动偏移量
int RollingBoxD::readDeviation()
{
    return m_deviation;
}

//设置偏移量
void RollingBoxD::setDeviation(int n)
{
    m_deviation = n;
    repaint();
}

最后就是把它们整合在一起了

整合控件
rollingbox.h

//时间滚动条控件(封装)

#ifndef ROLLINGBOX_H
#define ROLLINGBOX_H

#include <QTimer>
#include <QWidget>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <rollingboxy.h>
#include <rollingboxm.h>
#include <rollingboxd.h>

class RollingBox : public QWidget
{
    Q_OBJECT

public:
    explicit RollingBox(QWidget *parent = 0);

//    int readValue();                //获取当前值
    int readYear();
    int readMonth();
    int readDay();
    int m_currYear;
    int m_currMonth;
    int m_currDay;

public slots:
    void receiveValue();

private:
    QTimer *time;
    RollingBoxY *rby;
    RollingBoxM *rbm;
    RollingBoxD *rbd;

protected:
    int m_maxRange;                 //设置偏移量
    int maxValue();
};

#endif // ROLLINGBOX_H

rollingbox.cpp

#include <rollingbox.h>

RollingBox::RollingBox(QWidget *parent)
    :QWidget(parent)
{
    rby = new RollingBoxY(this);
    rby -> move(60,0);
    rby -> setFixedSize(200,300);
    rbm = new RollingBoxM(this);
    rbm -> move(260,0);
    rbm -> setFixedSize(200,300);
    rbd = new RollingBoxD(this);
    rbd -> move(460,0);
    rbd -> setFixedSize(200,300);

    time = new QTimer(this);
    time -> start(33);     //每隔若干秒读取一次当前选中的值(default:30帧)
    connect(time,SIGNAL(timeout()),this,SLOT(receiveValue()));
}

int RollingBox::maxValue()
{
    //日期最大值(随年份和月份变化)
    if(m_currMonth == 1 || m_currMonth == 3 || m_currMonth == 5 || m_currMonth == 7 || m_currMonth == 8
            || m_currMonth == 10 || m_currMonth == 12)
        m_maxRange = 31;
    else if(m_currMonth == 4 || m_currMonth == 6 || m_currMonth == 9 || m_currMonth == 11)
        m_maxRange = 30;
    else if(m_currMonth == 2 && ((m_currYear % 4 == 0 && m_currYear % 100 != 0) || m_currYear % 400 == 0))
        m_maxRange = 29;
    else m_maxRange = 28;

    return m_maxRange;
}

void RollingBox::receiveValue()
{
    m_currYear = rby -> readValue();
    m_currMonth = rbm -> readValue();
    m_currDay = rbd -> readValue();
    rbd -> setRange(1,RollingBox::maxValue());
}

int RollingBox::readYear()
{
    return m_currYear;
}

int RollingBox::readMonth()
{
    return m_currMonth;
}

int RollingBox::readDay()
{
    return m_currDay;
}

最后在想要添加滚动日期选择器的页面new一个RollingBox就行了。

参考资料

  1. Qt ScrollBar 实现滚动屏时间选择器
  2. 【QT】自制控件—滚动选择器
  3. Qt自定义控件——滑动选择器
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐