文章目录
前言
提示:这里可以添加本文要记录的大概内容:
现在我们把幻灯片的页面做出来 就是当用户右键根目录的时候选择轮播图播放
这个时候会进入这个页面 SlidShowDlg
页面中主要部分是一个图片展示区域
下面有一个图片预览的列表
提示:以下是本篇文章正文内容,下面案例可供参考
一、SlidShowDlg最终实现效果展示
二、创建一个类SlidShowDlg
1.SlidShowDlg的ui部分
创建一个名字为SlidShowDlg设计师界面类,然后在ui文件里添加两个QWidget,分别命名为preShow和slideShow。
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仓库中
点击此处进入