QT 实现电子相册(五)--实现幻灯片的暂停和切换

状态切换按钮

我们之前制作过按钮类为PicButton,但是这次我们要制作另外一个按钮类,主要是实现状态切换,比如当前是播放状态,那么按钮就显示出暂停的图标,点击暂停后切换为播放的图标。同时还要支持滑动,点击,正常的三种效果。
PicStateBtn构造函数

  1. PicStateBtn::PicStateBtn(QWidget *parent ):QPushButton (parent){
  2. }

添加一个函数设置播放状态和暂停状态的图标

  1. void PicStateBtn::SetIcons(const QString &normal, const QString &hover, const QString &pressed,
  2. const QString &normal_2, const QString &hover_2, const QString &pressed_2)
  3. {
  4. _normal = normal;
  5. _hover = hover;
  6. _pressed = pressed;
  7. _normal_2 = normal_2;
  8. _hover_2 = hover_2;
  9. _pressed_2 = pressed_2;
  10. QPixmap tmpPixmap;
  11. tmpPixmap.load(normal);
  12. this->resize(tmpPixmap.size());
  13. this->setIcon(tmpPixmap);
  14. this->setIconSize(tmpPixmap.size());
  15. _cur_state = PicBtnStateNormal;
  16. }

normal,hover,pressed分别表示播放状态正常,悬浮,点击的效果。
_normal_2,_hover_2,_pressed_2分别表示暂停状态正常,悬浮,点击的效果。
_cur_state表示现在是上述六种状态的哪一种,具体定义在const.h文件里

  1. enum PicBtnState {
  2. PicBtnStateNormal = 1,
  3. PicBtnStateHover = 2,
  4. PicBtnStatePress = 3,
  5. PicBtnState2Normal = 4,
  6. PicBtnState2Hover = 5,
  7. PicBtnState2Press = 6,
  8. };

对应的我们实现了六种状态的加载函数

  1. void PicStateBtn::setNormalIcon(){
  2. qDebug()<<"setNormalIcon _normal " << _normal << endl;
  3. QPixmap tmpPixmap;
  4. tmpPixmap.load(_normal);
  5. this->setIcon(tmpPixmap);
  6. _cur_state = PicBtnStateNormal;
  7. }
  8. void PicStateBtn::setHoverIcon(){
  9. qDebug()<<"setHoverIcon _hover " << _hover << endl;
  10. QPixmap tmpPixmap;
  11. tmpPixmap.load(_hover);
  12. this->setIcon(tmpPixmap);
  13. _cur_state = PicBtnStateHover;
  14. }
  15. void PicStateBtn::setPressIcon(){
  16. qDebug()<<"setPressIcon _pressed " << _pressed << endl;
  17. QPixmap tmpPixmap;
  18. tmpPixmap.load(_pressed);
  19. this->setIcon(tmpPixmap);
  20. _cur_state = PicBtnStatePress;
  21. }
  22. void PicStateBtn::setNormal2Icon()
  23. {
  24. qDebug()<<"setPressIcon _normal_2 " << _normal_2 << endl;
  25. QPixmap tmpPixmap;
  26. tmpPixmap.load(_normal_2);
  27. this->setIcon(tmpPixmap);
  28. _cur_state = PicBtnState2Normal;
  29. }
  30. void PicStateBtn::setHover2Icon()
  31. {
  32. qDebug()<<"setPressIcon _hover_2 " << _hover_2 << endl;
  33. QPixmap tmpPixmap;
  34. tmpPixmap.load(_hover_2);
  35. this->setIcon(tmpPixmap);
  36. _cur_state = PicBtnState2Hover;
  37. }
  38. void PicStateBtn::setPress2Icon()
  39. {
  40. qDebug()<<"setPressIcon _pressed_2 " << _pressed_2 << endl;
  41. QPixmap tmpPixmap;
  42. tmpPixmap.load(_pressed_2);
  43. this->setIcon(tmpPixmap);
  44. _cur_state = PicBtnState2Press;
  45. }

