Qt实现简易视频播放器

news/2025/2/9 5:43:46 标签: 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_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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.niftyadmin.cn/n/5845604.html

相关文章

携手AWS,零成本在EKS上体验AutoMQ企业版

01 前言 AutoMQ是一款贯彻云优先理念来设计的 Kafka 替代产品。AutoMQ 创新地对 Apache Kafka 的存储层进行了基于云的重新设计&#xff0c;在 100% 兼容 Kafka 的基础上通过将持久性分离至 EBS 和 S3 带来了 10x 的成本降低以及 100x 的弹性能力提升&#xff0c;并且相比 Apa…

springcloud gateway 负载均衡

Spring Cloud Gateway的负载均衡是Spring Cloud生态系统中一个非常重要的功能&#xff0c;它使得微服务架构中的服务调用能够更加高效和均衡。以下是关于Spring Cloud Gateway负载均衡的详细解析&#xff1a; 一、Spring Cloud Gateway简介 Spring Cloud Gateway是一个基于Sp…

使用 Apifox、Postman 测试 Dubbo 服务,Apache Dubbo OpenAPI 即将发布

作者&#xff1a;何亮&#xff0c;Apache Dubbo Contributor Apache Dubbo OpenAPI 简介 设计背景 在微服务体系中&#xff0c;RPC 服务的文档管理、测试、调用协作一直都是影响研发效能的关键一环&#xff0c;这些难题通常是由于 RPC 的特性所决定的&#xff1a;RPC 服务的…

SpringSecurity:授权服务器与客户端应用(入门案例)

文章目录 一、需求概述二、开发授权服务器1、pom依赖2、yml配置3、启动服务端 三、开发客户端应用1、pom依赖2、yml配置3、SecurityConfig4、接口5、测试 一、需求概述 maven需要3.6.0以上版本 二、开发授权服务器 1、pom依赖 <dependency><groupId>org.springfr…

leetcode_深度遍历和广度遍历 100. 相同的树

100. 相同的树 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两棵树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 思路: (递归法) 返回True的情况: 两棵树都为空两棵树相同 返回False的情况: 两棵…

知识图谱智能应用系统:数据存储架构与流程解析

在当今数字化时代,知识图谱作为一种强大的知识表示和管理工具,正逐渐成为企业、科研机构以及各类智能应用的核心技术。知识图谱通过将数据转化为结构化的知识网络,不仅能够高效地存储和管理海量信息,还能通过复杂的查询和推理,为用户提供深度的知识洞察。然而,构建一个高…

RK3568上使用C++结合V4L2拉流,并RKMPP硬件编解码,并保存为MP4文件

在RK3568平台上使用C结合V4L2捕获视频流&#xff0c;并通过RKMPP进行硬件编码后保存为MP4文件&#xff0c;可以按照以下步骤实现&#xff1a; 1. 环境准备 硬件&#xff1a;RK3568开发板、摄像头模块。软件依赖&#xff1a; Linux内核支持V4L2。Rockchip MPP库&#xff08;RKM…

【RocketMQ 存储】- 同步刷盘和异步刷盘

文章目录 1. 前言2. 概述3. submitFlushRequest 提交刷盘请求4. FlushDiskWatcher 同步刷盘监视器5. 同步刷盘但是不需要等待刷盘结果6. 小结 本文章基于 RocketMQ 4.9.3 1. 前言 RocketMQ 存储部分系列文章&#xff1a; 【RocketMQ 存储】- RocketMQ存储类 MappedFile【Rock…