Qt picture timing scrolling player + transparent transition animation

reference

Qt image browser

QT makes a picture player

After the adaptive label+pixmap in Qt fills the window, it cannot be zoomed out but can only be zoomed in

Qt's animation class modifies the transparency to achieve the switching effect of the upper and lower pictures

解决:[QWidget::paintEngine: Should no longer be called QPainter::begin: Paint device returned engine == 0, type: 1]

Can display jpg, jpeg, png, bmp. You can drag the picture from the computer to the window and display it or open the file selection and scroll the picture regularly

Overload implements dragEnterEvent (drag), dropEvent (drag and drop), resizeEvent (window size change)

Solution:[QWidget::paintEngine: Should no longer be called QPainter::begin: Paint device returned engine == 0, type: 1] Which control needs to be drawn on, it is necessary to rewrite paintEvent() in which control class, so this project needs to use the custom MyQLabel to inherit QLabel

There is no transparent transition animation version Qt picture timing scrolling player, no transparent transition animation

structure

insert image description here

PicturePlay.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    myqlabel.cpp \
    pictureplay.cpp

HEADERS += \
    myqlabel.h \
    pictureplay.h

FORMS += \
    pictureplay.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${
    
    TARGET}/bin
else: unix:!android: target.path = /opt/$${
    
    TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    res.qrc

main.cpp

#include "pictureplay.h"

#include <QApplication>
#include<QFile>
int main(int argc, char *argv[])
{
    
    
    QApplication a(argc, argv);
    // 加载样式文件
    QFile file(":/style.qss");
    /*判断文件是否存在*/
    if(file.exists())
    {
    
    
        file.open(QFile::ReadOnly);// 以只读的方式打开
        //QString styleSheet=QLatin1String(file.readAll());// 以Latin-1编码的字符串的方式保存读出的结果
        QString styleSheet=QString(file.readAll());// 以默认编码的字符串的方式保存读出的结果
        qApp->setStyleSheet(styleSheet);// 设置全局样式
        file.close();// 关闭文件
    }
    //在窗口显示前判断使用
    PicturePlay w;
    w.show();
    return a.exec();
}

myqlabel.h custom QLabel

#ifndef MYQLABEL_H
#define MYQLABEL_H

#include <QWidget>
#include <QLabel>
#include <QPainter>
#include <QPropertyAnimation>

class MyQLabel : public QLabel
{
    
    
    Q_OBJECT
public:
    explicit MyQLabel(QWidget *parent = nullptr);
    void paintEvent(QPaintEvent *event)override;// 绘图事件;
    // 图片切换动画类;
    QPropertyAnimation* m_opacityAnimation;
    QPixmap currentPixmap;
    QPixmap nextPixmap;
    bool paintEvent_Flag;
signals:

};

#endif // MYQLABEL_H

myqlabel.cpp custom QLabel

#include "myqlabel.h"
#include<QVariant>
#include<QDebug>
MyQLabel::MyQLabel(QWidget *parent) : QLabel(parent)
{
    
    
    this->setProperty("ImageOpacity",1.0);
    this->paintEvent_Flag=false;
    // 动画切换类;
    m_opacityAnimation = new QPropertyAnimation(this, "ImageOpacity");
    // 这里要设置的动画时间小于图片切换时间;
    m_opacityAnimation->setDuration(1500);
    // 设置ImageOpacity属性值的变化范围;
    m_opacityAnimation->setStartValue(1.0);
    m_opacityAnimation->setEndValue(0.0);
    // 透明度变化及时更新绘图;
    connect(m_opacityAnimation, SIGNAL(valueChanged(const QVariant&)),this, SLOT(update()));
    m_opacityAnimation->stop();
}
int i=0;
void MyQLabel::paintEvent(QPaintEvent *event)
{
    
    
    if(!currentPixmap.isNull()&&!nextPixmap.isNull()&&paintEvent_Flag)
    {
    
    
        this->clear();
        QPainter painter(this);
        QRect imageRect = this->rect();
        float imageOpacity = this->property("ImageOpacity").toFloat();
        //qDebug()<<"i=["<<i++<<"]imageOpacity="<<imageOpacity;
        painter.setOpacity(1);
        painter.drawPixmap(imageRect, nextPixmap.scaled(imageRect.size()));
        painter.setOpacity(imageOpacity);
        painter.drawPixmap(imageRect, currentPixmap.scaled(imageRect.size()));// 透明度减到0
    }
    QLabel::paintEvent(event);
}

pictureplay.h

#ifndef PICTUREPLAY_H
#define PICTUREPLAY_H

#include <QWidget>
#include <QFile>
#include <QDir>
#include <QTimer>
#include <QThread>
#include <QFileDialog>
#include <QScrollArea>
#include <QPropertyAnimation>
QT_BEGIN_NAMESPACE
namespace Ui {
    
     class PicturePlay; }
QT_END_NAMESPACE

class PicturePlay : public QWidget
{
    
    
    Q_OBJECT

public:
    PicturePlay(QWidget *parent = nullptr);
    ~PicturePlay();
private slots:

    void myautoPhoto(); //自动播放函数
    void showDinamicPhoto(QString path);//动态图播放(格式为gif)

    void on_pathBtn_clicked();//浏览:打开目录

    void on_autoPhoto_clicked(bool checked);//自动播放选择

    void on_photoList_clicked(const QModelIndex &index);//单击播放图片

    void my_currentRowChanged(int Row);// // 当前项变化
    void on_cleanBtn_clicked();//清除


private:

    QString Dir;//打开文件的路径
    QString tempDir; //照片的绝地路径
    QVector<QString> photoPath;//存放照片相对路径的容器
    QTimer *mtime; //定时器
    QString delayTime; //延时间隔
    bool autoFlag; //判断是否进入的自动播放格式
    int num; // 当前显示图片index;
    // 如果文件的后缀名是jpg、jpeg、bmp或png,则接受拖放事件,否则忽略拖放事件
    QStringList acceptedFileTypes;

    void dragEnterEvent(QDragEnterEvent *event)override;//拖进事件
    void dropEvent(QDropEvent *event)override;// 拖进放下事件
    void resizeEvent(QResizeEvent *event)override;//用于在窗口大小改变时处理事件
    void paintEvent(QPaintEvent *event)override;// 绘图事件;

    // 切换图片;
    QPixmap pixmap;
//    QPixmap currentPixmap;
//    QPixmap nextPixmap;

    // 用来做图片切换滑动效果,目前以透明度作为切换效果;
    QScrollArea* m_imagePlayWidget;
//    // 图片切换动画类;
//    QPropertyAnimation* m_opacityAnimation;



private:
    Ui::PicturePlay *ui;
};
#endif // PICTUREPLAY_H

pictureplay.cpp

#include "pictureplay.h"
#include "ui_pictureplay.h"
#include<QDebug>
#include<QMessageBox>
#include<QMovie>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QPainter>

PicturePlay::PicturePlay(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::PicturePlay)
{
    
    
    ui->setupUi(this);
    this->setWindowTitle("图片播放器");
    this->setWindowIcon(QIcon(":/ImageEye.jpg"));
    this->setAcceptDrops(true);//设置允许向窗口拖入图片
    this->setMinimumSize(QSize(400,300));// 设置最小值

    ui->photoShow->setAlignment(Qt::AlignCenter);  //居中显示
    //自适应的label+pixmap充满窗口后,无法缩小只能放大
    ui->photoShow->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);// Ignored忽略
    // 内容是否自动缩放,参数true自动缩放
    ui->photoShow->setScaledContents(true);//显示图片的全部

    // 设置拉伸因子(默认缩到最小时失效:一样大小)
    ui->horizontalLayout_2->setStretch(1,3);
    //  设置水平布局控件到边框距离(左,上,右,下)
    ui->horizontalLayout_2->setContentsMargins(5,0,5,0);

    //设置窗口布局,(此时控件会随窗口缩放)
    this->setLayout(ui->horizontalLayout_2);

    ui->photoPath->setFocusPolicy(Qt::NoFocus);//无法获得焦点,自然无法输入,其他文本控件类似
    ui->photoPath->setPlaceholderText("绝对路径");
    ui->delayEdit->setPlaceholderText("秒");

    //不显示行向滚动条,子项文本过长自动显示...
    ui->photoList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    num=0; //照片张数
    delayTime.clear(); //延时间隔
    mtime = new QTimer(this);


    //连接自动播放槽函数
    connect(mtime,SIGNAL(timeout()),this,SLOT(myautoPhoto()));
    connect(ui->photoList,SIGNAL(currentRowChanged(int)),this,SLOT(my_currentRowChanged(int)));


    // 如果文件的后缀名是jpg、jpeg、bmp或png,则接受拖放事件,否则忽略拖放事件
    acceptedFileTypes.append("jpg");
    acceptedFileTypes.append("jpeg");
    acceptedFileTypes.append("bmp");
    acceptedFileTypes.append("png");
    acceptedFileTypes.append("gif");

}

