文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT Widget 开发(一)工程文件结构-CSDN博客
开源 C++ QT Widget 开发(二)基本控件应用-CSDN博客
开源 C++ QT Widget 开发(三)图表--波形显示器-CSDN博客
开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-CSDN博客
开源 C++ QT Widget 开发(五)通讯--串口调试-CSDN博客
开源 C++ QT Widget 开发(六)通讯--TCP调试-CSDN博客
开源 C++ QT Widget 开发(七)线程--多线程及通讯-CSDN博客
开源 C++ QT Widget 开发(八)网络--Http文件下载-CSDN博客
开源 C++ QT Widget 开发(九)图表--仪表盘-CSDN博客
开源 C++ QT Widget 开发(十)IPC进程间通信--共享内存-CSDN博客
开源 C++ QT Widget 开发(十一)进程间通信--Windows 窗口通信-CSDN博客
开源 C++ QT Widget 开发(十二)图表--环境监测表盘-CSDN博客
开源 C++ QT Widget 开发(十三)IPC通讯--本地套接字 (Local Socket)
开源 C++ QT Widget 开发(十四)多媒体--录音机
开源 C++ QT Widget 开发(十五)多媒体--音频播放
推荐链接:
开源 java android app 开发(一)开发环境的搭建-CSDN博客
开源 java android app 开发(二)工程文件结构-CSDN博客
开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客
开源 java android app 开发(四)GUI界面重要组件-CSDN博客
开源 java android app 开发(五)文件和数据库存储-CSDN博客
开源 java android app 开发(六)多媒体使用-CSDN博客
开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客
开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客
开源 java android app 开发(九)后台之线程和服务-CSDN博客
开源 java android app 开发(十)广播机制-CSDN博客
开源 java android app 开发(十一)调试、发布-CSDN博客
开源 java android app 开发(十二)封库.aar-CSDN博客
推荐链接:
开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客
开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客
开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客
开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客
开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客
内容: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(); // 文件检查
};
函数功能详细分析
- 构造函数 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); // 文件无效时禁用播放按钮
}
}
- 析构函数 MainWindow::~MainWindow()
功能:安全清理资源
MainWindow::~MainWindow()
{
if (player->state() == QMediaPlayer::PlayingState) {
player->stop(); // 如果正在播放,先停止
}
delete ui;
}
- 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;
}
- 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("播放器启动完成");
}
}
- 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));
}
}
}
- 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;
}
}
- 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); // 弹出错误对话框
}
- 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);
}
三、所有源码
-
pro文件
QT += core gui
QT += core gui multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += 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.cppHEADERS +=
mainwindow.hFORMS +=
mainwindow.uiDefault rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target -
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_NAMESPACEclass MainWindow : public QMainWindow
{
Q_OBJECTpublic:
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,点击播放按钮则开始播放。