接下来实现鼠标滑动,点击,以及正常效果的切换,我们重写event

  1. bool PicStateBtn::event(QEvent *event)
  2. {
  3. switch (event->type())
  4. {
  5. case QEvent::Enter:
  6. if(_cur_state < PicBtnState2Normal){
  7. setHoverIcon();
  8. }else{
  9. setHover2Icon();
  10. }
  11. break;
  12. case QEvent::Leave:
  13. if(_cur_state < PicBtnState2Normal){
  14. setNormalIcon();
  15. }else{
  16. setHover2Icon();
  17. }
  18. break;
  19. case QEvent::MouseButtonPress:
  20. if(_cur_state < PicBtnState2Normal){
  21. setPressIcon();
  22. }else{
  23. setPress2Icon();
  24. }
  25. break;
  26. case QEvent::MouseButtonRelease:
  27. if(_cur_state < PicBtnState2Normal){
  28. setHover2Icon();
  29. }else{
  30. setHoverIcon();
  31. }
  32. break;
  33. default:
  34. break;
  35. }
  36. return QPushButton::event(event);
  37. }

上述根据现在是播放状态还是暂停状态,再根据鼠标事件设置图标。
另外我们需要实现开始和暂停的切换效果,所以新增两个槽函数

  1. void PicStateBtn::SlotStart()
  2. {
  3. setNormal2Icon();
  4. }
  5. void PicStateBtn::SlotStop()
  6. {
  7. setNormalIcon();
  8. }

我们回到SlideShowDlg.ui文件,将playBtn升级为PicStateBtn。
然后在SlideShowDlg的构造函数里为按钮设置图标,并且将PicAnimationWid的SigStart和SigStop信号与之连接起来

  1. ui->playBtn->SetIcons(":/icon/play.png",":/icon/play_hover.png",":/icon/play_press.png",
  2. ":/icon/pause.png",":/icon/pause_hover.png",":/icon/pause_press.png");
  3. //连接动画和按钮显示状态
  4. connect(ui->picAnimation,&PicAnimationWid::SigStart, ui->playBtn, &PicStateBtn::SlotStart);
  5. connect(ui->picAnimation,&PicAnimationWid::SigStop, ui->playBtn, &PicStateBtn::SlotStop);

这样动画一开始按钮就被设置为暂停图标,表示动画正在运行,点击暂停图标又会切换为运行图标,表示动画已经暂停。

关闭按钮

SlideShowDlg的关闭按钮比较影响体验,我们隐藏边框上的按钮,在构造函数里添加

  1. this->setWindowFlags(Qt::Dialog|Qt::FramelessWindowHint);

升级SlideShowDlg的ui文件中的closeBtn为PicButton,并且在构造函数里添加加载图标的逻辑和信号连接。

  1. ui->closeBtn->SetIcons(":/icon/closeshow.png",":/icon/closeshow_hover.png",
  2. ":/icon/closeshow_press.png");
  3. //连接关闭按钮
  4. connect(ui->closeBtn, &QPushButton::clicked, this, &SlideShowDlg::close);

这样就能响应关闭按钮的事件了。

切换跳转

我们需要点击向前和向后按钮,实现动画图片的切换,这里先升级SlideShowDlg的ui文件中的slidenextBtn和slidpreBtn按钮为PicButton
然后在构造函数添加加载逻辑和信号连接

  1. ui->slidpreBtn->SetIcons(":/icon/previous.png",
  2. ":/icon/previous_hover.png",
  3. ":/icon/previous_press.png");
  4. ui->slidenextBtn->SetIcons(":/icon/next.png",
  5. ":/icon/next_hover.png",
  6. ":/icon/next_press.png");
  7. //连接向后查看按钮
  8. connect(ui->slidenextBtn, &QPushButton::clicked,this,&SlideShowDlg::SlotSlideNext);
  9. //连接向前查看按钮
  10. connect(ui->slidpreBtn, &QPushButton::clicked,this,&SlideShowDlg::SlotSlidePre);

这两个槽函数内部调用了PicAnimationWid的SlideNext和SlidePre

  1. void SlideShowDlg::SlotSlideNext()
  2. {
  3. ui->picAnimation->SlideNext();
  4. }
  5. void SlideShowDlg::SlotSlidePre()
  6. {
  7. ui->picAnimation->SlidePre();
  8. }

PicAnimationWid的两个函数分别实现图片的切换

  1. void PicAnimationWid::SlideNext()
  2. {
  3. Stop();
  4. if(!_cur_item){
  5. return;
  6. }
  7. auto * cur_pro_item = dynamic_cast<ProTreeItem*>(_cur_item);
  8. auto * next_item = cur_pro_item->GetNextItem();
  9. if(!next_item){
  10. return;
  11. }
  12. SetPixmap(next_item);
  13. update();
  14. }
  15. void PicAnimationWid::SlidePre()
  16. {
  17. Stop();
  18. if(!_cur_item){
  19. return;
  20. }
  21. auto * cur_pro_item = dynamic_cast<ProTreeItem*>(_cur_item);
  22. auto * pre_item = cur_pro_item->GetPreItem();
  23. if(!pre_item){
  24. return;
  25. }
  26. SetPixmap(pre_item);
  27. update();
  28. }

