QT:音视频播放器

news2024/11/25 7:13:00

目录

一.播放器设计

二.需要使用的控件

三.选择视频

四.播放视频

五.暂停视频

六.关闭视频

七.播放状态设置

八.切换视频(上一首)

九.切换视频(下一首)

十.设置视频滑块

十一.更新滑块显示

十二.实现效果

十三.代码设计

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.coloradmin.cn/o/2127399.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国产视频转换HDMI1.4转单/双MIPI DSI/CSI LT6911C芯片方案,带音频输出,QFN64封装 Lontium

LT6911C:HDMI 1.4 TO MIPI DSI/CSI 芯片简介&#xff1a; LT6911C是一款高性能的HDMI1.4转换器MIPI DSI/CSI芯片用于VR/智能手机/显示应用。对于MIPI DSI/CSI输出&#xff0c;LT6911C功能可配置单端口或双端口MIPIDSI/CSI 1高速时钟通道和1~4个高速数据通道最大1.5Gb/s/lane&am…

SSHamble:一款针对SSH技术安全的研究与分析工具

关于SSHamble SSHamble是一款功能强大的SSH技术安全分析与研究工具&#xff0c;该工具基于Go语言开发&#xff0c;可以帮助广大研究人员更好地分析SSH相关的安全技术与缺陷问题。 功能介绍 SSHamble 是用于 SSH 实现的研究工具&#xff0c;其中包含下列功能&#xff1a; 1、针…

【算法思想·二叉搜索树】特性篇

