qt序列帧读取

2023-11-16

 描述:

        有时候ui给我们的动画是一序列的图片,播放动画需要一张一张图片读取,显得有点麻烦,存储的资源目录也显得比较凌乱。为解决这个问题,又不想使用gif,可以使用如下自定义序列帧播放组件,一次读取,通过计算当前帧的位置从内存读取当前帧。可以控制顺时针/逆时针播放、是否循环、播放每一帧的事件间隔。希望对大家有用。

.hpp文件

#include <QWidget>
#include <QPointer>

class QTimer;
class FramePalyWidget : public QWidget{
    Q_OBJECT
public:
    explicit FramePalyWidget (QWidget* parent = nullptr);

    virtual ~FramePalyWidget ();

    /**
     ************************************************************
     * @brief:      设置动画图标
     * @param[in]:  图标实例
     * @param[in]:  图标实例动画帧数
     * @return:     
     ************************************************************
     */
    void SetAnimation(const QPixmap& pix, int count = 1);

    /**
     ************************************************************
     * @brief:      设置定时器时间间隔(毫秒级)
     ************************************************************
     */
    void SetInterval(int msec);

    /**
     ************************************************************
     * @brief:      开始动画   
     ************************************************************
     */
    void Start();

    /**
     ************************************************************
     * @brief:      停止动画    
     ************************************************************
     */
    void Stop();

    /**
     ************************************************************
     * @brief:      设置是否循环播放
     * @param[in]:  isLoop : true 循环播放, false 不循环播放   
     ************************************************************
     */
    void SetLoop(bool isLoop);

    /**
     ************************************************************
     * @brief:      设置是否顺时针播放
     * @param[in]:  bClockwise: true 顺时针, false: 逆时针  
     ************************************************************
     */
    void SetClockwise(bool bClockwise);

protected:

    /**
     ************************************************************
     * @brief: 初始化ui
     ************************************************************
     */
    void InitUi();


    /**
     ************************************************************
     * @brief: 信号绑定
     ************************************************************
     */
    void ConnectSignals();

signals:

    /**
     ************************************************************
     * @brief: 播放完成信号    
     ************************************************************
     */
    void sigPlayFinished();

private slots:
    /**
     ************************************************************
     * @brief:  更新帧槽函数  
     ************************************************************
     */
    void slotUpdateFrame();

protected:

    void paintEvent(QPaintEvent*) override;

private:
    bool                    m_bClockwise;           // 是否顺时针播放
    bool                    m_bIsLoop;              // 是否循环
    int                     m_nFrameCount;          // 帧数
    int                     m_nCurrentIndex;        // 当前展示图片下标
    int                     m_nInterval;            // 时间间隔(毫秒)
    QPointer<QTimer>        m_pClockTimer;          // 更新定时器
    QPixmap                 m_currentPix;           // 当前帧
    QPixmap                 m_originalPix;          // 原始位图
    QList<QPixmap>          m_pixList;              // 位图列表
};

.cpp文件

#include "FramePalyWidget.hpp"
#include <QTimer>
#include <QPainter>

FramePalyWidget::FramePalyWidget(QWidget* parent)
    : QWidget(parent)
    , m_bIsLoop(true)
    , m_bClockwise(true){
    InitUi();
    ConnectSignals();
}

FramePalyWidget::~FramePalyWidget() {

    
}

/**
 ************************************************************
 * @brief: 初始化ui
 ************************************************************
 */
void FramePalyWidget::InitUi(){
    m_pClockTimer = new QTimer(this);
    m_pClockTimer->setInterval(10);
}

/**
 ************************************************************
 * @brief: 信号绑定
 ************************************************************
 */
void FramePalyWidget::ConnectSignals() {
    connect(m_pClockTimer, SIGNAL(timeout()), this, SLOT(slotUpdateFrame()));
}


/**
 ************************************************************
 * @brief:      设置定时器时间间隔(毫秒级)
 ************************************************************
 */
void FramePalyWidget::SetInterval(int msec) {
    if (m_pClockTimer) {
        m_pClockTimer->setInterval(msec);
    }
}

/**
 ************************************************************
 * @brief:      设置动画图标
 * @param[in]:  图标实例
 * @param[in]:  图标实例动画帧数
 * @param[out]: 动画切帧速度 (毫秒级)
 * @return:
 ************************************************************
 */
void FramePalyWidget::SetAnimation(const QPixmap& pix, int count) {
    m_nFrameCount = count;
    m_nCurrentIndex = 0;

    if(m_nFrameCount <= 0) {
        return;
    }

    if (!m_pixList.empty()) {
        m_pixList.clear();
    }
    
    if(m_pClockTimer->isActive()){
        m_pClockTimer->stop();
    }

    // 帧获取
    for (short i = 0; i < count; ++i) {
        m_pixList.append(pix.copy(i * (pix.width() / count), 0,
                         pix.width() / count, pix.height()));
    }

    m_currentPix = m_pixList.at(0);
    update();

}

/**
 ************************************************************
 * @brief:      开始动画
 ************************************************************
 */
void FramePalyWidget::Start() {
    if(m_pClockTimer){
        if (m_pClockTimer->isActive()) {
            m_pClockTimer->stop();
        }
        m_pClockTimer->start();
    }
}

/**
 ************************************************************
 * @brief:      停止动画
 ************************************************************
 */
void FramePalyWidget::Stop() {
    if (m_pClockTimer) {
        m_pClockTimer->stop();
    }
}

/**
 ************************************************************
 * @brief:      设置是否循环播放
 * @param[in]:  isLoop : true 循环播放, false 不循环播放
 ************************************************************
 */
void FramePalyWidget::SetLoop(bool isLoop) {
    m_bIsLoop = isLoop; 
}

/**
 ************************************************************
 * @brief:      设置是否顺时针播放
 * @param[in]:  bClockwise: true 顺时针, false: 逆时针
 ************************************************************
 */
void FramePalyWidget::SetClockwise(bool bClockwise) {
    m_bClockwise = bClockwise;
}

/**
 ************************************************************
 * @brief:  更新帧槽函数
 ************************************************************
 */
void FramePalyWidget::slotUpdateFrame() {
    if (m_nCurrentIndex < m_nFrameCount && m_nCurrentIndex >= 0) {
        //  更新帧
        m_currentPix = m_pixList.at(m_nCurrentIndex);
        update();

        if (m_bClockwise) {
            //  判断帧数
            if (m_nCurrentIndex >= (m_nFrameCount - 1)) {
                if (m_bIsLoop) {
                    m_nCurrentIndex = 0;
                    return;
                }
                m_pClockTimer->stop();
                m_nCurrentIndex = 0;
                emit sigPlayFinished();
                return;
            }

            //  跳帧
            ++m_nCurrentIndex;
        }
        else {
            //  判断帧数
            if (m_nCurrentIndex <= 0) {
                if (m_bIsLoop) {
                    m_nCurrentIndex = m_nFrameCount - 1;
                    return;
                }
                m_pClockTimer->stop();
                m_nCurrentIndex = m_nFrameCount - 1 ;
                emit sigPlayFinished();
                return;
            }
            //  跳帧
            --m_nCurrentIndex;
        }
        
    }
}

void FramePalyWidget::paintEvent(QPaintEvent*) {
    QPainter painter(this);
    painter.drawPixmap(rect(), m_currentPix);
}

使用样例:

    FramePalyWidget* m_pTestAnimal = new FramePalyWidget(this);
    m_pTestAnimal ->setFixedSize(70, 140);
    m_pTestAnimal ->SetAnimation(QPixmap(":res/img/test/test.png"), 50);
    m_pTestAnimal ->SetClockwise(true);        // 顺时针读取
    m_pTestAnimal ->SetInterval(40);           // 帧读取事件间隔
    m_pTestAnimal ->SetLoop(false);            // 是否循环

    connect(m_pTestAnimal , &FramePalyWidget::sigPlayFinished,
            this, [&](){
        // TODO finished
    });

序列帧合成地址:

地址1:https://www.toptal.com/developers/css/sprite-generator

地址2:https://cdkm.com/cn/merge-image

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

qt序列帧读取 的相关文章

随机推荐

  • SpringBoot 整合 Druid+ 全局事务管理 +Mybatis-Plus+ 代码生成器

    SpringBoot 整合 Druid 全局事务管理 Mybatis Plus 代码生成器 在 springboot 开发当中 Druid 全局事务管理 代码生成器都是非常实用的 特此记录下整合的过程 整合 Druid 连接池 spring
  • js逆向技巧

    一 总结 搜索 全局搜索 代码内搜索 debug 常规debug XHR debug 行为debug 查看请求调用的堆栈 执行堆内存中的函数 修改堆栈中的参数值 写js代码 打印windows对象的值 勾子 cookie钩子 请求钩子 he
  • 存在的意义

    到目前为止出现过的所有备选答案 1 成功 成为人上人 2 好吃的 好听的 好看的 3 明白些道理 遇见有趣的事 4 像吃蛋糕和甜甜圈一样 在于吃的过程 5 纯粹为了快感而嗑药 6 为了我爱的人和爱我的人 7 为社会和他人作贡献 8 为神服务
  • FreeRTOS学习笔记—任务挂起和恢复

    文章目录 一 任务挂起和恢复API函数 1 1 vTaskSuspend 函数 1 2 vTaskResume 函数 1 3 xTaskResumeFromISR 函数 二 任务挂起和恢复 2 1 任务1挂起解挂任务2 2 2 中断中解挂任
  • 绘制ER图(数据库关系图)

    http www texample net tikz examples entity relationship diagram https www assembla com wiki show tikz er2 usepackage tik
  • Spring Boot 报错ThreadPoolTaskExecutor : Shutting down ExecutorService ‘applicationTaskExecutor‘

    问题 Spring Boot 报错ThreadPoolTaskExecutor Shutting down ExecutorService applicationTaskExecutor 原因 idea没有运行项目 运行了测试类 解决 运行
  • Angular 11常用api

    ngFor渲染每个商品 div div 属性绑定 div h3 a a h3 div 插值 div h3 a product name a h3 div
  • 【Android】使用native开发遇到的bug---java.lang.UnsatisfiedLinkError: No implementation found for

    今晚终于解决了一个困扰我三天的bug 就是这个java lang UnsatisfiedLinkError No implementation found for 感觉人生达到了巅峰 特此记录下 前几天老师给我一个项目 让我实现一个将视频下
  • 使用Xshell通过隧道代理进行SSH跳转访问

    文章来源 http www aloneray com 886 html 可能需要vpn翻墙才能访问 链接流程 本地服务器Local gt 中转服务器Jump gt 目标服务器Destination 简单来说 就是不直接访问你的目标服务器 而
  • weblogic作为文件浏览服务器,weblogic配置文件服务器

    weblogic配置文件服务器 内容精选 换一换 该任务指导用户使用Loader将数据从Hive导出到SFTP服务器 创建或获取该任务中创建Loader作业的业务用户和密码 确保用户已授权访问作业中指定的Hive表的权限 获取SFTP服务器
  • Guava库API使用部分总结

    Guava库 1 Guava是对Java API的补充 对Java开发中常用功能进行更优雅的实现 使得编码更加轻松 代码容易理解 Guava使用了多种设计模式 同时经过了很多测试 得到了越来越多开发团队的青睐 Java最新版本的API采纳了
  • MySQL试题2

    二 题目 01 查询 1 课程比 2 课程成绩高的学生的信息及课程分数 方法一 select s t1 score t2 score from select sid score from t score where cid 1 t1 sel
  • avalon.js 1.4.6简单列表数据绑定ms-repeat ms-click

    1 列表数据绑定
  • RPMB原理介绍

    RPMB介绍 RPMB Replay Protected Memory Block Partition 是 eMMC 中的一个具有安全特性的分区 eMMC 在写入数据到 RPMB 时 会校验数据的合法性 只有指定的 Host 才能够写入 同
  • matlab确定灰度阈值T,基于阈值的灰度图像提取法

    对于简单的灰度图像 如果目标与背景的灰度存在一定的差异 那么可以用阈值来提取目标 关键是确定阈值 常用方法有 模态法 P参数法 可变阈值法 大津法和迭代逼近法等 模态法 取直方图的波谷作为阈值 适用于目标与背景灰度差异较大 目标与背景的直方
  • 2021-05-27

    点三信息Java后端实习面经 长沙 1 你做这个rpc项目的动机是什么 我 为了学习 后来面试官又说我动机不明 醉了 2 讲一下BIO NIO AIO 不用AIO的话 发送过来一个包的流程是怎样的 3 零拷贝 什么情况下适用零拷贝 什么情况
  • 史上最全 55道 MySQL面试题及答案,看完碾压面试官

    前言 MySQL面试题以及答案整理 最新版 MySQL高级面试题大全 发现网上很多MySQL面试题都没有答案 所以花了很长时间搜集 本套MySQL面试题大全 汇总了大量经典的MySQL程序员面试题以及答案 包含MySQL语言常见面试题 My
  • 解决Transformer固有缺陷:复旦大学等提出线性复杂度SOFT

    作者 机器之心编辑部 来源 机器之心 来自复旦大学 萨里大学和华为诺亚方舟实验室的研究者首次提出一种无 softmax Transformer 视觉 Transformer ViT 借助 patch wise 图像标记化和自注意力机制已经在
  • 一个docker内不同参数执行个npm run

    dockerfile www wwwroot cat Dockerfile FROM node 12 18 0 alpine3 11 ARG NPM RUN ARG build ARG NPM RUN ARG TWO build story
  • qt序列帧读取

    描述 有时候ui给我们的动画是一序列的图片 播放动画需要一张一张图片读取 显得有点麻烦 存储的资源目录也显得比较凌乱 为解决这个问题 又不想使用gif 可以使用如下自定义序列帧播放组件 一次读取 通过计算当前帧的位置从内存读取当前帧 可以控