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代码:

javascript 复制代码
#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代码:

javascript 复制代码
/ 包含主窗口类的头文件
#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);
}
javascript 复制代码
// 在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);
}
相关推荐
Ase5gqe23 分钟前
关于maven的java面试题汇总
java·开发语言·maven
suuijbd1 小时前
单例模式和单例Bean
java·开发语言·单例模式
华梦岚1 小时前
Perl语言的语法糖
开发语言·后端·golang
厉君韵1 小时前
Scala语言的数据类型
开发语言·后端·golang
霜雪殇璃2 小时前
2025.1.8(qt图形化界面之消息框)
开发语言·qt
runscript.sh3 小时前
golang 开启HTTP代理认证
开发语言·http·golang
一念&4 小时前
python爬虫--简单登录
开发语言·爬虫·python
苏貝貝4 小时前
【C++】多态(下)
java·开发语言·c++
明月看潮生4 小时前
青少年编程与数学 02-008 Pyhon语言编程基础 25课题、文件操作
开发语言·python·青少年编程·文件操作·编程与数学