切换后动画暂停,变为显示图片的状态。
为了美观,我们完善一下qss

  1. #preListWidget {
  2. color:rgb(231,231,231);
  3. background-color:rgb(46,47,48);
  4. border: 0px;
  5. }
  6. #preListWidget::item:hover {
  7. background: rgb(38, 95, 153);
  8. }
  9. #preListWidget::item:selected {
  10. background: rgb(38, 95, 153);
  11. }
  12. #slidpreBtn,#slidenextBtn,#playBtn {
  13. border: 0px;
  14. }
  15. #slideprewid {
  16. margin-left:10px;
  17. }
  18. #slidenextwid{
  19. margin-right:10px;
  20. }
  21. #preListWidget::item:selected:active{
  22. border: 2px solid #FFFFFF;
  23. }
  24. #preListWidget::item:selected{
  25. border: 2px solid #FFFFFF;
  26. }

将按钮设置为无边框,然后设置了preListWidget的边框和边距等信息。

添加音乐

为了让动画播放过程中能够播放音乐,我们在mainwindow的菜单里添加音乐菜单。

  1. //设置背景音乐动作
  2. QAction * act_music = new QAction(QIcon(":/icon/music.png"), tr("背景音乐"),this);
  3. act_music->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));
  4. menu_set->addAction(act_music);
  5. //设置音乐
  6. connect(act_music, &QAction::triggered, pro_tree_widget, &ProTreeWidget::SlotSetMusic);

在打开音乐的槽函数里弹出一个文件夹对话框

  1. void ProTreeWidget::SlotSetMusic(bool)
  2. {
  3. qDebug() << "SlotSetMusic" <<endl;
  4. QFileDialog file_dialog;
  5. file_dialog.setFileMode(QFileDialog::ExistingFiles);
  6. file_dialog.setWindowTitle("选择导入的文件夹");
  7. file_dialog.setDirectory(QDir::currentPath());
  8. file_dialog.setViewMode(QFileDialog::Detail);
  9. file_dialog.setNameFilter("(*.mp3)");
  10. QStringList fileNames;
  11. if (file_dialog.exec()){
  12. fileNames = file_dialog.selectedFiles();
  13. }
  14. if(fileNames.length() <= 0){
  15. return;
  16. }
  17. _playlist->clear();
  18. for(auto filename : fileNames){
  19. qDebug() << "filename is " << filename << endl;
  20. _playlist->addMedia(QUrl::fromLocalFile(filename));
  21. }
  22. if(_player->state()!=QMediaPlayer::PlayingState)
  23. {
  24. _playlist->setCurrentIndex(0);
  25. }
  26. }

文件夹对话框返回选择的文件列表,我们将文件列表加入播放列表_playlist。然后通过判断_player的状态设置索引。
_player为QMediaPlayer类型,_playlist为QMediaPlaylist类型,我们需要在pro里添加multimedia才能使用。

  1. QT += core gui multimedia

另外要在protreewidget头文件包含

  1. #include<QtMultimedia/QMediaPlayer>
  2. #include<QtMultimedia/QMediaPlaylist>

然后在SlideShowDlg的构造函数添加播放音乐和停止音乐的槽函数

  1. //连接音乐启动信号
  2. connect(ui->picAnimation, &PicAnimationWid::SigStartMusic, _protree_widget,
  3. &ProTreeWidget::SlotStartMusic);
  4. //连接音乐关闭信号
  5. connect(ui->picAnimation,&PicAnimationWid::SigStopMusic, _protree_widget,
  6. &ProTreeWidget::SlotStopMusic);

播放音乐和停止音乐逻辑很简单

  1. void ProTreeWidget::SlotStartMusic()
  2. {
  3. qDebug()<< "ProTreeWidget::SlotStartMusic" << endl;
  4. _player->play();
  5. }
  6. void ProTreeWidget::SlotStopMusic()
  7. {
  8. qDebug()<< "ProTreeWidget::SlotStopMusic" << endl;
  9. _player->pause();
  10. }

到此为止就完成了动画播放的所有逻辑,这里是整体效果图
https://cdn.llfc.club/1675153305157.jpg

源码链接

