文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT Widget 开发(一)工程文件结构-优快云博客
开源 C++ QT Widget 开发(二)基本控件应用-优快云博客
开源 C++ QT Widget 开发(三)图表--波形显示器-优快云博客
开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-优快云博客
开源 C++ QT Widget 开发(五)通讯--串口调试-优快云博客
开源 C++ QT Widget 开发(六)通讯--TCP调试-优快云博客
开源 C++ QT Widget 开发(七)线程--多线程及通讯-优快云博客
开源 C++ QT Widget 开发(八)网络--Http文件下载-优快云博客
开源 C++ QT Widget 开发(九)图表--仪表盘-优快云博客
开源 C++ QT Widget 开发(十)IPC进程间通信--共享内存-优快云博客
开源 C++ QT Widget 开发(十一)进程间通信--Windows 窗口通信-优快云博客
开源 C++ QT Widget 开发(十二)图表--环境监测表盘-优快云博客
开源 C++ QT Widget 开发(十三)IPC通讯--本地套接字 (Local Socket)
开源 C++ QT Widget 开发(十四)多媒体--录音机
开源 C++ QT Widget 开发(十五)多媒体--音频播放
推荐链接:
开源 java android app 开发(一)开发环境的搭建-优快云博客
开源 java android app 开发(二)工程文件结构-优快云博客
开源 java android app 开发(三)GUI界面布局和常用组件-优快云博客
开源 java android app 开发(四)GUI界面重要组件-优快云博客
开源 java android app 开发(五)文件和数据库存储-优快云博客
开源 java android app 开发(六)多媒体使用-优快云博客
开源 java android app 开发(七)通讯之Tcp和Http-优快云博客
开源 java android app 开发(八)通讯之Mqtt和Ble-优快云博客
开源 java android app 开发(九)后台之线程和服务-优快云博客
开源 java android app 开发(十)广播机制-优快云博客
开源 java android app 开发(十一)调试、发布-优快云博客
开源 java android app 开发(十二)封库.aar-优快云博客
推荐链接:
开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-优快云博客
开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-优快云博客
开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-优快云博客
开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-优快云博客
开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-优快云博客
内容:Qt实现的WAV音频播放器应用程序。实现win10系统下,播放wav音频的功能。
目录:
1.功能介绍
2.核心代码分析
3.所有源码
4.显示效果
一.功能介绍
这是一个基于QMediaPlayer的音频播放器,主要功能包括:
音频播放控制:播放、停止、进度控制
文件检查:验证音频文件的存在和有效性
状态监控:实时显示播放状态和进度
错误处理:完善的错误检测和处理机制
日志系统:详细的运行日志记录
二、核心代码分析
头文件 (mainwindow.h)
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_playButton_clicked(); // 播放按钮点击
void updatePlaybackProgress(); // 更新播放进度
void handlePlayerStateChanged(QMediaPlayer::State state); // 播放状态变化
void handlePlayerError(QMediaPlayer::Error error); // 错误处理
private:
Ui::MainWindow *ui;
QMediaPlayer *player; // 媒体播放器核心
QTimer *progressTimer; // 进度更新计时器
QString audioFilePath; // 音频文件路径
void logMessage(const QString &message); // 日志记录
bool checkAudioFile(); // 文件检查
};
函数功能详细分析
1. 构造函数 MainWindow::MainWindow()
功能:初始化播放器界面和组件
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, player(new QMediaPlayer(this)) // 创建媒体播放器
, progressTimer(new QTimer(this)) // 创建进度计时器
{
ui->setupUi(this);
setWindowTitle("16K WAV音频播放器 - Qt5.14"); // 设置窗口标题
audioFilePath = QDir::currentPath() + "/Output.wav"; // 设置默认文件路径
ui->filePathLabel->setText("文件: " + audioFilePath); // 显示文件路径
player->setVolume(80); // 设置默认音量(80%)
// 信号槽连接
connect(player, SIGNAL(stateChanged(QMediaPlayer::State)),
this, SLOT(handlePlayerStateChanged(QMediaPlayer::State)));
connect(player, SIGNAL(error(QMediaPlayer::Error)),
this, SLOT(handlePlayerError(QMediaPlayer::Error)));
connect(progressTimer, &QTimer::timeout, this, &MainWindow::updatePlaybackProgress);
// UI初始化
ui->playButton->setText("开始播放");
ui->statusLabel->setText("就绪");
ui->progressBar->setValue(0);
// 文件检查
if (checkAudioFile()) {
logMessage("音频文件检查正常,准备就绪");
} else {
ui->playButton->setEnabled(false); // 文件无效时禁用播放按钮
}
}
2. 析构函数 MainWindow::~MainWindow()
功能:安全清理资源
MainWindow::~MainWindow()
{
if (player->state() == QMediaPlayer::PlayingState) {
player->stop(); // 如果正在播放,先停止
}
delete ui;
}
3. checkAudioFile() - 音频文件检查
功能:验证音频文件的存在和有效性
bool MainWindow::checkAudioFile()
{
QFileInfo fileInfo(audioFilePath);
if (!fileInfo.exists()) {
logMessage("错误:音频文件不存在 - " + audioFilePath);
ui->statusLabel->setText("错误:文件不存在");
return false;
}
if (fileInfo.size() == 0) {
logMessage("警告:音频文件为空");
ui->statusLabel->setText("警告:文件为空");
return false;
}
logMessage(QString("找到音频文件:%1 (%2 字节)")
.arg(audioFilePath)
.arg(fileInfo.size()));
return true;
}
4. on_playButton_clicked() - 播放控制
功能:处理播放/停止按钮点击事件
void MainWindow::on_playButton_clicked()
{
if (player->state() == QMediaPlayer::PlayingState) {
// 停止播放
logMessage("用户停止播放");
player->stop();
progressTimer->stop();
ui->playButton->setText("开始播放");
} else {
// 开始播放
if (!checkAudioFile()) {
logMessage("播放失败:音频文件不可用");
return;
}
logMessage("开始播放音频文件...");
player->setMedia(QUrl::fromLocalFile(audioFilePath)); // 设置媒体源
if (player->mediaStatus() == QMediaPlayer::InvalidMedia) {
logMessage("错误:无法加载媒体文件");
ui->statusLabel->setText("错误:无效的媒体文件");
return;
}
player->play(); // 开始播放
progressTimer->start(100); // 启动进度更新计时器(100ms间隔)
logMessage("播放器启动完成");
}
}
5. updatePlaybackProgress() - 进度更新
功能:实时更新播放进度显示
void MainWindow::updatePlaybackProgress()
{
if (player->duration() > 0) {
qint64 position = player->position(); // 当前位置(毫秒)
qint64 duration = player->duration(); // 总时长(毫秒)
int seconds = position / 1000; // 转换为秒
int totalSeconds = duration / 1000;
// 格式化时间显示 (MM:SS / MM:SS)
ui->timeLabel->setText(
QString("%1:%2 / %3:%4")
.arg(seconds / 60, 2, 10, QLatin1Char('0')) // 当前分钟
.arg(seconds % 60, 2, 10, QLatin1Char('0')) // 当前秒
.arg(totalSeconds / 60, 2, 10, QLatin1Char('0')) // 总分钟
.arg(totalSeconds % 60, 2, 10, QLatin1Char('0')) // 总秒
);
// 计算进度百分比
int progress = (duration > 0) ? static_cast<int>(position * 100 / duration) : 0;
ui->progressBar->setValue(progress);
// 每秒记录一次进度日志(避免过于频繁)
static int lastSecond = -1;
if (seconds != lastSecond) {
lastSecond = seconds;
logMessage(QString("播放进度: %1% (%2/%3 秒)")
.arg(progress)
.arg(seconds)
.arg(totalSeconds));
}
}
}
6. handlePlayerStateChanged() - 状态变化处理
功能:响应播放器状态变化
void MainWindow::handlePlayerStateChanged(QMediaPlayer::State state)
{
switch (state) {
case QMediaPlayer::StoppedState:
logMessage("播放停止");
ui->statusLabel->setText("播放完成");
ui->playButton->setText("开始播放");
progressTimer->stop(); // 停止进度更新
ui->progressBar->setValue(100); // 进度条置满
break;
case QMediaPlayer::PlayingState:
logMessage("播放进行中...");
ui->statusLabel->setText("正在播放");
ui->playButton->setText("停止播放");
logMessage(QString("媒体时长: %1 毫秒").arg(player->duration()));
break;
case QMediaPlayer::PausedState:
logMessage("播放暂停");
ui->statusLabel->setText("播放暂停");
ui->playButton->setText("继续播放");
break;
}
}
7. handlePlayerError() - 错误处理
功能:处理播放器错误
void MainWindow::handlePlayerError(QMediaPlayer::Error error)
{
QString errorMsg;
switch (error) {
case QMediaPlayer::NoError:
return;
case QMediaPlayer::ResourceError:
errorMsg = "资源错误:无法访问媒体文件";
break;
case QMediaPlayer::FormatError:
errorMsg = "格式错误:不支持的媒体格式"; // WAV格式可能不支持
break;
case QMediaPlayer::NetworkError:
errorMsg = "网络错误";
break;
case QMediaPlayer::AccessDeniedError:
errorMsg = "访问被拒绝:没有足够的权限";
break;
default:
errorMsg = "未知错误";
break;
}
logMessage(errorMsg);
ui->statusLabel->setText("播放错误");
ui->playButton->setText("开始播放");
QMessageBox::warning(this, "播放错误", errorMsg); // 弹出错误对话框
}
8. logMessage() - 日志记录
功能:统一的日志记录系统
void MainWindow::logMessage(const QString &message)
{
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);
ui->logTextEdit->append(logEntry); // 添加到UI日志框
qDebug() << logEntry; // 输出到控制台
// 自动滚动到日志底部
QTextCursor cursor = ui->logTextEdit->textCursor();
cursor.movePosition(QTextCursor::End);
ui->logTextEdit->setTextCursor(cursor);
}
三、所有源码
1. pro文件
QT += core gui
QT += core gui multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
2. mainwindow.h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMediaPlayer>
#include <QTimer>
#include <QFileInfo>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_playButton_clicked();
void updatePlaybackProgress();
void handlePlayerStateChanged(QMediaPlayer::State state);
void handlePlayerError(QMediaPlayer::Error error);
private:
Ui::MainWindow *ui;
QMediaPlayer *player;
QTimer *progressTimer;
QString audioFilePath;
void logMessage(const QString &message);
bool checkAudioFile();
};
#endif // MAINWINDOW_H
3.mianwindow.cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDir>
#include <QDateTime>
#include <QFileInfo>
#include <QDebug>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, player(new QMediaPlayer(this))
, progressTimer(new QTimer(this))
{
ui->setupUi(this);
// 设置窗口标题
setWindowTitle("16K WAV音频播放器 - Qt5.14");
// 设置音频文件路径
audioFilePath = QDir::currentPath() + "/Output.wav";
ui->filePathLabel->setText("文件: " + audioFilePath);
// 配置播放器
player->setVolume(80); // 设置音量 0-100
// 连接信号槽
connect(player, SIGNAL(stateChanged(QMediaPlayer::State)),
this, SLOT(handlePlayerStateChanged(QMediaPlayer::State)));
connect(player, SIGNAL(error(QMediaPlayer::Error)),
this, SLOT(handlePlayerError(QMediaPlayer::Error)));
connect(progressTimer, &QTimer::timeout, this, &MainWindow::updatePlaybackProgress);
// 初始化UI状态
ui->playButton->setText("开始播放");
ui->statusLabel->setText("就绪");
ui->progressBar->setValue(0);
// 检查音频文件
if (checkAudioFile()) {
logMessage("音频文件检查正常,准备就绪");
} else {
ui->playButton->setEnabled(false);
}
}
MainWindow::~MainWindow()
{
if (player->state() == QMediaPlayer::PlayingState) {
player->stop();
}
delete ui;
}
bool MainWindow::checkAudioFile()
{
QFileInfo fileInfo(audioFilePath);
if (!fileInfo.exists()) {
logMessage("错误:音频文件不存在 - " + audioFilePath);
ui->statusLabel->setText("错误:文件不存在");
return false;
}
if (fileInfo.size() == 0) {
logMessage("警告:音频文件为空");
ui->statusLabel->setText("警告:文件为空");
return false;
}
logMessage(QString("找到音频文件:%1 (%2 字节)")
.arg(audioFilePath)
.arg(fileInfo.size()));
return true;
}
void MainWindow::on_playButton_clicked()
{
if (player->state() == QMediaPlayer::PlayingState) {
// 停止播放
logMessage("用户停止播放");
player->stop();
progressTimer->stop();
ui->playButton->setText("开始播放");
} else {
// 开始播放
if (!checkAudioFile()) {
logMessage("播放失败:音频文件不可用");
return;
}
logMessage("开始播放音频文件...");
player->setMedia(QUrl::fromLocalFile(audioFilePath));
if (player->mediaStatus() == QMediaPlayer::InvalidMedia) {
logMessage("错误:无法加载媒体文件");
ui->statusLabel->setText("错误:无效的媒体文件");
return;
}
player->play();
// 启动进度更新计时器
progressTimer->start(100);
logMessage("播放器启动完成");
}
}
void MainWindow::updatePlaybackProgress()
{
if (player->duration() > 0) {
qint64 position = player->position();
qint64 duration = player->duration();
int seconds = position / 1000;
int totalSeconds = duration / 1000;
// 更新时间显示
ui->timeLabel->setText(
QString("%1:%2 / %3:%4")
.arg(seconds / 60, 2, 10, QLatin1Char('0'))
.arg(seconds % 60, 2, 10, QLatin1Char('0'))
.arg(totalSeconds / 60, 2, 10, QLatin1Char('0'))
.arg(totalSeconds % 60, 2, 10, QLatin1Char('0'))
);
// 更新进度条
int progress = (duration > 0) ? static_cast<int>(position * 100 / duration) : 0;
ui->progressBar->setValue(progress);
// 记录播放进度
static int lastSecond = -1;
if (seconds != lastSecond) {
lastSecond = seconds;
logMessage(QString("播放进度: %1% (%2/%3 秒)")
.arg(progress)
.arg(seconds)
.arg(totalSeconds));
}
}
}
void MainWindow::handlePlayerStateChanged(QMediaPlayer::State state)
{
switch (state) {
case QMediaPlayer::StoppedState:
logMessage("播放停止");
ui->statusLabel->setText("播放完成");
ui->playButton->setText("开始播放");
progressTimer->stop();
ui->progressBar->setValue(100);
break;
case QMediaPlayer::PlayingState:
logMessage("播放进行中...");
ui->statusLabel->setText("正在播放");
ui->playButton->setText("停止播放");
logMessage(QString("媒体时长: %1 毫秒").arg(player->duration()));
break;
case QMediaPlayer::PausedState:
logMessage("播放暂停");
ui->statusLabel->setText("播放暂停");
ui->playButton->setText("继续播放");
break;
}
}
void MainWindow::handlePlayerError(QMediaPlayer::Error error)
{
QString errorMsg;
switch (error) {
case QMediaPlayer::NoError:
return;
case QMediaPlayer::ResourceError:
errorMsg = "资源错误:无法访问媒体文件";
break;
case QMediaPlayer::FormatError:
errorMsg = "格式错误:不支持的媒体格式";
break;
case QMediaPlayer::NetworkError:
errorMsg = "网络错误";
break;
case QMediaPlayer::AccessDeniedError:
errorMsg = "访问被拒绝:没有足够的权限";
break;
default:
errorMsg = "未知错误";
break;
}
logMessage(errorMsg);
ui->statusLabel->setText("播放错误");
ui->playButton->setText("开始播放");
QMessageBox::warning(this, "播放错误", errorMsg);
}
void MainWindow::logMessage(const QString &message)
{
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);
ui->logTextEdit->append(logEntry);
qDebug() << logEntry;
// 自动滚动到底部
QTextCursor cursor = ui->logTextEdit->textCursor();
cursor.movePosition(QTextCursor::End);
ui->logTextEdit->setTextCursor(cursor);
}
4.mainwindow.ui文件
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>500</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>600</width>
<height>500</height>
</size>
</property>
<property name="windowTitle">
<string>16K WAV音频播放器</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="titleLabel">
<property name="text">
<string>16K WAV音频播放器 (Qt5.14)</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filePathLabel">
<property name="text">
<string>文件路径显示</string>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="timeLabel">
<property name="text">
<string>00:00 / 00:00</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="font">
<font>
<pointsize>18</pointsize>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>true</bool>
</property>
<property name="format">
<string>%p%</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="playButton">
<property name="text">
<string>开始播放</string>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="styleSheet">
<string>QPushButton {
background-color: #4CAF50;
color: white;
font-weight: bold;
font-size: 14px;
border-radius: 5px;
border: none;
}
QPushButton:hover {
background-color: #45a049;
}
QPushButton:pressed {
background-color: #3d8b40;
}
QPushButton:disabled {
background-color: #cccccc;
}</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>就绪</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>播放日志</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTextEdit" name="logTextEdit">
<property name="maximumHeight">
<number>200</number>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>播放日志将显示在这里...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
四、显示效果
工程文件夹下需要有Output.wav,点击播放按钮则开始播放。


1071

被折叠的 条评论
为什么被折叠?



