QT项目----电子相册(5)


前言

提示:这里可以添加本文要记录的大概内容:

现在我们把幻灯片的页面做出来 就是当用户右键根目录的时候选择轮播图播放
这个时候会进入这个页面 SlidShowDlg
页面中主要部分是一个图片展示区域
下面有一个图片预览的列表


提示:以下是本篇文章正文内容,下面案例可供参考

一、SlidShowDlg最终实现效果展示

在这里插入图片描述

二、创建一个类SlidShowDlg

1.SlidShowDlg的ui部分

创建一个名字为SlidShowDlg设计师界面类,然后在ui文件里添加两个QWidget,分别命名为preShowslideShow

1 将SlidShowDlg设置为垂直布局,将拉伸比例设置为7比1,这样整体的布局被分为两部分,上部分为slideShow,下部分为preShow,slideShow设置为网格布局,preShow设置为垂直布局。
2 在slideShow里添加两个固定宽度为80像素的widget(slidenextwid和slideprewid),这个两个widget设置为垂直布局,分别在widget里添加button(slidenextBtn和slidepreBtn),
3 在slideShow里添加一个水平布局放在右上角,然后在该布局里添加两个按钮(closeBtn和playBtn)。
4 在slideShow里添加一个widget(命名为picAnimation)设置为网格布局
5 在preShow中添加一个高度固定为120的widget,让其宽度自适应,该widget设置为网格布局,在该widget内部添加一个widget命名为preListWidget,主要用来展示预览图
在这里插入图片描述
我们对右上角的开始暂停(playBtn)和关闭按钮(closeBtn)都进行提升,提升为PicBtn
PicStateBtn
这两个类是我们自己定义的

同样我们将下面的列表提升为PreListWid
中间展示区提升为PicAnimationWid

然后我们一一实现这些我们定义的类

2.SlidShowDlg

构造函数

explicit SlidShowDlg(QWidget *parent,QTreeWidgetItem* first_item,
QTreeWidgetItem* last_item);

first_item表示播放的第一个item
last_item表示播放的最后一个item

然后我们在ProTreeWidget中加入轮播图这个选项,并且进行连接

_action_slideshow=new QAction(QIcon("qrc:/photo/1daeb62f494e3a0f040ea34c18cd41f.jpg"),
tr("轮播图播放"),this);
connect(_action_slideshow,&QAction::triggered,this,&ProTreeWidget::SlotSlidShow);

然后我们完善这个槽函数就行

内部创建一个SlideShowDlg智能指针对象,然后设置为模态对话框,并且最大化显示

void ProTreeWidget::SlotSlidShow()
{
    if(!_right_btn_item)
    {
        return;
    }

    auto *now=dynamic_cast<ProTreeItem*>(_right_btn_item);

    auto *first_child_item=now->GetFirstPicChild();
    auto *last_child_item=now->GetLastPicChild();
    if(!first_child_item||!last_child_item)return;

    //qDebug()<<"first"<<first_child_item->GetPath();
    // qDebug()<<"last"<<last_child_item->GetPath();
    _slide_show_dlg=std::make_shared<SlidShowDlg>(this,first_child_item,last_child_item);

    _slide_show_dlg->setModal(true);//模态
    _slide_show_dlg->showMaximized();


}

GetFirstPicChild()GetLastPicChild() 是我们写好的函数

ProTreeItem *ProTreeItem::GetFirstPicChild()//获取第一张照片
{
    if(this->type()==TreeItemPic)
    {
        return nullptr;
    }

    auto child_count=this->childCount();
    if(child_count==0)
    {
        return nullptr;
    }

    for(int i=0;i<child_count-1;i++)
    {
        auto * first_child=this->child(i);
        auto * first_tree_child=dynamic_cast<ProTreeItem*>(first_child);
        if(first_tree_child->type()==TreeItemPic)//是图片
        {
            return first_tree_child;
        }

        //是文件夹
        first_child=first_tree_child->GetFirstPicChild();
        if(!first_child)continue;//为空 什么也没有

        first_tree_child=dynamic_cast<ProTreeItem*>(first_child);
        return first_tree_child;

    }
    return nullptr;
}

获取最后一张图片从后往前找就行

三、PicAnimationWid

1.构造函数PicAnimationWid

接下来我们需要在SlideShowDlg的动画区域添加动画逻辑,类PicAnimationWid为图片动画展示窗口,继承于QWidget,构造函数比较简单

PicAnimationWid::PicAnimationWid(QWidget *parent)
    : QWidget{parent},_factor(0.0),_cur_item(nullptr),_b_start(false)
{
    _timer=new QTimer(this);
    connect(_timer,&QTimer::timeout,this,&PicAnimationWid::TimeOut);
}

