Qt实现简易视频播放器

 使用Qt6实现简易音乐播放器,效果如下:

github:

Gabriel-gxb/VideoPlayer: qt6实现简易视频播放器

    

一、整体架构

该代码整体架构围绕着MainWindow类构建一个媒体播放器相关的应用程序。

主要组件

(一)界面组件(通过UI文件关联)
  1.ui成员变量:由Ui::MainWindow生成的界面相关对象,通过setupUi函数在MainWindow构造函数中进行初始化设置。这个界面包含了如slider_volume(音量滑块)、slider_progress(进度滑块)、video_widget(视频显示部件)、pushButton_start(开始按钮)、pushButton_full(全屏按钮)、label_time(时间显示标签)等多个用于交互和显示的控件。

(二)媒体播放组件
  1.player对象:QMediaPlayer类型,是整个媒体播放功能的核心。在MainWindow构造函数中创建,并设置了音频输出(QAudioOutput对象)和视频输出(ui->video_widget)。
  2.QAudioOutput对象:用于处理音频输出相关功能,与player关联,在MainWindow构造函数中创建并配置。

(三)事件处理相关
  1.事件过滤器(eventFilter函数):在MainWindow类中实现。用于处理特定对象(ui->slider_volume、ui->slider_progress、ui->video_widget)的特定事件(鼠标移动、键盘按键等)。
  2.信号 - 槽连接(connect函数):用于连接player对象的多个信号(durationChanged、positionChanged、playbackStateChanged)到MainWindow类中的lambda表达式,实现对媒体播放状态变化的响应并更新界面相关显示。

架构中的交互关系

(一)界面与媒体播放组件的交互
  1.在MainWindow构造函数中,将player的视频输出设置为ui->video_widget,从而建立了界面显示部件与媒体播放核心的关联。
  2.通过多个connect函数建立的信号 - 槽连接,实现了媒体播放状态(时长、当前位置、播放状态等)到界面显示(如滑块位置、标签文本等)的更新。

(二)事件处理与其他组件的交互
  1.在eventFilter函数中,针对不同的被观察对象(ui->slider_volume、ui->slider_progress、ui->video_widget)的特定事件进行处理。
  2.当ui->slider_volume发生鼠标移动事件时,获取滑块值并在鼠标位置显示工具提示,实现了界面操作与用户提示信息的交互。
  3.对于ui->slider_progress的鼠标移动事件,根据player的播放源情况,在鼠标位置显示播放位置和总时长信息,关联了进度滑块操作与媒体播放信息。
  4.当ui->video_widget有键盘按键事件时,根据不同的按键(如Esc键和Space键)执行相应操作,如退出全屏、暂停/播放媒体等,将键盘操作与视频显示及媒体播放操作相联系。

    

二、基本布局

这里使用QVideoWidget来显示视频,需要包含MultimediaWidgets模块,在ui界面中放置一个QWidget组件,然后将其提升为QVideoWidget。

    

三、代码注释

.h代码:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>class QMediaPlayer;QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_pushButton_volume_clicked(bool checked);void on_pushButton_open_clicked();void on_pushButton_start_clicked(bool checked);void on_pushButton_full_clicked(bool checked);void on_slider_volume_valueChanged(int value);void on_slider_progress_sliderMoved(int position);void on_slider_progress_sliderPressed();void on_comboBox_currentIndexChanged(int index);private:Ui::MainWindow *ui;QMediaPlayer *player;QString m_durationTime;QString m_positionTime;// QWidget interface// QObject interface
public:virtual bool eventFilter(QObject *watched, QEvent *event) override;// QWidget interface
};#endif // MAINWINDOW_H

.cpp代码:

