QT:音视频播放器

目录

一.播放器设计

二.需要使用的控件

三.选择视频

四.播放视频

五.暂停视频

六.关闭视频

七.播放状态设置

八.切换视频(上一首)

九.切换视频(下一首)

十.设置视频滑块

十一.更新滑块显示

十二.实现效果

十三.代码设计

1.mainwindow.h

2.mainwindow.cpp


一.播放器设计

播放器主要包含了媒体播放器的基本功能,如选择视频,播放、停止、快进、快退、播放列表管理、时间显示、状态指示等。

mainwindow.ui

二.需要使用的控件

QSlider是一个用于用户输入的控件,主要用于实现滑动条功能。滑动条允许用户通过滑动滑块在一组连续的值中选择一个值。QSlider通常用于控制数值的调整,如音量、亮度、滚动条等。

QLabel是一个用于显示文本或者图像的控件。它是Qt框架中用于界面布局和显示信息的基本组件之一。QLabel可以用来显示各种类型的信息,如文本、图片、图标等。

QListView是一个用于显示和浏览项目列表的视图控件。它通常与QModel(如QAbstractListModel或QStandardItemModel)一起使用来管理数据,并允许用户通过滚动和点击来浏览这些数据。

QPushButton是一个常用的标准控件,用于创建按钮,用户可以通过点击按钮来触发事件。QPushButton可以显示文本、图标或者两者的组合。

三.选择视频

  1. 打开一个文件对话框,让用户选择一个或多个视频文件。
  2. 如果用户选择了文件,则将文件路径添加到一个QStringList对象中。
  3. 遍历这些文件路径,并将每个文件的名称添加到一个QStandardItemModel(假设名为model)中。
  4. 设置一个多媒体播放器(假设名为player)的媒体内容为第一个文件的内容,准备播放。

四.播放视频

五.暂停视频

六.关闭视频

七.播放状态设置

八.切换视频(上一首)

  1. 获取和检查索引:首先获取当前在列表视图中选中的项目的索引,然后检查这个索引是否有效。如果有效,说明用户已经选中了一个项目。

  2. 处理有效索引:如果当前索引有效,代码将计算前一个项目的索引,并确保这个索引在列表范围内循环,即使当前处于第一项也能回到最后一项。然后,它将这个前一个项目设置为列表视图的当前选中项,并获取该项对应的媒体文件路径,最后通过媒体播放器对象播放这个媒体文件。

  3. 处理无效索引:如果当前索引无效,即没有选中任何项目,代码将自动选择列表中的最后一项,并播放与该项对应的媒体文件。这确保了即使在没有任何选中项的情况下,用户也能通过播放列表的最后一项来开始播放。

九.切换视频(下一首)

1.当用户点击“下一项”按钮时被调用。它首先获取当前选中的列表项索引,如果索引有效,则获取下一项的索引,如果下一项索引无效(即已经是最后一项),则跳转到列表的第一项。

2.将列表视图的当前索引设置为新的索引,并播放与该索引对应的媒体文件。如果当前索引无效(即列表为空或没有选中任何项),它将选择并播放列表的第一项。

十.设置视频滑块

十一.更新滑块显示

1.更新滑块显示

2.计算已经观看时间和剩余时间

3.格式化时间

4.更新已观看时间和剩余时间都标签

十二.实现效果

十三.代码设计

1.mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDialog>
#include <QWidget>
#include <QMediaPlayer>
#include <QVideoWidget>
#include <QPushButton>
#include <QLineEdit>
#include <QFileDialog>
#include <QUrl>
#include <QListView>
#include <QStandardItem>
#include <QStandardItemModel>


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_play_clicked();

    void on_stop_clicked();

    void on_pushButton_clicked();

    void on_close_clicked();

    void on_horizontalSlider_actionTriggered(int position);

    void on_listView_doubleClicked(const QModelIndex &index);

    void updatePosition(qint64 position);
    void labelstateChanged(QMediaPlayer::State state);

    QString formatTime(int seconds);

    void on_previous_clicked();

    void on_next_clicked();