PicturePlay::~PicturePlay()
{
    
    
    delete ui;
}
//自动播放函数
void PicturePlay::myautoPhoto()
{
    
    
    tempDir.clear();
    tempDir=photoPath.at(num); //从容器中找到要播放的照片的相对路径

    ui->photoList->setCurrentRow(num);// QListWidget项跳动

    if(tempDir.endsWith(".gif")  || tempDir.endsWith(".Gif"))
    {
    
    
        showDinamicPhoto(tempDir);
        num++;
    }

    else if(!(tempDir.endsWith(".gif")  || tempDir.endsWith(".Gif")))
    {
    
    
        ui->photoShow->paintEvent_Flag=true;
        ui->photoShow->currentPixmap=pixmap;// 1.先赋值

        pixmap.load(tempDir);// 2.再更新全局图片
        // 先设置下一个的图片;
        ui->photoShow->nextPixmap=pixmap;//3.最后需要立即显示的图片
        // 动画类重新开始;
        ui->photoShow->m_opacityAnimation->start();

        //判断自动播放的时候是否播放到了最后一张图片,如果是则停止自动播放
        if(num ==  (photoPath.count()-1))
        {
    
    
            //qDebug()<<num;
            if(ui->loopPlayback->checkState()==Qt::Checked)//循环重放
            {
    
    
                num = -1;// 下面num++,为了循环重放回到第一张图片
                qDebug()<<num;
            }
            else
            {
    
    
                mtime->stop();
                num = 0;
                if(autoFlag)//选中自动播放的情况
                {
    
    
                    autoFlag = false;
                }
                //qDebug()<<num;
                ui->autoPhoto->setCheckState(Qt::Unchecked);//把自动播放按钮置于没有选择的状态
            }
        }

        if(autoFlag)
        {
    
    
            num++;
        }
    }
}
//动态图播放(格式为gif)
void PicturePlay::showDinamicPhoto(QString path)
{
    
    
    ui->photoShow->clear();
    ui->photoShow->paintEvent_Flag=false;
    QMovie *movie = new QMovie(path);  // path图片路径
    movie->start(); //开始播放动态图
    ui->photoShow->setMovie(movie); //将图片设置为为动态
    ui->photoShow->setScaledContents(true); //尽可能完整的播放整张动图 ,此处要设置为true
}

//浏览:打开目录
void PicturePlay::on_pathBtn_clicked()
{
    
    
    Dir = QFileDialog::getExistingDirectory(this);//获取本文件所在的具体路径
    //qDebug()<<"路径:"<<Dir;
    ui->photoPath->setText(Dir);//显示打开的文件的具体路径
    QDir dir(Dir);
    QStringList file;
    QFileInfoList fileList = dir.entryInfoList(file,QDir::Files); //获取目录下的文件
    QString fileDir; //保存图片所在的路径
    //列出目录下的文件
    for(int i=0;i<fileList.count();i++)
    {
    
    
        if(i==50)
        {
    
    
            break;
        }
        QFileInfo info = fileList.at(i);
        fileDir.clear();
        if(info.fileName() == "." || info.fileName() == "..") //跳过这两个目录
        {
    
    
            continue;
        }
        // 检查文件的后缀名是否在接受的文件类型列表中;(获取文件的后缀名,并将其转换为小写字母)
        if(!acceptedFileTypes.contains(info.suffix().toLower()))
        {
    
    
            continue;
        }

        fileDir+=Dir + "/";
        QString filename = info.fileName();
        fileDir+=filename;
        photoPath.append(fileDir);// 把图片的路径装到容器中

        QListWidgetItem *item = new QListWidgetItem(QIcon(fileDir),info.fileName());//建立文件缩小图标
        item->setToolTip(info.fileName());// tip提示
        item->setTextAlignment(Qt::AlignCenter);//设置item项中的文字位置
        ui->photoList->addItem(item);//把图片相对路径显示到窗口中
    }
    if(ui->photoList->count()==0)
    {
    
    
        QListWidgetItem *item = new QListWidgetItem("无合适图片");
        item->setTextAlignment(Qt::AlignCenter);//设置item项中的文字位置
        ui->photoList->addItem(item);//把图片相对路径显示到窗口中
    }
}

//自动播放选择
void PicturePlay::on_autoPhoto_clicked(bool checked)
{
    
    
    if(ui->delayEdit->text().isEmpty())
    {
    
    
        QMessageBox::warning(this,"提示","请输入需要间隔的播放时间(秒)");
        //把按钮重新置于没有被选中的状态;不要用setCheckable()函数[不可用],很容易出错
        ui->autoPhoto->setCheckState(Qt::Unchecked);
        return;
    }
    else if(ui->photoList->count() == 0)
    {
    
    
        QMessageBox::warning(this,"警告","还没有可以播放的图片");
        ui->autoPhoto->setCheckState(Qt::Unchecked); //把按钮重新置于没有被选中的状态
        return;
    }
    else if(ui->delayEdit->text().toInt()<2)// 图片切换时间大于动画时间
    {
    
    
        QMessageBox::warning(this,"警告","请输入间隔的播放时间大于等于 2 (秒)");
        ui->autoPhoto->setCheckState(Qt::Unchecked); //把按钮重新置于没有被选中的状态
        return;
    }
    else if(checked) //启动定时器
    {
    
    
        delayTime = ui->delayEdit->text();
        mtime->start(delayTime.toInt()*1000);//启动定时器并设置播放时间间隔
        autoFlag = true;// 选中自动播放
        // 第一次:立即显示,无需等待
        ui->photoList->setCurrentRow(num);// QListWidget项跳动
        pixmap.load(photoPath.at(num));// 先更新全局图片
        ui->photoShow->paintEvent_Flag=true;
        ui->photoShow->currentPixmap=ui->photoShow->nextPixmap=pixmap;//再赋值
        update();//  手动刷新
    }
    else if(!checked)//停止定时器
    {
    
    
        mtime->stop();//停止定时器
        delayTime.clear();
        autoFlag = false;
        ui->photoShow->paintEvent_Flag=false;
    }
}