/ 包含主窗口类的头文件
#include "mainwindow.h"
// 包含主窗口类的UI头文件
#include "./ui_mainwindow.h"// 包含音频输出相关的头文件
#include <QAudioOutput>
// 包含文件对话框相关的头文件
#include <QFileDialog>
// 包含键盘事件相关的头文件
#include <QKeyEvent>
// 包含列表视图相关的头文件
#include <QListView>
// 包含工具提示相关的头文件
#include <QToolTip>
// 包含目录操作相关的头文件
#include <qdir.h>
// 包含媒体播放器相关的头文件
#include <qmediaplayer.h>// 主窗口类的构造函数
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{// 设置UI界面ui->setupUi(this);// 创建一个QMediaPlayer对象,指定this(主窗口)为父对象player = new QMediaPlayer(this);// 创建一个QAudioOutput对象,指定this为父对象QAudioOutput *audioOutput = new QAudioOutput(this);// 将音频输出设置给媒体播放器player->setAudioOutput(audioOutput);// 将视频输出设置为video_widget(应该是一个用于显示视频的部件)player->setVideoOutput(ui->video_widget);// 为音量滑块安装事件过滤器,事件将被MainWindow类处理(this指向MainWindow类实例)ui->slider_volume->installEventFilter(this);// 为进度滑块安装事件过滤器ui->slider_progress->installEventFilter(this);// 为视频部件安装事件过滤器ui->video_widget->installEventFilter(this);// 连接媒体播放器的durationChanged信号到一个lambda表达式connect(player,&QMediaPlayer::durationChanged,this,[&](qint64 duration){// 设置进度滑块的最大值为媒体的总时长ui->slider_progress->setMaximum(duration);// 将总时长(毫秒)转换为秒int sec = duration/1000;// 将秒转换为分钟int min = sec/60;// 计算剩余的秒数sec %= 60;// 将总时长格式化为"mm:ss"的字符串m_durationTime = QTime(0,min,sec).toString("mm:ss");// 在标签上显示当前播放位置和总时长ui->label_time->setText(m_positionTime+"/"+m_durationTime);});// 连接媒体播放器的positionChanged信号到一个lambda表达式connect(player,&QMediaPlayer::positionChanged,this,[&](qint64 position){// 设置进度滑块的当前值为媒体的播放位置ui->slider_progress->setValue(position);// 将播放位置(毫秒)转换为秒int sec = position/1000;// 将秒转换为分钟int min = sec/60;// 计算剩余的秒数sec %= 60;// 将播放位置格式化为"mm:ss"的字符串m_positionTime = QTime(0,min,sec).toString("mm:ss");// 在标签上更新显示当前播放位置和总时长ui->label_time->setText(m_positionTime+"/"+m_durationTime);});// 连接媒体播放器的playbackStateChanged信号到一个lambda表达式connect(player,&QMediaPlayer::playbackStateChanged,this,[&](QMediaPlayer::PlaybackState newState){// 如果播放状态为停止状态if(newState == QMediaPlayer::StoppedState){// 取消开始按钮的选中状态ui->pushButton_start->setChecked(false);// 设置开始按钮的图标为播放图标ui->pushButton_start->setIcon(QIcon(":/D:/Apps/qIcon/play.png"));}});
}// 主窗口类的析构函数
MainWindow::~MainWindow()
{// 释放UI对象的内存delete ui;
}// 事件过滤器函数,用于处理特定对象的特定事件
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{// 如果被观察的对象是音量滑块并且事件类型是鼠标移动事件if(watched == ui->slider_volume && event->type() == QEvent::MouseMove){// 将事件转换为鼠标事件QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);// 获取音量滑块的当前值int value = static_cast<QSlider*>(watched)->value();// 在鼠标位置显示音量滑块的值QToolTip::showText(mouseEvent->globalPosition().toPoint(),QString::number(value));}// 如果被观察的对象是进度滑块并且事件类型是鼠标移动事件if(watched == ui->slider_progress && event->type() == QEvent::MouseMove){QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);// 如果媒体播放器的播放源不为空if(!player->source().isEmpty())// 在鼠标位置显示当前播放位置和总时长QToolTip::showText(mouseEvent->globalPosition().toPoint(),m_positionTime+"/"+m_durationTime);}// 如果被观察的对象是视频部件并且事件类型是键盘按下事件if(watched == ui->video_widget && event->type() == QEvent::KeyPress){QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);// 如果按下的键是Esc键if(keyEvent->key() == Qt::Key_Escape){// 如果视频部件处于全屏状态if(ui->video_widget->isFullScreen()){// 取消视频部件的全屏状态ui->video_widget->setFullScreen(false);// 取消全屏按钮的选中状态ui->pushButton_full->setChecked(false);}}// 如果按下的键是空格键并且视频部件处于全屏状态if(keyEvent->key() == Qt::Key_Space && ui->video_widget->isFullScreen()){// 如果媒体播放器正在播放if(player->isPlaying()){// 设置开始按钮的图标为播放图标ui->pushButton_start->setIcon(QIcon(":/D:/Apps/qIcon/play.png"));// 取消开始按钮的选中状态ui->pushButton_start->setChecked(false);// 暂停媒体播放器player->pause();}else{// 设置开始按钮的图标为暂停图标ui->pushButton_start->setIcon(QIcon(":/D:/Apps/qIcon/pause.png"));// 设置开始按钮为选中状态ui->pushButton_start->setChecked(true);// 开始播放媒体播放器player->play();}}}// 调用父类(QMainWindow)的事件过滤器函数,继续处理其他事件return QMainWindow::eventFilter(watched,event);
}
// 在MainWindow类中的槽函数,当音量按钮(pushButton_volume)被点击时触发
void MainWindow::on_pushButton_volume_clicked(bool checked)
{// 根据按钮的选中状态(checked)来设置音量滑块(slider_volume)是否可见ui->slider_volume->setVisible(checked);
}// 在MainWindow类中的槽函数,当开始按钮(pushButton_start)被点击时触发
void MainWindow::on_pushButton_start_clicked(bool checked)
{// 如果按钮被选中(checked为true)if(checked){// 设置开始按钮的图标为暂停图标(pause.png)ui->pushButton_start->setIcon(QIcon(":/D:/Apps/qIcon/pause.png"));// 调用player对象的play函数开始播放(这里的player应该是一个用于播放的对象,如QMediaPlayer之类的)player->play();}else{// 如果按钮未被选中,设置开始按钮的图标为播放图标(play.png)ui->pushButton_start->setIcon(QIcon(":/D:/Apps/qIcon/play.png"));// 调用player对象的pause函数暂停播放player->pause();}
}// 在MainWindow类中的槽函数,当全屏按钮(pushButton_full)被点击时触发
void MainWindow::on_pushButton_full_clicked(bool checked)
{// 根据按钮的选中状态(checked)来设置视频窗口(video_widget)是否全屏ui->video_widget->setFullScreen(checked);
}// 在MainWindow类中的槽函数,当音量滑块(slider_volume)的值改变时触发
void MainWindow::on_slider_volume_valueChanged(int value)
{// 设置播放对象(player)的音频输出音量,将滑块的值(0 - 100)转换为0.0 - 1.0之间的数值player->audioOutput()->setVolume(value/100.0);// 如果滑块的值为0,表示静音,设置音量按钮的图标为静音图标(静音.png)if(value==0)ui->pushButton_volume->setIcon(QIcon(":/D:/Apps/qIcon/静音.png"));else// 如果滑块的值不为0,设置音量按钮的图标为音量图标(音量.png)ui->pushButton_volume->setIcon(QIcon(":/D:/Apps/qIcon/音量.png"));
}// 在MainWindow类中的槽函数,当打开按钮(pushButton_open)被点击时触发
void MainWindow::on_pushButton_open_clicked()
{// 获取当前路径QString curPath = QDir::currentPath();// 设置文件对话框的标题为"打开文件"QString title = "打开文件";// 设置文件过滤器,这里只显示*.mp4文件QString filter = "*mp4";// 设置文件对话框的选项,这里表示不使用本地对话框(DontUseNativeDialog)QFileDialog::Options options = QFileDialog::DontUseNativeDialog;// 弹出文件对话框,获取用户选择的文件路径,返回值存储在afile中QString afile = QFileDialog::getOpenFileName(this,curPath,title,filter,nullptr,options);// 如果用户没有选择文件(返回的文件路径为空字符串),直接返回,不进行后续操作if(afile.isEmpty())return;// 设置播放对象(player)的播放源为用户选择的本地文件(将本地文件路径转换为QUrl格式)player->setSource(QUrl::fromLocalFile(afile));// 开始播放player->play();// 设置开始按钮为选中状态,并设置其图标为暂停图标(pause.png)ui->pushButton_start->setChecked(true);ui->pushButton_start->setIcon(QIcon(":/D:/Apps/qIcon/pause.png"));
}// 在MainWindow类中的槽函数,当进度滑块(slider_progress)被移动时触发
void MainWindow::on_slider_progress_sliderMoved(int position)
{// 设置播放对象(player)的播放位置为滑块的当前位置player->setPosition(position);
}// 在MainWindow类中的槽函数,当进度滑块(slider_progress)被按下时触发
void MainWindow::on_slider_progress_sliderPressed()
{// 设置播放对象(player)的播放位置为进度滑块(slider_progress)的当前值player->setPosition(ui->slider_progress->value());
}// 在MainWindow类中的槽函数,当组合框(comboBox)的当前索引改变时触发
void MainWindow::on_comboBox_currentIndexChanged(int index)
{// 获取组合框中当前选中项的文本QString str = ui->comboBox->itemText(index);// 将文本去掉最后一个字符(可能是单位之类的)后转换为双精度浮点数qreal value = str.left(str.size()-1).toDouble();// 设置播放对象(player)的播放速率为转换后的数值player->setPlaybackRate(value);
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/14360.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 JFreeChart 创建动态图表:从入门到实战

文章目录 前言一、JFreeChart 简介二、环境准备三、 创建第一个折线图四、自定义图表样式4.1 设置背景色4.2 设置折线颜色4.3 设置字体&#xff08;解决中文乱码&#xff09;4.4 设置横坐标的标签宽度和方向 五、导出图表六、实战&#xff1a;动态生成日报图表总结 前言 在数据…

Codeforces Round 940 (Div. 2) and CodeCraft-23(A-D)

题目链接&#xff1a;Dashboard - Codeforces Round 940 (Div. 2) and CodeCraft-23 - Codeforces A. Stickogon 思路 正多边形意味着要用相等的木棍&#xff0c;相等的木棍最少需要3根才能组成正三角&#xff0c;我们把相等的数的数量除3加起来 代码 void solve(){int n;…

C++开发(软件开发)常见面试题

目录 1、C里指针和数组的区别 2、C中空指针请使用nullptr不要使用NULL 3、http/https区别和头部结构&#xff1f; 4、有了mac地址为什么还要ip地址&#xff1f;ip地址的作用 5、有了路由器为什么还要交换机&#xff1f; 6、面向对象三大特性 7、友元函数 8、大端小端 …

常用数据结构之String字符串

字符串 在Java编程语言中&#xff0c;字符可以使用基本数据类型char来保存&#xff0c;在 Java 中字符串属于对象&#xff0c;Java 提供了 String 类来创建和操作字符串。 操作字符串常用的有三种类&#xff1a;String、StringBuilder、StringBuffer 接下来看看这三类常见用…

wordpressAI工具,已接入Deepseek 支持自动生成文章、生成图片、生成长尾关键词、前端AI窗口互动、批量采集等

基于关键词或现有内容生成SEO优化的文章&#xff0c;支持多种AI服务&#xff08;如OpenAI、百度文心一言、智谱AI等&#xff09;&#xff0c;并提供定时任务、内容采集、关键词生成等功能。 核心功能 文章生成 关键词生成&#xff1a;根据输入的关键词生成高质量文章。 内容…

Vite 为什么快,是怎么打包的

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

协议-ACLLite-ffmpeg

是什么&#xff1f; FFmpeg是一个开源的多媒体处理工具包&#xff0c;它集成了多种功能&#xff0c;包括音视频的录制、转换和流式传输处理。FFmpeg由一系列的库和工具组成&#xff0c;其中最核心的是libavcodec和libavformat库。 libavcodec是一个领先的音频/视频编解码器库&…

【C++高并发服务器WebServer】-12:TCP详解及实现

本文目录 一、TCP通信流程二、套接字函数2.1 socket()2.2 bind()2.3 listen()2.4 accept()2.5 connect() 三、demo实现3.1 server端代码3.2 client端代码 四、TCP三次握手五、TCP滑动窗口六、TCP四次挥手七、多进程并发服务器 一、TCP通信流程 先来讲讲服务器端&#xff0c;是…

【Maven】项目管理工具-Maven

目录 1. Maven简介 1.1 项目管理 1.2 项目构建 1.3 项目构建工具 1.4 Maven的四大特征 1.4.1 依赖管理系统 1.4.2 多模块构建 1.4.3 一致的项目结构 1.4.4 一致的构建模型和插件机制 1.5 Maven模型 ​编辑 2.maven的安装配置 2.1 Maven的安装配置 2.1.1检测jdk的版…

dijkstra算法类型题解

dijkstra算法&#xff08;有权图&#xff0c;无权图&#xff09;&#xff1a; 带权路径长度——当图是带权图时&#xff0c;一条路径上所有边的权值之和&#xff0c;称为该路径的带权路径长度 初始化三个数组&#xff0c;final标记各顶点是否已找到最短路径&#xff0c;dist最…

RabbitMQ 消息顺序性保证

方式一&#xff1a;Consumer设置exclusive 注意条件 作用于basic.consume不支持quorum queue 当同时有A、B两个消费者调用basic.consume方法消费&#xff0c;并将exclusive设置为true时&#xff0c;第二个消费者会抛出异常&#xff1a; com.rabbitmq.client.AlreadyClosedEx…

基于开源AI智能名片2+1链动模式S2B2C商城小程序的个人IP活动运营策略与影响力提升研究

摘要&#xff1a;本文围绕个人IP运营者借助活动运营提升影响力这一主题&#xff0c;深入探讨如何将开源AI智能名片21链动模式S2B2C商城小程序融入借势、造势、提升参与感及用户激励等活动运营环节。通过分析该创新模式与活动运营各要素的结合点&#xff0c;为个人IP运营者提供切…

计算机图形学论文 | 面向制造的设计: 五轴铣削的几何制造可行性评估

&#x1f355;&#x1f355;&#x1f355;宝子们好久不见&#xff0c;新年快乐~~~&#xff0c;今天我们来更新一篇关于五轴CNC制造中的模型制造可达性分析的论文。老规矩&#xff1a; 红色是名词&#xff0c;蓝色是结论&#xff0c;绿色是文章工作&#xff0c;黄色是一些其他重…

deepseek搭建本地知识库

ollama是一个大模型的运行框架&#xff0c;在上面可以运行不同的大模型 部署deepseek 下载ollama&#xff1a;https://ollama.com/ 下载模型&#xff1a;https://ollama.com/library/deepseek-r1:1.5b ollama run deepseek-r1:1.5b运行起来之后&#xff0c;本地命令行就可以…

青少年编程与数学 02-009 Django 5 Web 编程 01课题、概要

青少年编程与数学 02-009 Django 5 Web 编程 01课题、概要 一、Django 5Django 5 的主要特性包括&#xff1a; 二、MVT模式三、官方网站四、内置功能数据库 ORM&#xff08;对象关系映射&#xff09;用户认证和授权表单处理模板引擎URL 路由缓存框架国际化和本地化安全性功能管…

deepseek本地部署-linux

1、官网推荐安装方法(使用脚本,我绕不过github,未采用) 登录ollama下载网站https://ollama.com/download/linux,linux下有下载脚本。 正常来说,在OS系统下直接执行脚本即可。 2、手动安装方法 2.1获取ollama-linux-arm64.tgz wget https://ollama.com/download/ollam…

多光谱技术在华为手机上的应用发展历史

2018 年&#xff0c;华为 P20 系列首次搭载 5 通道色温传感器&#xff0c;可帮助手机在不同光照条件下保持画面色彩一致性。 2020 年&#xff0c;华为 P40 系列搭载 8 通道多光谱色温传感器&#xff08;实际为 11 通道&#xff0c;当时只用 8 个通道检测可见光&#xff09;&am…

增加工作台菜单页面,AI问答应用支持上下文设置,数据库表索引优化,zyplayer-doc 2.4.8 发布啦!

zyplayer-doc是一款适合企业和个人使用的WIKI知识库管理工具&#xff0c;支持在线编辑富文本、Markdown、表格、Office文档、API接口、思维导图、Drawio以及任意的文本文件&#xff0c;专为私有化部署而设计&#xff0c;最大程度上保证企业或个人的数据安全&#xff0c;支持以内…

4.python+flask+SQLAlchemy+达梦数据库

前提 1.liunx Centos7上通过docker部署了达梦数据库。从达梦官网下载的docker镜像。(可以参考前面的博文) 2.windows上通过下载x86,win64位的达梦数据库,只安装客户端,不安装服务端。从达梦官网下载达梦数据库windows版。(可以参考前面的博文) 这样就可以用windows的达…

基础入门-网站协议身份鉴权OAuth2安全Token令牌JWT值Authirization标头

知识点&#xff1a; 1、网站协议-http/https安全差异&#xff08;抓包&#xff09; 2、身份鉴权-HTTP头&OAuth2&JWT&Token 一、演示案例-网站协议-http&https-安全测试差异性 1、加密方式 HTTP&#xff1a;使用明文传输&#xff0c;数据在传输过程中可以被…