private:
    QMediaPlayer *player;
    QVideoWidget *videoWidget;
    QString videoPath;

    QStandardItemModel *model; // 声明 model 变量

    Ui::MainWindow *ui;

};
#endif // MAINWINDOW_H

2.mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMediaPlayer>
#include <QVideoWidget>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    player = new QMediaPlayer(this);
    videoWidget = new QVideoWidget(this);

    // 设置视频输出
    player->setVideoOutput(videoWidget);


    connect(player, &QMediaPlayer::positionChanged,this, &MainWindow::updatePosition);
    connect(player, &QMediaPlayer::stateChanged,this,&MainWindow::labelstateChanged);
    connect(ui->listView, &QListView::doubleClicked, this, &MainWindow::on_listView_doubleClicked);

    connect(ui->horizontalSlider, &QSlider::valueChanged, this, &MainWindow::on_horizontalSlider_actionTriggered);

    // 初始化模型
    model = new QStandardItemModel(this);
    ui->listView->setModel(model); // 设置 listView 的模型

    videoWidget->move(QPoint(10, 50));

    // 设置视频Widget的尺寸
    videoWidget->resize(280, 280);
}

MainWindow::~MainWindow()
{
    delete ui;
    // 这里确保删除player和videoWidget以避免内存泄露
    delete player;
    delete videoWidget;
}

void MainWindow::on_play_clicked()
{
   player->play();
}

void MainWindow::on_stop_clicked()
{
    player->pause();
}

void MainWindow::on_pushButton_clicked()
{
    
    // 使用QFileDialog获取文件路径列表
    QStringList filePaths = QFileDialog::getOpenFileNames(this, tr("选择视频文件"), ""/*, tr("MP4 Files (*.mp4);;All Files (*)")*/);
    
    // 检查是否选择了文件
    if (!filePaths.isEmpty()) {
        // 遍历所有选中的文件路径
        for (const QString &filePath : filePaths) {
            // 创建一个新的QStandardItem,包含文件名
            QStandardItem *item = new QStandardItem(QFileInfo(filePath).fileName());
            
            // 将该项添加到模型中,这里假设model是QStandardItemModel的实例
            model->appendRow(item); // 将视频文件名称添加到模型中
        }
        
        // 设置播放器的媒体内容为第一个文件的内容,准备播放
        player->setMedia(QMediaContent(QUrl::fromLocalFile(filePaths.first())));
    }

}

void MainWindow::on_close_clicked()
{
    player->stop();
}

void MainWindow::on_horizontalSlider_actionTriggered(int position)
{
    player->setPosition(position * 1000);
}


void MainWindow::on_listView_doubleClicked(const QModelIndex &index)
{

    if (index.isValid()) {
        QStandardItem *item = model->itemFromIndex(index);
        if (item) {
            // 获取列表中当前选中项的文件路径
            QString filePath =  "E:/lzy/MediaPlayer/Test/" + item->text();  //videoPath;
            // 检查文件是否存在
            if (QFile::exists(filePath)) {
                player->setMedia(QMediaContent(QUrl::fromLocalFile(filePath)));
                player->play();
            } else {
                qDebug() << "File does not exist: " << filePath;
            }
        }
    }
}


void MainWindow::updatePosition(qint64 position)
{ // 更新滑块显示
     ui->horizontalSlider->setMaximum(player->duration() / 1000);
    // ui->horizontalSlider->setValue(position / 1000);


    // 计算已观看时间和剩余时间
    int currentSeconds = position / 1000;
    int totalSeconds = player->duration() / 1000;
    int remainingSeconds = totalSeconds - currentSeconds;

    // 格式化时间
    QString currentTimeStr = formatTime(currentSeconds);
    QString remainingTimeStr = formatTime(remainingSeconds);
    QString totalSecondsStr = formatTime(totalSeconds);

    // 更新已观看时间和剩余时间的标签
    ui->labelCurrentTime->setText(currentTimeStr);
    ui->labelRemainingTime->setText(remainingTimeStr);
    ui->labeltotalTime->setText(totalSecondsStr);

}