_factor为动画因子,控制图片渐隐效果
_b_start控制动画是否播放
_cur_item 表示当前要绘制显示的ProTreeItem对象。
启动了一个定时器,然后定时回调TimeOut函数
同样的道理析构函数需要实现定时器的停止

2.析构函数PicAnimationWid

PicAnimationWid::~PicAnimationWid()
{
    _timer->stop();
}

connect(_timer,&QTimer::timeout,this,&PicAnimationWid::TimeOut);这个&QTimer::timeout就是到达我们设置好的时间的时候就会发送一个信号触发这个&PicAnimationWid::TimeOut槽函数

我们实现槽函数TimeOut

3.槽函数TimeOut

void PicAnimationWid::TimeOut()
{
    if(!_cur_item)
    {
        Stop();
        update();
        return;
    }

    _factor+=0.01;
    if(_factor>=1)
    {
        _factor=0;
        auto* cur_pro_item=dynamic_cast<ProTreeItem*>(_cur_item);
        auto *next_pro_item=cur_pro_item->GetNextItem();
        if(!next_pro_item)
        {
            Stop();
            update();
            return;
        }
        SetPixmap(next_pro_item);
        update();
        return;
    }

    update();
}

这里的思路就是如果此时我们有图片,就往下走,然后判断这张图的下一张还有没有图
如果有,我们就调用 SetPixmap 来设置下一张图片
当然我们在 SetPixmap中也有更新_cur_item 等于下一张图

函数每次对factor增加0.01进而控制动画,如果factor变为1说明已经完成一张图片的消失和另一张的展示,需要更新下一组两张图片用来做渐隐渐现的效果。
update函数是基类的刷新函数,会触发paintEvent函数,这个函数功能之后介绍。先介绍SetPixmap函数,该函数用来加载两张图片做渐变效果。
实现SetPixmap设置要绘制的图片

四、实现SetPixmap函数

1.SetPixmap代码实现

void PicAnimationWid::SetPixmap(QTreeWidgetItem *item)
{
    if(!item)
    {
        return;
    }

    auto* tree_item=dynamic_cast<ProTreeItem*>(item);
    auto path=tree_item->GetPath();
    _pixmap1.load(path);
    _cur_item=tree_item;
    if(_map_items.find(path)==_map_items.end())
    {
        _map_items[path]=tree_item;
        //发送更新列表逻辑
        emit SigUpPreList(item);

    }

    emit SigSelectItem(item);

    auto* next_item=tree_item->GetNextItem();
    if(!next_item)
    {
        return ;
    }

    auto next_path=next_item->GetPath();
    _pixmap2.load(next_path);
    if(_map_items.find(next_path)==_map_items.end())
    {
        _map_items[next_path]=next_item;
        //发送更新列表逻辑
        emit SigUpPreList(next_item);
    }
}

2.SetPixmap代码思路

1.首先函数参数是这个图片节点,然后用dynamic_cast转换为我们自定义的ProTreeItem
这样我们可以调用我们写好的函数来获取这张图片的地址,
2.然后将这个图先加载到第一张图画上(_pixmap1)
此时别忘记更新_cur_item
3.然后我们判断这个图是否出现过,如果没出现我们就存入容器中
我们这里的信号 SigUpPreList是通知下面的列表要更新照片
4.下面的获取下一张图也是同理 然后存入第二张图画中

因为要双缓冲绘图,所以要缓存两张图片,用_pixmap1和_pixmap2缓存。
实现_pixmap1渐隐,_pixmap2渐现

SigUpPreList信号是用来通知下方预览框更新预览图,因为我们要做的是上方播放动画后,下方会更新预览图。
当前正在播放的图在下方预览图有选中提示,所以SigSelectItem信号是用来通知下方预览图选中效果。
接下来要实现开始函数,让动画动起来

3.SigUpPreList信号思路讲解

这里我们发送信号SigUpPreList
我们在ProTreeWidget中连接信号,然后在PreListWid中实现一个槽函数,然后添加一个图片节点就行
下面我直接放代码

1.连接信号

 connect(ui->picAnimation,&PicAnimationWid::SigUpPreList,prelistWid,&PreListWid::SlotUpPreList);

2.实现槽函数

void PreListWid::SlotUpPreList(QTreeWidgetItem *tree_item)
{
    if(!tree_item)
    {
        return ;
    }

    auto * pro_item=dynamic_cast<ProTreeItem*>(tree_item);
    auto path=pro_item->GetPath();
    auto it=_set_items.find(path);
    if(it!=_set_items.end())return;//说明列表中已经有这个图了

    AddListItem(path);

}

3.实现添加图片函数 AddListItem

