Qt编写自定义控件:卷轴式数字滚动

代码:

#ifndef REELWIDGET_H
#define REELWIDGET_H

#include <QWidget>
#include <QTimer>

class ReelWidget : public QWidget
{
    Q_OBJECT

public:
    ReelWidget(QWidget *parent = nullptr);
    void setNewNumber(uint newNumber);

protected:
    void paintEvent(QPaintEvent *event)override;

private:
    uint lastNumber{0};
    uint currentNumber{1};
    const QPixmap bg;
    bool isRuning{false};
    QTimer timer;
    enum class state
    {
        noRun,
        runing_up,
        runing_down
    };
    state widgetState{state::noRun};
    QRect whenRuningImageRect_Current;
    QRect whenRuningImageRect_Last;
    void onTimer();
    void drawAUnit(const QRect &rect, QPainter * painter, uint number);
};

class testReelWidget : public QWidget
{
    Q_OBJECT

public:
    testReelWidget(QWidget *parent = nullptr);
    void setValue(int value);

protected:
    QSize sizeHint() const override;

private:
    QList<ReelWidget *> list;
    QTimer timer;
    void onTimer();
};

#endif // REELWIDGET_H
#include "reelwidget.h"
#include <QPainter>
#include <QPaintEvent>
#include <QHBoxLayout>
#include <QQueue>
#include <random>

int rateFactor = 5;

ReelWidget::ReelWidget(QWidget *parent)
    : QWidget(parent),bg{":/bg.png"}
{
    setFixedSize(bg.rect().size());
    auto font = this->font();
    font.setPixelSize(36);
    setFont(font);

    connect(&timer,&QTimer::timeout,this,&ReelWidget::onTimer);
    timer.setInterval(40);
}

void ReelWidget::setNewNumber(uint newNumber)
{
    if(this->widgetState == state::noRun && newNumber != currentNumber)
    {
        lastNumber = this->currentNumber;
        this->currentNumber = newNumber;
        widgetState = lastNumber > currentNumber ? state::runing_down : state::runing_up;
        whenRuningImageRect_Current = bg.rect();
        whenRuningImageRect_Last = whenRuningImageRect_Current;
        whenRuningImageRect_Current.moveTop(widgetState == state::runing_up ? bg.rect().height() : -bg.rect().height());
        timer.start();
    }
}

void ReelWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setFont(this->font());
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.setRenderHint(QPainter::SmoothPixmapTransform,true);
    if(widgetState == state::noRun)
    {
        drawAUnit(event->rect(),&painter,currentNumber);
    }
    else
    {
        drawAUnit(whenRuningImageRect_Last,&painter,lastNumber);
        drawAUnit(whenRuningImageRect_Current,&painter,currentNumber);
    }
}

void ReelWidget::onTimer()
{
    if(widgetState == state::runing_up)
    {
        auto currentTop = whenRuningImageRect_Current.top() - rateFactor;
        if(currentTop <= 0)
        {
            timer.stop();
            widgetState = state::noRun;
        }
        else
        {
            whenRuningImageRect_Last.moveTop(whenRuningImageRect_Last.top() - rateFactor);
            whenRuningImageRect_Current.moveTop(currentTop);
        }
    }
    else
    {
        auto currentTop = whenRuningImageRect_Current.top() + rateFactor;
        if(currentTop >= 0)
        {
            timer.stop();
            widgetState = state::noRun;
        }
        else
        {
            whenRuningImageRect_Last.moveTop(whenRuningImageRect_Last.top() + rateFactor);
            whenRuningImageRect_Current.moveTop(currentTop);
        }
    }
    update();
}

void ReelWidget::drawAUnit(const QRect & rect, QPainter * painter, uint number)
{
    painter->drawPixmap(rect,bg);
    painter->setPen(Qt::white);
    painter->drawText(rect,Qt::AlignCenter,QString::number(number));
}

testReelWidget::testReelWidget(QWidget *parent)
    :QWidget(parent)
{
    QHBoxLayout * hb = new QHBoxLayout(this);
    hb->setSpacing(0);

    setValue(123456789);
    connect(&timer,&QTimer::timeout,this,&testReelWidget::onTimer);
    timer.start(2000);
}

void testReelWidget::setValue(int value)
{
    QQueue<int> queue;
    while(value != 0)
    {
        queue.push_front(value % 10);
        value /= 10;
    }
    if(queue.size() != list.size())
    {
        auto layout = this->layout();
        for(auto w : list)
        {
            layout->removeWidget(w);
        }
        qDeleteAll(list);
        list.clear();
        for(auto value : queue)
        {
            auto w = new ReelWidget(this);
            layout->addWidget(w);
            list << w;
        }
    }
    for(int i = 0;i < list.size();++i)
    {
        list[i]->setNewNumber(queue.dequeue());
    }
}

QSize testReelWidget::sizeHint() const
{
    return QSize(list.size() * 55,90);
}

void testReelWidget::onTimer()
{
    static std::uniform_int_distribution<int> u(0,9);//均匀分布
    static std::default_random_engine e;//随机数引擎
    for(auto w : list)
    {
        w->setNewNumber(u(e));
    }
}

效果:

用到的背景图片:

UI参考:jQuery卷轴式数字滚动代码

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值