QString MainWindow::formatTime(int seconds)
{
    int minutes = seconds / 60;
    int secs = seconds % 60;
    return QString("%1:%2").arg(minutes, 2, 10, QChar('0')).arg(secs, 2, 10, QChar('0'));
}


void MainWindow::labelstateChanged(QMediaPlayer::State state)
{
    switch (state) {
    case QMediaPlayer::StoppedState:
        ui->labelstate->setText(tr("停止状态!"));
        break;
    case QMediaPlayer::PlayingState:
        ui->labelstate->setText(tr("播放状态!"));
        break;
    case QMediaPlayer::PausedState:
        ui->labelstate->setText(tr("暂停状态!"));
        break;
    default: break;
    }
}

void MainWindow::on_previous_clicked()
{
    // 获取当前选中的索引
    QModelIndex currentIndex = ui->listView->currentIndex();
    // 检查当前索引是否有效
    if (currentIndex.isValid()) {
        // 获取当前选中项的上一项的索引
        int currentRow = currentIndex.row();
        int previousRow = (currentRow - 1 + model->rowCount()) % model->rowCount(); // 使用模运算确保索引循环
        QModelIndex previousIndex = model->index(previousRow, currentIndex.column());
        // 选择上一项
        ui->listView->setCurrentIndex(previousIndex);
        // 播放上一项
        QStandardItem *previousItem = model->itemFromIndex(previousIndex);
        if (previousItem) {
            // 设置媒体内容为上一项的视频路径
            QString filePath = "E:/lzy/MediaPlayer/Test/" + previousItem->text(); // 假设文件路径是这样设置的
            player->setMedia(QMediaContent(QUrl::fromLocalFile(filePath)));
            player->play();
        }
    } else {
        // 如果当前索引无效,可能是没有选中任何项目,可以选择最后一项
        QModelIndex lastIndex = model->index(model->rowCount() - 1, 0);
        ui->listView->setCurrentIndex(lastIndex);
        // 播放最后一项
        QStandardItem *lastItem = model->itemFromIndex(lastIndex);
        if (lastItem) {
            QString filePath = "E:/lzy/MediaPlayer/Test/" + lastItem->text(); // 假设文件路径是这样设置的
            player->setMedia(QMediaContent(QUrl::fromLocalFile(filePath)));
            player->play();
        }
    }
}