void PreListWid::AddListItem(const QString &path)
{
    QPixmap src_pixmap(path);
    src_pixmap=src_pixmap.scaled(PREICON_SIZE,PREICON_SIZE,Qt::KeepAspectRatio);

    QPixmap dst_pixmap(QSize(PREICON_SIZE,PREICON_SIZE));
    dst_pixmap.fill(QColor(220,220,220,50));//填充一个透明的灰色

    QPainter painter(&dst_pixmap);//创建一个画刷

	//获取图片宽高
    auto src_width=src_pixmap.width();
    auto src_height=src_pixmap.height();

	//获取画板宽高
    auto dst_width=dst_pixmap.width();
    auto dst_height=dst_pixmap.height();

	//使得图片放入画板中可以居中
    auto x=((dst_width-src_width)/2);
    auto y=((dst_height-src_height)/2);

    painter.drawPixmap(x,y,src_pixmap);//将图片绘制到画板中

    _global++;
    auto* pItem=new PreListItem(QIcon(dst_pixmap),path,_global,this);
    pItem->setSizeHint(QSize(PREICON_SIZE,PREICON_SIZE));
    this->addItem(pItem);
    _set_items[path]=pItem;
    if(_global==1)
    {
        _pos_origin=this->pos();
    }

}

这里我们的PREICON_SIZE是我们自己定义的长度 90

五、实现动画

1.开始和停止

void PicAnimationWid::Start()
{
    emit SigStart();
    emit SigStartMusic();
    _factor=0;
    _timer->start(15);
    _b_start=true;
}

SigStart信号用来通知右上方按钮的显示播放还是暂停状态,之后在处理信号连接问题。
_factor为动画因子
_b_start被设置为true
定时器每隔15ms更新一次
SigStartMusic信号用来更新音乐,之后再处理信号连接问题
同样实现一个停止动画的逻辑

void PicAnimationWid::Stop()//停止动画
{
    emit SigStop();
    emit SigStopMusic();
    _timer->stop();
    _factor=0;
    _b_start=false;
}

我们接下来要实现双缓冲绘图的逻辑

2.双缓冲绘图的逻辑

1.代码实现

void PicAnimationWid::paintEvent(QPaintEvent *event)
{
    if(_pixmap1.isNull())
    {
        return;
    }

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);//效果更好 防锯齿

    //将图片等比例拉伸
    QRect rect=geometry();
    int w=rect.width();
    int h=rect.height();
    _pixmap1=_pixmap1.scaled(w,h,Qt::KeepAspectRatio);

    //透明图
    QPixmap alphaPixmap(_pixmap1.size());
    alphaPixmap.fill(Qt::transparent);


    QPainter p1(&alphaPixmap);
    p1.setCompositionMode(QPainter::CompositionMode_Source);//原图覆盖到目标图像上
    p1.drawPixmap(0,0,_pixmap1);//把 _pixmap1 画到 alphaPixmap 上,左上角从 (0,0) 开始
    p1.setCompositionMode(QPainter::CompositionMode_Destination);//此模式不会破坏原图
    int alpha=255*(1.0f-_factor);
    p1.fillRect(alphaPixmap.rect(),QColor(0,0,0,alpha));
    p1.end();


    int x = (w - _pixmap1.width()) / 2;
    int y = (h - _pixmap1.height()) / 2;
    painter.drawPixmap(x, y, alphaPixmap);
    if(_pixmap2.isNull())
    {
        return;
    }

    _pixmap2=_pixmap2.scaled(w,h,Qt::KeepAspectRatio);
    alpha = 255 * (_factor);
    QPixmap alphaPixmap2(_pixmap2.size());
    alphaPixmap2.fill(Qt::transparent);
    QPainter p2(&alphaPixmap2);
    p2.setCompositionMode(QPainter::CompositionMode_Source);
    p2.drawPixmap(0, 0, _pixmap2);
    p2.setCompositionMode(QPainter::CompositionMode_DestinationIn);
    p2.fillRect(alphaPixmap2.rect(), QColor(0, 0, 0, alpha));
    p2.end();
    x = (w - _pixmap2.width()) / 2;
    y = (h - _pixmap2.height()) / 2;
    painter.drawPixmap(x, y, alphaPixmap2);
}

2.思路分析

所谓双缓冲绘图逻辑如下:

提前加载好图片的两个pixmap分别为_pixmap1和_pixmap2。然后基于现在的widget大小做等比拉伸
创建两个pixmap用作遮盖,分别为alphaPixmap和alphaPixmap2,将他们填充为透明的颜色
分别创建两个画刷,然后绑定alphaPixmap和alphaPixmap2,用画刷分别绘制_pixmap1和_pixmap2
CompositionMode_DestinationIn表示遮罩的模式为显示重叠区域,CompositionMode_Source表示原图的绘制模式。
最后根据alpha值分别p2和p1的两个矩形区域设置透明度。
最后统一用一个painter分别绘制两个alphaPixmap和alphaPixmap2

总结

后面我们把轮播图下方的图像列表做完,当中间动画播放的时候,下面加载出已经播放的图片列表
源代码已经放在Github仓库中
点击此处进入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值