QT项目----电子相册(5)
文章目录
- 前言
- 一、SlidShowDlg最终实现效果展示
- 二、创建一个类SlidShowDlg
- 1.SlidShowDlg的ui部分
- 2.SlidShowDlg
- 三、PicAnimationWid
- 1.构造函数PicAnimationWid
- 2.析构函数PicAnimationWid
- 3.槽函数TimeOut
- 四、实现SetPixmap函数
- 1.SetPixmap代码实现
- 2.SetPixmap代码思路
- 3.SigUpPreList信号思路讲解
- 五、实现动画
- 1.开始和停止
- 2.双缓冲绘图的逻辑
- 1.代码实现
- 2.思路分析
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
现在我们把幻灯片的页面做出来 就是当用户右键根目录的时候选择轮播图播放
这个时候会进入这个页面 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仓库中
点击此处进入