void MainWindow::on_next_clicked()
{
    // 获取当前选中的索引
    QModelIndex currentIndex = ui->listView->currentIndex();

    // 检查当前索引是否有效
    if (currentIndex.isValid()) {
        // 获取当前选中项的下一项的索引
        QModelIndex nextIndex = model->index(currentIndex.row() + 1, currentIndex.column());

        // 如果下一项索引无效,则跳转到最后一项
        if (!nextIndex.isValid()) {
            nextIndex = model->index(0, 0);
        }

        // 选择下一项
        ui->listView->setCurrentIndex(nextIndex);

        // 播放下一项
        QStandardItem *nextItem = model->itemFromIndex(nextIndex);
        if (nextItem) {
            // 设置媒体内容为下一项的视频路径
            QString filePath = "E:/lzy/MediaPlayer/Test/" + nextItem->text(); // 假设文件路径是这样设置的
            player->setMedia(QMediaContent(QUrl::fromLocalFile(filePath)));
            player->play();
        }
    } else {
        // 如果当前索引无效,可能是没有选中任何项目,可以选择第一项
        QModelIndex firstIndex = model->index(0, 0);
        ui->listView->setCurrentIndex(firstIndex);

        // 播放第一项
        QStandardItem *firstItem = model->itemFromIndex(firstIndex);
        if (firstItem) {
            QString filePath = "E:/lzy/MediaPlayer/Test/" + firstItem->text(); // 假设文件路径是这样设置的
            player->setMedia(QMediaContent(QUrl::fromLocalFile(filePath)));
            player->play();
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

并行程序设计基础——并行I/O(2)

目录 一、显式偏移的并行文件读写 1、阻塞方式 1.1 MPI_FILE_READ_AT 1.2 MPI_FILE_WRITE_AT 1.3 MPI_FILE_READ_AT_ALL 1.4 MPI_FILE_WRITE_AT_ALL 2、非阻塞方式 2.1 MPI_FILE_IREAD_AT 2.2 MPI_FILE_IWRITE_AT 3、两步非阻塞组调用 3.1 MPI_FILE_READ_AT_ALL_BEG…

基于SpringBoot的医院挂号预约管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的医院挂号…

港科夜闻 | 叶玉如校长出席2024科技+新质生产力高峰论坛发表专题演讲,贡献国家科技强国战略...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、叶玉如校长出席“2024科技新质生产力高峰论坛”&#xff0c;做了题为“三个创新&#xff1a;培育和发展新质生产力、贡献国家科技强国战略”的主题演讲。该论坛于9月2日在香港召开。论坛围绕夯实基础科研、推动源头创新、…

『功能项目』管理器基类【38】

我们打开上一篇37单例模式框架的项目&#xff0c; 本章要做的事情是编写管理器基类 首先创建脚本&#xff1a;ManagerBase.cs using UnityEngine; public abstract class ManagerBase : MonoBehaviour{public virtual void Init() { } } public class ManagerBase<T> : …

Docker容器技术1——docker基本操作

Docker容器技术 随着云计算和微服务架构的普及&#xff0c;容器技术成为了软件开发、测试和部署过程中的重要组成部分。其中&#xff0c;Docker作为容器技术的代表之一&#xff0c;以其简便易用的特点赢得了广大开发者的青睐。 Docker允许开发者在轻量级、可移植的容器中打包和…

JAVA——方法

public static 返回值类型 方法名(参数){//方法体return 数据; } 一、定义与调用 public class demo9_12 {public static void main(String[] args) {// 调用myName();}//定义public static void myName(){System.out.println("Hello World");} } 运行 二、含参数…

类和对象(c++)

欢迎来到本期频道&#xff01; 类和对象 类定义&#xff1a;格式&#xff1a;类域&#xff1a;访问限定符友元内部类this指针静态与非静态成员关系类型转换六大默认成员函数&#xff08;C98&#xff09;1️⃣构造函数2️⃣拷贝构造函数浅拷贝与深拷贝 3️⃣赋值重载拷贝函数4️…

产线工控安全之防勒索病毒杀手锏

在当今数字化时代&#xff0c;数据安全已成为企业运营中不可或缺的一部分。勒索病毒和内部泄密事件的频发&#xff0c;使得企业必须采取更为严格的安全措施来保护其关键数据和运营系统。苏州深信达网络科技推出的MCK主机加固解决方案&#xff0c;正是为了应对这些挑战而设计的。…

Excel图片批量插入单元格排版处理插件【图片大师】

为了方便大家在图片的插入排版的重复工作中解放出来&#xff0c;最近发布了一款批量插入图片的插件&#xff0c;欢迎大家下载&#xff0c;免费试用。 这是图片的文件夹&#xff1a; 主要功能如下: 1&#xff0c;匹配单元格名称的多张图批量插入到一个单元格 该功能支持设置图…

vivo手机已删除的短信还能恢复吗?

虽然现在我们很少使用vivo手机的短信功能&#xff0c;但是我们偶尔还会通过vivo手机短信功能接收一些重要的信息。如果我们在清理垃圾短信的时候误删了vivo手机重要短信&#xff0c;该怎么恢复呢&#xff1f; 方法一&#xff1a;通过vivo云服务恢复 1、确保您已开启vivo云服务…

IP包头的总长度字段和UDP包头的长度字段之间的关系

IP包头的总长度字段和UDP包头的长度字段之间的关系&#xff0c;并通过实例加以说明。 IP包头的总长度字段 **总长度&#xff08;Total Length&#xff09;**字段是一个16位的字段&#xff0c;表示整个IP数据包的总长度&#xff0c;包括IP包头和数据部分。单位是字节。由于该字…

实操了 AI 大模型项目落地, 程序员成功转变为 AI 大模型工程师

前言 根据《2024 年全球人工智能行业报告》最新的数据显示&#xff0c;全球 AI 市场预计将以每年超过 40% 的速度增长&#xff0c;到 2030 年市值将达到数万亿美元&#xff0c;这也是预示着在接下来的十年到十五年里&#xff0c;人工智能将获得巨大的发展红利。 在过去的一年…

SQL的高级查询练习知识点中(day25)

1 学习目标 重点掌握聚合函数的使用重点掌握字段别名重点掌握分组查询的语法重点掌握having的使用方法了解子查询的语法重点掌握排序查询语法 2 排序查询 2.1 语法 SELECT要查询的东西 FROM表 WHERE 条件 ORDER BY 排序的字段|表达式|函数|别名 [ASC|DESC] ASC 升序&#…

某市第二届全民国防知识竞赛流程规则方案

1.勇往直前 &#xff08;1&#xff09;答题规则&#xff1a;参赛队员按顺序作答 &#xff08;2&#xff09;答题方式&#xff1a;根据大屏幕显示的题库&#xff08;一道单选题、一道多选题、一道判断题&#xff09;&#xff0c;采用依托答题系统进行现场答题。 &#xff08;3&…

mac电脑打包太多xcode项目导致硬盘满了的解决方法

主要针对于在文件夹中删除了很多无用的xode打包项目&#xff0c;但是硬盘还是占用的方法 1.删除各种没用的xcode项目 2.设置-通用-储存空间设置-开发者&#xff08;右键&#xff09;&#xff0c;选择xcode项目构建和索引、xcode缓存。点击删除

从用户反馈看相亲交友平台的设计缺陷及改进方向

相亲交友平台在现代社会中扮演着越来越重要的角色&#xff0c;尤其是在数字化时代&#xff0c;它们通过智能匹配算法帮助用户找到潜在的伴侣&#xff0c;并提供了一系列功能来增强用户体验开发h17711347205。以下是对相亲交友平台用户体验的一些分析&#xff1a; 用户界面设计…

C语言深入理解指针六(19)

文章目录 前言一、sizeof & strlensizeofstrlensizeof和strlen的对比 二、数组和指针笔试题解析一维数组字符数组二维数组 三、指针运算笔试题解析题目1题目2题目3题目4题目5题目6题目7 总结 前言 本篇都将是练习题&#xff0c;从而让你对指针的理解更上一层楼 一、sizeof …

RP2040 C SDK ADC功能使用

RP2040 C SDK ADC功能使用 &#x1f33f;RP2040 ADC功能说明文档&#xff1a;https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#hardware_adc &#x1f4d7;RP2040 ADC介绍 SAR ADC500 kS/s (Using an independent 48MHz clock)12 bit (RP2040 8.7 ENOB, R…

网络工程师必知:高危端口大全,零基础入门到精通,收藏这一篇就够了

在计算机网络中&#xff0c;端口是主机与外界进行通信的通道。每个端口都可以看作是一条用于传输数据的虚拟线路。端口号是用于标识计算机网络服务的逻辑地址&#xff0c;它与IP地址共同构成了通信过程中的标识符。IP地址用于定位网络中的主机&#xff0c;而端口号则用于区分同…

如何通过食堂采购小程序端降低成本,提升效率?

随着数字化管理工具的普及&#xff0c;越来越多的食堂正在引入小程序来优化采购流程&#xff0c;减少成本和提升效率。食堂采购小程序端通过技术手段实现了自动化、智能化的管理方式&#xff0c;为管理者提供了极大的便利。本文将探讨如何利用技术手段开发一个高效的食堂采购小…