源码链接
https://gitee.com/secondtonone1/qt-learning-notes

热门评论

热门文章

  1. vscode搭建windows C++开发环境

    喜欢(596) 浏览(82989)
  2. 聊天项目(28) 分布式服务通知好友申请

    喜欢(507) 浏览(6080)
  3. 使用hexo搭建个人博客

    喜欢(533) 浏览(11838)
  4. Linux环境搭建和编码

    喜欢(594) 浏览(13490)
  5. Qt环境搭建

    喜欢(517) 浏览(24581)

最新评论

  1. string类 WangQi888888:确实错了,应该是!isspace(sind[index]). 否则不进入循环,还是原来的字符串“some string”
  2. 堆排序 secondtonone1:堆排序非常实用,定时器就是这个原理制作的。
  3. 网络编程学习方法和图书推荐 Corleone:啥程度可以找工作
  4. 处理网络粘包问题 zyouth: //消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里 if (bytes_transferred < data_len) { memcpy(_recv_msg_node->_data + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred); _recv_msg_node->_cur_len += bytes_transferred; ::memset(_data, 0, MAX_LENGTH); _socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH), std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2, shared_self)); //头部处理完成 _b_head_parse = true; return; } 把_b_head_parse = true;放在_socket.async_read_some前面是不是更好
  5. Qt MVC结构之QItemDelegate介绍 胡歌-此生不换:gpt, google
  6. 聊天项目(7) visualstudio配置grpc diablorrr:cmake文件得改一下 find_package(Boost REQUIRED COMPONENTS system filesystem),要加上filesystem。在target_link_libraries中也同样加上
  7. interface应用 secondtonone1:interface是万能类型,但是使用时要转换为实际类型来使用。interface丰富了go的多态特性,也降低了传统面向对象语言的耦合性。
  8. 无锁并发队列 TenThousandOne:_head  和 _tail  替换为原子变量。那里pop的逻辑,val = _data[h] 可以移到循环外面吗
  9. 答疑汇总(thread,async源码分析) Yagus:如果引用计数为0,则会执行 future 的析构进而等待任务执行完成,那么看到的输出将是 这边应该不对吧,std::future析构只在这三种情况都满足的时候才回block: 1.共享状态是std::async 创造的(类型是_Task_async_state) 2.共享状态没有ready 3.这个future是共享状态的最后一个引用 这边共享状态类型是“_Package_state”,引用计数即使为0也不应该block啊
  10. 再谈单例模式 secondtonone1:是的,C++11以后返回局部static变量对象能保证线程安全了。
  11. 利用栅栏实现同步 Dzher:作者你好!我觉得 std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); 这个例子中的assert fail并不会发生,原子变量设定了非relaxed内存序后一个线程的原子变量被写入,那么之后的读取一定会被同步的,c和d线程中只可能同时发生一个z++未执行的情况,最终z不是1就是2了,我测试了很多次都没有assert,请问我这个观点有什么错误,谢谢!
  12. 面试题汇总(一) secondtonone1:看到网络上经常提问的go的问题,做了一下汇总,结合自己的经验给出的答案,如有纰漏,望指正批评。
  13. protobuf配置和使用 熊二:你可以把dll放到系统目录,也可以配置环境变量,还能把dll丢到lib里
  14. boost::asio之socket的创建和连接 项空月:发现一些错别字 :每隔vector存储  是不是是每个. asio::mutable_buffers_1 o或者    是不是多打了个o
  15. 创建项目和编译 secondtonone1:谢谢支持
  16. 类和对象 陈宇航:支持!!!!
  17. 构造函数 secondtonone1:构造函数是类的基础知识,要着重掌握
  18. 聊天项目(9) redis服务搭建 pro_lin:redis线程池的析构函数,除了pop出队列,还要free掉redis连接把
  19. 聊天项目(15) 客户端实现TCP管理者 lkx:已经在&QTcpSocket::readyRead 回调函数中做了处理了的。
  20. Qt 对话框 Spade2077:QDialog w(); //这里是不是不需要带括号
  21. visual studio配置boost库 一giao里我离giaogiao:请问是修改成这样吗:.\b2.exe toolset=MinGW
  22. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。
  23. C++ 并发三剑客future, promise和async Yunfei:大佬您好,如果这个线程池中加入的异步任务的形参如果有右值引用,这个commit中的返回类型推导和bind绑定就会出现问题,请问实际工程中,是不是不会用到这种任务,如果用到了,应该怎么解决?

个人公众号

个人微信