//单击播放图片
void PicturePlay::on_photoList_clicked(const QModelIndex &index)
{
    
    
    qDebug()<<"单击播放图片";
    Q_UNUSED(index);//忽略编译器发出的警告,表明变量event未使用
    //如果选中了自动播放的情况下,点击列表中的内容,则停止自动播放
    if(autoFlag) //选中自动播放的情况
    {
    
    
        mtime->stop();
        //把按钮重新置于没有被选中的状态;不要用setCheckable()函数[不可用],很容易出错
        ui->autoPhoto->setCheckState(Qt::Unchecked);
        autoFlag = false;
    }
    num = ui->photoList->row(ui->photoList->currentItem()); //获取当前点击的内容的行号
    //在没有选中自动播放的情况下,判断当前是否点击了最后一张照片,如果是最后一张照片,在选中自动播放的情况下让它返回到第一张照片
    if(!autoFlag&&num == ui->photoList->count())
    {
    
    
        num = 0;
    }
    tempDir.clear();
    /*tempDir+=Dir+"/";
    QString path = ui->photoList->currentItem()->text();
    tempDir+=path*/;
    tempDir=photoPath.at(num); //从容器中找到要播放的照片的相对路径

    //判断是否是动态图;endsWith()字符串结尾
    if(tempDir.endsWith(".gif") || tempDir.endsWith(".Gif"))
    {
    
    
        showDinamicPhoto(tempDir);
    }
    else
    {
    
    
        if(pixmap.isNull())
        {
    
    
            pixmap.load(tempDir);// 更新全局图片
            ui->photoShow->currentPixmap=ui->photoShow->nextPixmap=pixmap;
        }else
        {
    
    
            ui->photoShow->currentPixmap=pixmap;
            pixmap.load(tempDir);// 更新全局图片
            ui->photoShow->nextPixmap=pixmap;
        }
        ui->photoShow->paintEvent_Flag=true;
        // 动画类重新开始;
        ui->photoShow->m_opacityAnimation->start();
    }
}
// 当前项变化
void PicturePlay::my_currentRowChanged(int Row)
{
    
    
    qDebug()<<"当前项变化:"<<Row;
}
//拖进事件
void PicturePlay::dragEnterEvent(QDragEnterEvent *event)
{
    
    
    // 用于检查拖放的数据是否包含URL,并且获取拖放事件中的URL数量==1
    if(event->mimeData()->hasUrls()&&event->mimeData()->urls().count()==1)
    {
    
    
        // 获取拖放事件中的第一个URL的本地文件路径
        QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
        // 检查文件的后缀名是否在接受的文件类型列表中;(获取文件的后缀名,并将其转换为小写字母)
        if(acceptedFileTypes.contains(file.suffix().toLower()))
        {
    
    
            event->acceptProposedAction();//表明用户可以在窗口部件上拖放对象[接受拖放事件的操作]
        }
    }
}
// 拖进放下事件
void PicturePlay::dropEvent(QDropEvent *event)
{
    
    
    // 获取拖放事件中的第一个URL的本地文件路径
    QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());

    //qDebug()<<"绝对路径:"<<file.absoluteFilePath();
    //从文件中加载图片,参数file.absoluteFilePath()表示包含文件名的绝对路径,load()返回一个bool值,表示是否加载成功
    if(pixmap.load(file.absoluteFilePath()))
    {
    
    
        ui->photoShow->paintEvent_Flag=true;
        ui->photoShow->currentPixmap=ui->photoShow->nextPixmap=pixmap;
        update();//  手动刷新,否则鼠标拖进放下,如果鼠标不在窗口不会刷新,图片不会显示

        ui->photoPath->setText(file.absolutePath());//显示打开的文件的绝对路径,这不包括文件名。
        photoPath.append(file.absoluteFilePath());// 把图片的路径装到容器中

        QListWidgetItem *item = new QListWidgetItem(QIcon(file.absoluteFilePath()),file.fileName());//建立文件缩小图标
        item->setToolTip(file.fileName());// tip提示
        item->setTextAlignment(Qt::AlignCenter);//设置item项中的文字位置
        ui->photoList->addItem(item);//把图片相对路径显示到窗口中
    }else
    {
    
    
        // 错误消息框
        QMessageBox::critical(this,tr("Error"),tr("The image file count not be read"));
    }
}
//用于在窗口大小改变时处理事件
int j=0;
void PicturePlay::resizeEvent(QResizeEvent *event)
{
    
    
    Q_UNUSED(event);//忽略编译器发出的警告,表明变量event未使用
    qDebug()<<"窗口大小改变:"<<j++;
    if(!pixmap.isNull())
    {
    
    
        // 在MyQLaBel的paintEvent绘图事件,会重绘
        //ui->photoShow->setPixmap(pixmap.scaled(ui->photoShow->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
    }
}
//绘图事件
void PicturePlay::paintEvent(QPaintEvent *event)
{
    
    
    //QPainter painter(ui->photoShow);
    /*报错
    QWidget::paintEngine: Should no longer be called
    QPainter::begin: Paint device returned engine == 0, type: 1
    */
    //想要在哪个控件上绘制,就要在哪个控件类中重写 paintEvent() 方法才行。
    //比如想要在 QLabel 上绘图,就需要新建一个类继承于 QLabel,然后重写 paintEvent() 方法
    //也就是在此文件只能用QPainter painter(this);

    Q_UNUSED(event);//忽略编译器发出的警告,表明变量event未使用
}
// 清除
void PicturePlay::on_cleanBtn_clicked()
{
    
    
    mtime->stop();
    num = 0;
    if(autoFlag)//选中自动播放的情况
    {
    
    
        autoFlag = false;
    }
    pixmap.fill(Qt::transparent);//函数用于将QPixmap对象pixmap清空,参数Qt::transparent表示填充透明色,无返回值
    ui->autoPhoto->setCheckState(Qt::Unchecked);//把自动播放按钮置于没有选择的状态
    ui->loopPlayback->setCheckState(Qt::Unchecked);//把循环重放钮置于没有选择的状态
    photoPath.clear();
    ui->photoPath->clear();
    ui->photoList->clear();

    ui->photoShow->clear();
    ui->photoShow->nextPixmap=ui->photoShow->currentPixmap=pixmap;
    ui->photoShow->paintEvent_Flag=false;

}


pictureplay.ui

insert image description here

style.qss

QListWidget
{
    
    
    border:1px solid gray;  /*边界线:宽度、颜色*/
    /*background:gray;*/    /*表格背景色*/
    color:black;        /*前景色:文字颜色*/
    /*margin:5px,5px,0px,50px;*/   /*上、下、左、右,间距*/
}
QListWidget{
    
     /*去掉选中时的虚线框*/
    outline:0px;
}
QListWidget::item
{
    
    
    padding-top:12px;
    padding-bottom:4px;
}
QListWidget::item:hover/*悬停*/
{
    
    
    show-decoration-selected:5;
    background:skyblue;/*天蓝色*/
}
QListWidget::item:selected
{
    
    
    /*border:0px;*/
    background:lightgray;/*浅灰色*/
    padding:0px;
    margin:0px;
    color:red;
}
/*上次选择后保留的状态,鼠标离开后显示*/
QListWidget::item:selected:!active
{
    
    
    border:none;/*无边框*/
    background:lightgreen;/*浅绿色*/
}

Effect

  • start up
    insert image description here
  • run
    insert image description here

insert image description here

source code

Youdaoyun: Qt picture timing scrolling player

Guess you like

Origin blog.csdn.net/qq_47355554/article/details/129235300