本文参考labuladong算法笔记[二叉搜索树心法&#xff08;特性篇&#xff09; | labuladong 的算法笔记] 1、概述 首先&#xff0c;BST&#xff08;binary search tree&#xff09; 的特性大家应该都很熟悉了&#xff08;详见基础知识章节的 二叉树基础&#xff09;&#xff1a…

【QGC】把QGroundControl地面站添加到Ubuntu侧边菜单栏启动

把QGroundControl地面站添加到Ubuntu侧边菜单栏启动 简介准备工作步骤 1: 创建 Desktop Entry 文件步骤 2: 编辑 Desktop Entry 文件步骤 3: 刷新应用程序菜单步骤 4: 将 QGroundControl 固定到侧边栏 环境&#xff1a; Ubuntu &#xff1a;20.04 LTS 简介 QGroundControl 是…

[综述笔记]Federated learning for medical image analysis: A survey

论文网址&#xff1a;Federated learning for medical image analysis: A survey - ScienceDirect 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&…

C++ Primer Plus(速记版)-基本语言

序章 快速入门 初窥输入/输出 C 并没有直接定义进行输入或输出(I/O)的任何语句&#xff0c;这种功能是由标准库提供的。 本书的大多数例子都使用了处理格式化输入和输出的 iostream 库。 iostream 库的基础是两种命名为 istream 和 ostream 的类型&#xff0c;分别表示输入流和…

卷积神经网络-经典分类网络结构(LetNet-5,AlexNet)

目录 一&#xff1a;LeNet-5解析 1.网络结构 输入层&#xff1a; 1.conv1&#xff1a; 2.pool1层&#xff1a; 3.conv2&#xff1a; 4.pool2&#xff1a; 5.fc3&#xff0c;fc4&#xff1a; 6.output层: 2.参数形状 二&#xff1a;AlexNet 1层&#xff1a; 2层&am…

招生管理|基于Java+vue的招生管理系统(源码+数据库+文档)

招生管理|学生管理系统|高校招生管理 目录 基于Javavue的招生管理系统 一、前言 二、系统设计 三、系统功能设计 系统功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|…

什么是OAuth 2.0?OAuth 2.0的工作流程是什么?与OAuth 1.0有哪些区别?

在浏览网页时&#xff0c;你肯定会遇到允许你使用社交媒体账户登录的网站。此功能一般是使用流行的OAuth 2.0框架构建的。OAuth 2.0是对OAuth 1.0的彻底重写&#xff0c;OAuth 2.0与OAuth 1.0或1.1不向后兼容。 1. OAuth产生背景 为了更好的理解OAuth&#xff0c;我们假设有如…

CAN总线-STM32上CAN外设

1.STM32 CAN外设简介 2.CAN网拓扑结构 3.CAN收发器电路 4.CAN框图 5.CAN基本结构 6.发送过程 7.接收过程 8.发送和接收配置位 9.标识过滤器&#xff08;重点&#xff09; 这里的FBMX模式设置的列表模式&#xff1a;你在列表中输入你想要的报文ID&#xff0c;不在你列表中的ID屏…

css grid布局属性详解

Grid布局 前言一、认识Grid1.1容器和项目1.2行和列1.3单元格和网格线 二、容器属性2.1.grid-template-columns与grid-template-rows属性2.1.1 直接使用长度单位比如px2.1.2 使用百分比 %2.1.3 使用repeat函数2.1.4 按比例划分 fr 关键字2.1.5 自动填充 auto 关键字2.1.6 最大值…

c4d的重命名工具(支持模型和材质) 及 python窗口定义

不是我牛逼&#xff0c;是豆包牛逼&#xff01; 一个简化版的窗口 import c4d from c4d import guiclass MyDialog(gui.GeDialog):def __init__(self):super().__init__()self.SetTitle("My Dialog")def CreateLayout(self):# 设置对话框布局return Truemy_dialog …

C语言补习课番外篇——采样sin(x)

需求&#xff1a;让stm32的DAC输出正弦波形 分析&#xff1a;DAC的原理这里不作过多介绍。在[0.2π]的定义域内对sin(x)的值域进行采样&#xff0c;采样次数为256次&#xff1b;采样结果需要等比例缩放到0~4095的无符号数范围内&#xff0c;并且输出到一个SinFile.txt文本文档…

无敌C++大王养成篇一

1.命名空间 namespace c语⾔项⽬类似下⾯程序这样的命名冲突是普遍存在的问题&#xff0c;C引⼊namespace就是为了更好的解决 这样的问题 #include<stdio.h> //#include<stdlib.h>int rand 10;int main() {printf("%d\n",rand); }//运行时编译没有…

Grafana 可视化配置

Grafana 是什么 Grafana 是一个开源的可视化和监控工具&#xff0c;广泛用于查看和分析来自各种数据源的时间序列数据。它提供了一个灵活的仪表盘&#xff08;dashboard&#xff09;界面&#xff0c;用户可以通过它将数据源中的指标进行图表化展示和监控&#xff0c;帮助分析趋…

语音转文字工具全解析

无论是学生群体记录课堂笔记&#xff0c;职场人士整理会议纪要&#xff0c;还是自媒体创作者捕捉灵感火花&#xff0c;录音转文字软件都以其独特的便利性和高效性赢得了广泛的好评。今天&#xff0c;就让我们一起探索那些深受大家喜爱的录音转文字工具吧。 1.365在线转文字 链…

C++ | Leetcode C++题解之第397题整数替换

题目&#xff1a; 题解&#xff1a; class Solution { public:int integerReplacement(int n) {int ans 0;while (n ! 1) {if (n % 2 0) {ans;n / 2;}else if (n % 4 1) {ans 2;n / 2;}else {if (n 3) {ans 2;n 1;}else {ans 2;n n / 2 1;}}}return ans;} };

Window 本地启动Nacos

前言 本文帮助大家快速windows环境本地启动naco&#xff08;以版本2.2.3为例&#xff09; 进一步深入学习nacos推荐我的另外一篇文章&#xff1a; springCloud组件专题&#xff08;一&#xff09; --- Nacos_springcloud中的nacos如何使用-CSDN博客 ** 在本地启动nacos之前&…

C:字符函数与字符串函数-学习笔记

目录 1、字符分类函数 2、字符转换函数 3、字符串函数 4、strlen 函数的使用与模拟实现 4.1 strlen函数的使用 4.2 strlen函数的模拟实现 1、字符分类函数 C语言中有一系列的函数是专门做字符分类的&#xff0c;也就是一个字符是属于什么类型的字符的。 这些函数的使用都…

Vue(10)——自定义指令

自定义指令 自定义指令&#xff1a;可以封装一些dom操作&#xff0c;扩展额外功能。 全局注册-语法&#xff1a; Vue.directive(指令名,{ "inserted"(el){ inserted指指令所绑定的元素被添加到页面时自动调用 //可以对el标签扩展额外功能 el.focus() } }) 局部…