一、概述
最近在做一个Qt项目,涉及视频播放功能,怎么做都感觉差点意思,在刷抖音的时候发现这种九宫格布局看这挺舒服的于是就用qt写了一下,涉及一点ffmpeg的知识,不过不多(只是显示视频的第一帧做封面),想记录一下,于是就有了这个文章,希望能交流学下一哈......
二、项目结构
homepage:主页面,创建九宫格,协调videoitemwidget和videoplayerwindow
videoitemwidget:利用ffmpeg生成缩略图,以及其他视频信息展示
videoplayerwindow:播放视频(利用qt内置的api:Multimedia实现视频播放功能)
三、具体代码和注释
homepage.h
cpp
#ifndef HOMEPAGE_H
#define HOMEPAGE_H
#include <QWidget>
#include <QGridLayout>
#include <QScrollArea>
namespace Ui {
class homepage;
}
class homepage : public QWidget
{
Q_OBJECT
public:
explicit homepage(QWidget *parent = nullptr);
~homepage();
private slots:
void on_openButton_clicked();
void onVideoItemClicked();
private:
Ui::homepage *ui;
QGridLayout *gridLayout;
QWidget *gridContainer;
void clearPlaceholder();
void setupGridLayout();
};
#endif // HOMEPAGE_H
homepage.cpp
cpp
#include "homepage.h"
#include "ui_homepage.h"
#include "videoplayerwindow.h"
#include "videoitemwidget.h"
#include <QDir>
#include <QFileDialog>
#include <QLabel>
homepage::homepage(QWidget *parent)
: QWidget(parent)
, ui(new Ui::homepage)
, gridLayout(nullptr)//初始化网格布局
, gridContainer(nullptr)//初始化网格容器指针为空
{
ui->setupUi(this);
// 设置九宫格网格布局
setupGridLayout();
// 初始提示项
QLabel *placeholderLabel = new QLabel("点击「添加视频」按钮导入视频文件");
placeholderLabel->setAlignment(Qt::AlignCenter);
placeholderLabel->setStyleSheet(R"(
color: #7f8c8d;
font-size: 16px;
padding: 100px;
)");
gridLayout->addWidget(placeholderLabel, 0, 0, 1, 1);
}
homepage::~homepage()
{
delete ui;
}
void homepage::setupGridLayout()
{
// 创建网格容器
gridContainer = new QWidget();
//创建网格布局
gridLayout = new QGridLayout(gridContainer);
gridLayout->setSpacing(20);
gridLayout->setContentsMargins(20, 20, 20, 20);
gridLayout->setAlignment(Qt::AlignTop);
// 设置滚动区域
ui->scrollArea->setWidget(gridContainer);//将网络容器设置为滚动区域的空间
ui->scrollArea->setWidgetResizable(true);//允许滚动区域调整大小
ui->scrollArea->setStyleSheet("QScrollArea { border: none; background: transparent; }");
}
void homepage::clearPlaceholder()
{
// 清除初始提示
if (gridLayout->count() == 1) {
QLayoutItem *item = gridLayout->itemAt(0);
if (item && item->widget()) {
QLabel *label = qobject_cast<QLabel*>(item->widget());
if (label && label->text() == "点击「添加视频」按钮导入视频文件") {
delete item->widget();
}
}
}
}
void homepage::on_openButton_clicked()
{
const QString currentPath = QDir::currentPath();
const QString dlgTitle = "选择视频文件";
const QString filter = QStringLiteral("视频文件 (*.mp4 *.avi *.mkv *.mov *.wmv *.flv);;所有文件 (*.*)");
const QStringList filePaths = QFileDialog::getOpenFileNames(this, dlgTitle, currentPath, filter);
if (filePaths.isEmpty()) {
qDebug() << "未选择文件";
return;
}
// 清除初始提示
clearPlaceholder();
// 添加视频文件到网格布局
for (const QString &filePath : filePaths) {//遍历所有选择的文件路径
QFileInfo fileInfo(filePath);//获取文件信息
const QString fileName = fileInfo.fileName();//提取文件名
// 创建视频项控件
VideoItemWidget *videoItem = new VideoItemWidget(fileName, filePath);
// 连接点击信号
connect(videoItem, &VideoItemWidget::clicked, this, &homepage::onVideoItemClicked);
// 计算网格位置
int count = gridLayout->count();//当前网格项数量
int row = count / 3; // 计算行号每行3个
int col = count % 3;//计算列号
// 添加到网格布局
gridLayout->addWidget(videoItem, row, col);
qDebug() << "已添加视频:" << fileName;
}
}
//视频项点击槽函数:播放选中的视频
void homepage::onVideoItemClicked()
{
VideoItemWidget *videoItem = qobject_cast<VideoItemWidget*>(sender());//获取发送信号的视频项
if (!videoItem) return;
const QString filePath = videoItem->getFilePath();//获取视频路径
if (!filePath.isEmpty()) {
// 创建独立的播放窗口
VideoPlayerWindow *playerWindow = new VideoPlayerWindow(filePath, nullptr);
playerWindow->setWindowTitle("视频播放 - " + QFileInfo(filePath).fileName());
playerWindow->resize(800, 600);
// 设置窗口标志,使其完全独立
playerWindow->setWindowFlags(Qt::Window);
playerWindow->setAttribute(Qt::WA_DeleteOnClose);
//显示并激活窗口
playerWindow->show();
playerWindow->raise();//将窗口置于台前
playerWindow->activateWindow();//激活窗口
qDebug() << "正在播放视频:" << filePath;
}
}
videoitemwidget.h
cpp
#ifndef VIDEOITEMWIDGET_H
#define VIDEOITEMWIDGET_H
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QMouseEvent>
#include <QGraphicsDropShadowEffect>
#include <QEnterEvent>
#include <QPaintEvent>
#include <QFuture>
#include <QtConcurrent>
#include <QProcess>
class VideoItemWidget : public QWidget
{
Q_OBJECT
public:
explicit VideoItemWidget(const QString &fileName, const QString &filePath, QWidget *parent = nullptr);
~VideoItemWidget();
QString getFilePath() const { return filePath; }
signals:
void clicked();
void thumbnailReady(const QPixmap& pixmap);
protected:
void mousePressEvent(QMouseEvent *event) override;
void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
private slots:
void onThumbnailReady(const QPixmap& pixmap);
private:
void setupUI(const QString &fileName);
void loadVideoThumbnail();
void createDefaultThumbnail();
QPixmap generateThumbnailWithFFmpeg();
QString findFFmpegExecutable();
QString filePath;
QLabel *thumbnailLabel;
QLabel *titleLabel;
bool thumbnailLoaded;
};
#endif // VIDEOITEMWIDGET_H
videoitemwidget.cpp
cpp
#include "videoitemwidget.h"
#include <QPainter>
#include <QStyleOption>
#include <QImage>
#include <QPixmap>
#include <QFileInfo>
#include <QDir>
#include <QApplication>
#include <QStandardPaths>
#include <QProcess>
#include <QDebug>
//初始化视频项目小部件
VideoItemWidget::VideoItemWidget(const QString &fileName, const QString &filePath, QWidget *parent)
: QWidget(parent), filePath(filePath), thumbnailLoaded(false)
{
setupUI(fileName);//设置用户界面
loadVideoThumbnail();//加载视频缩略图
}
VideoItemWidget::~VideoItemWidget()
{
}
void VideoItemWidget::setupUI(const QString &fileName)
{
// 设置固定大小,适应九宫格布局
setFixedSize(240, 200);
// 设置样式
setStyleSheet(R"(
VideoItemWidget {
background-color: white;
border-radius: 8px;
border: 1px solid #e0e0e0;
}
VideoItemWidget:hover {
border: 2px solid #3498db;
background-color: #f8f9fa;
}
)");
// 创建主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(8, 8, 8, 8);
mainLayout->setSpacing(8);
// 创建缩略图标签
thumbnailLabel = new QLabel();
thumbnailLabel->setFixedSize(224, 140);//固定大小
thumbnailLabel->setAlignment(Qt::AlignCenter);//居中对齐
thumbnailLabel->setScaledContents(true);//缩放内容以适应标签
// 先创建默认缩略图
createDefaultThumbnail();
// 创建标题标签
titleLabel = new QLabel(fileName);
titleLabel->setStyleSheet(R"(
color: #2c3e50;
font-size: 14px;
font-weight: 500;
)");
titleLabel->setWordWrap(true);
titleLabel->setMaximumHeight(40);
titleLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
// 添加到布局
mainLayout->addWidget(thumbnailLabel);
mainLayout->addWidget(titleLabel);
// 设置光标形状
setCursor(Qt::PointingHandCursor);
// 连接缩略图准备信号
connect(this, &VideoItemWidget::thumbnailReady, this, &VideoItemWidget::onThumbnailReady);
}
void VideoItemWidget::loadVideoThumbnail()
{
// 在后台线程中使用FFmpeg生成缩略图
//QtConcurrent::run()会在单独的线程中执行给定的函数,并返回QFuture对象
QFuture<QPixmap> future = QtConcurrent::run([this]() {
return this->generateThumbnailWithFFmpeg();
});
// 使用QFutureWatcher来监视异步操作的完成状态
//QFutureWatcher是一个观察者类可以监视QFuture的状态变化
QFutureWatcher<QPixmap> *watcher = new QFutureWatcher<QPixmap>;
connect(watcher, &QFutureWatcher<QPixmap>::finished, this, [this, watcher]() {
QPixmap pixmap = watcher->result();
if (!pixmap.isNull()) {
emit thumbnailReady(pixmap);
}
watcher->deleteLater();//清理对象
});//当后台线程完成时会触发这个信号
watcher->setFuture(future);
}
QString VideoItemWidget::findFFmpegExecutable()//查找FFmpeg可执行文件
{
// 尝试在多个可能的位置查找 ffmpeg.exe
QStringList possiblePaths = {
"ffmpeg.exe", // 在系统 PATH 中
"ffmpeg", // 在系统 PATH 中(非Windows)
QApplication::applicationDirPath() + "/ffmpeg.exe",//应用程序目录
QApplication::applicationDirPath() + "/ffmpeg",
"D:/z_demo/ffmpeg-master-latest-win64-gpl-shared/ffmpeg-master-latest-win64-gpl-shared/bin/ffmpeg.exe"
};
//遍历所有可能的路径
for (const QString &path : possiblePaths) {
QFileInfo fileInfo(path);
if (fileInfo.exists() && fileInfo.isFile()) {
qDebug() << "找到 FFmpeg:" << path;
return path;
}
}
qDebug() << "未找到 FFmpeg 可执行文件";
return QString();
}
QPixmap VideoItemWidget::generateThumbnailWithFFmpeg()//创建缩略图
{
QString ffmpegPath = findFFmpegExecutable();//查找 ffmpeg.exe
if (ffmpegPath.isEmpty()) {
qDebug() << "无法找到 FFmpeg,使用默认缩略图";
return QPixmap();
}
// 创建临时文件路径
QString tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
QString thumbnailPath = tempDir + "/thumbnail_" +
QString::number(QDateTime::currentMSecsSinceEpoch()) +
"_" + QString::number(QRandomGenerator::global()->generate()) + ".jpg";
QProcess ffmpegProcess;
// 构建 FFmpeg 命令参数
QStringList arguments;
arguments << "-i" << filePath // 输入文件
<< "-ss" << "00:00:01" // 定位到第1秒
<< "-vframes" << "1" // 只取1帧
<< "-vf" << "scale=224:140:force_original_aspect_ratio=decrease" // 缩放
<< "-y" // 覆盖输出文件
<< thumbnailPath; // 输出文件
qDebug() << "执行 FFmpeg 命令:" << ffmpegPath << arguments;
// 启动 FFmpeg 进程
ffmpegProcess.start(ffmpegPath, arguments);
// 等待进程完成(最多10秒)
if (!ffmpegProcess.waitForFinished(10000)) {
qDebug() << "FFmpeg 处理超时:" << filePath;
ffmpegProcess.kill();
return QPixmap();
}
// 检查退出状态
if (ffmpegProcess.exitCode() != 0) {
QString errorOutput = ffmpegProcess.readAllStandardError();
qDebug() << "FFmpeg 错误:" << errorOutput;
return QPixmap();
}
// 检查输出文件是否存在
if (!QFile::exists(thumbnailPath)) {
qDebug() << "FFmpeg 未生成输出文件:" << thumbnailPath;
return QPixmap();
}
// 加载生成的缩略图
QPixmap result;
if (result.load(thumbnailPath)) {
qDebug() << "成功生成缩略图:" << filePath;
// 清理临时文件
QFile::remove(thumbnailPath);
return result;
} else {
qDebug() << "无法加载生成的缩略图:" << thumbnailPath;
// 清理临时文件
QFile::remove(thumbnailPath);
return QPixmap();
}
}
void VideoItemWidget::createDefaultThumbnail()
{
QPixmap thumbnail(224, 140);
// 根据文件扩展名选择不同的图标
QString extension = QFileInfo(filePath).suffix().toLower();
QString icon = "🎬"; // 默认图标
if (extension == "mp4") icon = "📹";
else if (extension == "avi") icon = "🎞️";
else if (extension == "mkv") icon = "📺";
else if (extension == "mov") icon = "🎥";
else if (extension == "wmv") icon = "📽️";
else if (extension == "flv") icon = "🔴";
// 创建渐变背景
QLinearGradient gradient(0, 0, 0, 140);
gradient.setColorAt(0, QColor(52, 152, 219));
gradient.setColorAt(1, QColor(41, 128, 185));
thumbnail.fill(Qt::transparent);
QPainter painter(&thumbnail);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制背景
painter.setBrush(gradient);
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(0, 0, 224, 140, 6, 6);
// 绘制图标和文字
painter.setPen(Qt::white);
painter.setFont(QFont("Arial", 24, QFont::Bold));
painter.drawText(QRect(0, 30, 224, 50), Qt::AlignCenter, icon);
painter.setFont(QFont("Arial", 10, QFont::Normal));
painter.drawText(QRect(0, 80, 224, 40), Qt::AlignCenter, "视频文件");
painter.drawText(QRect(0, 100, 224, 20), Qt::AlignCenter, extension.toUpper());
painter.end();
thumbnailLabel->setPixmap(thumbnail);
}
void VideoItemWidget::onThumbnailReady(const QPixmap& pixmap)
{
if (!pixmap.isNull()) {
thumbnailLabel->setPixmap(pixmap);
thumbnailLabel->setText("");
thumbnailLoaded = true;
qDebug() << "成功加载视频缩略图:" << QFileInfo(filePath).fileName();
}
}
void VideoItemWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
emit clicked();//发射点击信号
}
QWidget::mousePressEvent(event);//调用基类处理
}
void VideoItemWidget::enterEvent(QEnterEvent *event)//鼠标进入事件:添加阴影效果
{
QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect(this);
effect->setBlurRadius(20);
effect->setColor(QColor(0, 0, 0, 80));
effect->setOffset(0, 4);
setGraphicsEffect(effect);
QWidget::enterEvent(event);
}
void VideoItemWidget::leaveEvent(QEvent *event)
{
setGraphicsEffect(nullptr);//移除阴影效果
QWidget::leaveEvent(event);
}
void VideoItemWidget::paintEvent(QPaintEvent *event)
{
QStyleOption opt;
opt.initFrom(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
videoplayerwindow.h
cpp
#ifndef VIDEOPLAYERWINDOW_H
#define VIDEOPLAYERWINDOW_H
#include <QWidget>
#include <QMediaPlayer>
#include <QAudioOutput>
#include <QVideoWidget>
#include <QSlider>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QKeyEvent>
class VideoPlayerWindow : public QWidget
{
Q_OBJECT
public:
explicit VideoPlayerWindow(const QString &videoPath, QWidget *parent = nullptr);
~VideoPlayerWindow();
protected:
void keyPressEvent(QKeyEvent *event) override;
private slots:
void onPlaybackStateChanged(QMediaPlayer::PlaybackState state);
void onDurationChanged(qint64 duration);
void onPositionChanged(qint64 position);
void onPlayButtonClicked();
void onPauseButtonClicked();
void onMuteButtonClicked();
void onVolumeSliderValueChanged(int value);
void onProgressSliderMoved(int position);
void toggleFullscreen();
private:
void setupUI();
QMediaPlayer *player;
QAudioOutput *audioOutput;
QVideoWidget *videoWidget;
QPushButton *playButton;
QPushButton *pauseButton;
QPushButton *muteButton;
QPushButton *fullScreenButton;
QSlider *volumeSlider;
QSlider *progressSlider;
QLabel *timeLabel;
QLabel *titleLabel;
bool isMuted = false;
int previousVolume = 70;
bool isFullScreen = false;
QString videoFilePath;
};
#endif // VIDEOPLAYERWINDOW_H
videoplayerwindow.cpp
cpp
#include "videoplayerwindow.h"
#include <QFileInfo>
#include <QDebug>
VideoPlayerWindow::VideoPlayerWindow(const QString &videoPath, QWidget *parent)
: QWidget(parent), videoFilePath(videoPath)
{
setupUI();
// 初始化媒体播放器
player = new QMediaPlayer(this);
audioOutput = new QAudioOutput(this);
player->setAudioOutput(audioOutput);
player->setVideoOutput(videoWidget);
// 设置初始音量
audioOutput->setVolume(0.7);
// 连接信号槽
connect(player, &QMediaPlayer::playbackStateChanged, this, &VideoPlayerWindow::onPlaybackStateChanged);
connect(player, &QMediaPlayer::durationChanged, this, &VideoPlayerWindow::onDurationChanged);
connect(player, &QMediaPlayer::positionChanged, this, &VideoPlayerWindow::onPositionChanged);
// 设置视频文件
QUrl fileUrl = QUrl::fromLocalFile(videoPath);
player->setSource(fileUrl);
// 设置窗口标题
QFileInfo fileInfo(videoPath);
setWindowTitle(fileInfo.fileName());
titleLabel->setText(fileInfo.fileName());
qDebug() << "Video player window created for:" << videoPath;
}
VideoPlayerWindow::~VideoPlayerWindow()
{
}
void VideoPlayerWindow::setupUI()
{
setWindowTitle("视频播放器");
setMinimumSize(800, 600);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
// 视频控件
videoWidget = new QVideoWidget(this);
videoWidget->setMinimumSize(640, 360);
videoWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mainLayout->addWidget(videoWidget,1);
// 控制面板
QWidget *controlPanel = new QWidget(this);
controlPanel->setStyleSheet("background-color: #2c3e50;");
controlPanel->setMaximumHeight(60);
QHBoxLayout *controlLayout = new QHBoxLayout(controlPanel);
controlLayout->setContentsMargins(10, 5, 10, 5);
controlLayout->setSpacing(10);
// 文件名标签
titleLabel = new QLabel("视频播放器");
titleLabel->setStyleSheet("color: white; font-size: 14px; font-weight: bold;");
titleLabel->setMaximumWidth(200);
titleLabel->setWordWrap(true);
// 播放控制按钮
playButton = new QPushButton("播放");
pauseButton = new QPushButton("暂停");
muteButton = new QPushButton("🔊");
fullScreenButton = new QPushButton("全屏");
QString buttonStyle = "QPushButton {"
"background-color: #3498db;"
"color: white;"
"border: none;"
"padding: 8px 12px;"
"border-radius: 4px;"
"font-size: 12px;"
"}"
"QPushButton:hover {"
"background-color: #2980b9;"
"}"
"QPushButton:disabled {"
"background-color: #7f8c8d;"
"}";
playButton->setStyleSheet(buttonStyle);
pauseButton->setStyleSheet(buttonStyle);
muteButton->setStyleSheet(buttonStyle);
fullScreenButton->setStyleSheet(buttonStyle);
// 音量滑块
volumeSlider = new QSlider(Qt::Horizontal);
volumeSlider->setRange(0, 100);
volumeSlider->setValue(70);
volumeSlider->setMaximumWidth(100);
volumeSlider->setStyleSheet(
"QSlider::groove:horizontal {"
"border: 1px solid #999999;"
"height: 4px;"
"background: #34495e;"
"border-radius: 2px;"
"}"
"QSlider::handle:horizontal {"
"background: #e74c3c;"
"border: 1px solid #e74c3c;"
"width: 10px;"
"margin: -3px 0;"
"border-radius: 5px;"
"}"
"QSlider::sub-page:horizontal {"
"background: #e74c3c;"
"border-radius: 2px;"
"}");
// 进度条
progressSlider = new QSlider(Qt::Horizontal);
progressSlider->setStyleSheet(
"QSlider::groove:horizontal {"
"border: 1px solid #999999;"
"height: 6px;"
"background: #34495e;"
"border-radius: 3px;"
"}"
"QSlider::handle:horizontal {"
"background: #3498db;"
"border: 1px solid #3498db;"
"width: 12px;"
"margin: -5px 0;"
"border-radius: 6px;"
"}"
"QSlider::sub-page:horizontal {"
"background: #3498db;"
"border-radius: 3px;"
"}");
// 时间标签
timeLabel = new QLabel("00:00 / 00:00");
timeLabel->setStyleSheet("color: white; font-size: 12px;");
// 添加到控制面板
controlLayout->addWidget(titleLabel);
controlLayout->addWidget(playButton);
controlLayout->addWidget(pauseButton);
controlLayout->addWidget(muteButton);
controlLayout->addWidget(volumeSlider);
controlLayout->addWidget(progressSlider, 1); // 给进度条更多空间
controlLayout->addWidget(timeLabel);
controlLayout->addWidget(fullScreenButton);
mainLayout->addWidget(controlPanel);
// 连接按钮信号
connect(playButton, &QPushButton::clicked, this, &VideoPlayerWindow::onPlayButtonClicked);
connect(pauseButton, &QPushButton::clicked, this, &VideoPlayerWindow::onPauseButtonClicked);
connect(muteButton, &QPushButton::clicked, this, &VideoPlayerWindow::onMuteButtonClicked);
connect(fullScreenButton, &QPushButton::clicked, this, &VideoPlayerWindow::toggleFullscreen);
connect(volumeSlider, &QSlider::valueChanged, this, &VideoPlayerWindow::onVolumeSliderValueChanged);
connect(progressSlider, &QSlider::sliderMoved, this, &VideoPlayerWindow::onProgressSliderMoved);
// 初始按钮状态
playButton->setEnabled(true);
pauseButton->setEnabled(false);
}
void VideoPlayerWindow::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape && isFullScreen) {
toggleFullscreen();
event->accept();
}
else if(event->key() == Qt::Key_F11)
{
toggleFullscreen();
event->accept();
}
else if (event->key() == Qt::Key_Space) {
if (player->playbackState() == QMediaPlayer::PlayingState) {
onPauseButtonClicked();
} else {
onPlayButtonClicked();
}
event->accept();
} else {
QWidget::keyPressEvent(event);
}
}
void VideoPlayerWindow::onPlaybackStateChanged(QMediaPlayer::PlaybackState state)
{
switch (state) {
case QMediaPlayer::StoppedState:
playButton->setEnabled(true);
pauseButton->setEnabled(false);
break;
case QMediaPlayer::PlayingState:
playButton->setEnabled(false);
pauseButton->setEnabled(true);
break;
case QMediaPlayer::PausedState:
playButton->setEnabled(true);
pauseButton->setEnabled(false);
break;
default:
break;
}
}
void VideoPlayerWindow::onDurationChanged(qint64 duration)
{
progressSlider->setRange(0, duration);
int sec = duration / 1000;
int min = sec / 60;
sec = sec % 60;
int hour = min / 60;
min = min % 60;
QString totalTime;
if (hour > 0) {
totalTime = QString::asprintf("%02d:%02d:%02d", hour, min, sec);
} else {
totalTime = QString::asprintf("%02d:%02d", min, sec);
}
QString currentTime = timeLabel->text().split(" / ").first();
timeLabel->setText(currentTime + " / " + totalTime);
}
void VideoPlayerWindow::onPositionChanged(qint64 position)
{
if (!progressSlider->isSliderDown()) {
progressSlider->setValue(position);
}
int sec = position / 1000;
int min = sec / 60;
sec = sec % 60;
int hour = min / 60;
min = min % 60;
QString currentTime;
if (hour > 0) {
currentTime = QString::asprintf("%02d:%02d:%02d", hour, min, sec);
} else {
currentTime = QString::asprintf("%02d:%02d", min, sec);
}
QString totalTime = timeLabel->text().split(" / ").last();
timeLabel->setText(currentTime + " / " + totalTime);
}
void VideoPlayerWindow::onPlayButtonClicked()
{
player->play();
}
void VideoPlayerWindow::onPauseButtonClicked()
{
player->pause();
}
void VideoPlayerWindow::onMuteButtonClicked()
{
isMuted = !isMuted;
audioOutput->setMuted(isMuted);
if (isMuted) {
muteButton->setText("🔇");
previousVolume = volumeSlider->value();
volumeSlider->setValue(0);
} else {
muteButton->setText("🔊");
volumeSlider->setValue(previousVolume);
}
}
void VideoPlayerWindow::onVolumeSliderValueChanged(int value)
{
float vol = value / 100.0f;
audioOutput->setVolume(vol);
if (value == 0) {
muteButton->setText("🔇");
isMuted = true;
audioOutput->setMuted(true);
} else {
muteButton->setText("🔊");
isMuted = false;
audioOutput->setMuted(false);
}
}
void VideoPlayerWindow::onProgressSliderMoved(int position)
{
player->setPosition(position);
}
void VideoPlayerWindow::toggleFullscreen()
{
if (!isFullScreen) {
// 改为设置整个窗口全屏,而不是只设置videoWidget
showFullScreen();
fullScreenButton->setText("退出全屏");
isFullScreen = true;
} else {
showNormal(); // 退出全屏,恢复正常窗口
fullScreenButton->setText("全屏");
isFullScreen = false;
}
}
四、效果
九宫格效果
播放效果:

