文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT QML 开发(四)复杂控件--Listview
开源 C++ QT QML 开发(五)复杂控件--Gridview
开源 C++ QT QML 开发(十一)通讯--TCP服务器端
开源 C++ QT QML 开发(十二)通讯--TCP客户端
开源 C++ QT QML 开发(十五)通讯--http下载
开源 C++ QT QML 开发(十七)进程--LocalSocket
推荐链接:
开源 C# 快速开发(十六)数据库--sqlserver增删改查
本章节主要内容是:qml的多媒体音频的录制,保存为wav的16KHz格式。
1.代码分析
2.所有源码
3.效果演示
一、代码分析
- AudioManager 类分析
1.1 构造函数 AudioManager::AudioManager()
AudioManager::AudioManager(QObject *parent)
: QObject(parent)
, m_audioInput(nullptr)
, m_outputFile(nullptr)
, m_buffer(nullptr)
, m_timer(new QTimer(this))
, m_isRecording(false)
, m_audioDuration(0)
, m_statusText("就绪")
, m_timeText("00:00")
, m_progress(0)
, m_logText("")
{
// 连接计时器信号到更新进度槽函数
connect(m_timer, &QTimer::timeout, this, &AudioManager::updateProgress);
// 记录启动日志
logMessage("程序启动完成");
}
功能分析:
初始化所有成员变量:将指针变量初始化为nullptr,布尔值初始化为false,数值初始化为0,字符串初始化为默认值
创建计时器:QTimer用于定期更新录音进度显示
信号槽连接:将计时器的timeout信号连接到updateProgress槽函数,每100ms触发一次
初始状态设置:设置初始状态文本、时间显示和进度
日志记录:调用logMessage记录程序启动信息
1.2 析构函数 AudioManager::~AudioManager()
AudioManager::~AudioManager()
{
stopRecording();
}
功能分析:
资源清理:在对象销毁时自动调用stopRecording()确保录音被正确停止
防止资源泄漏:确保所有动态分配的资源被正确释放
1.3 音频输入设置 AudioManager::setupAudioInput()
void AudioManager::setupAudioInput()
{
// 设置音频格式:16kHz, 16位, 单声道
QAudioFormat format;
format.setSampleRate(16000); // 采样率16kHz
format.setChannelCount(1); // 单声道
format.setSampleSize(16); // 16位采样大小
format.setCodec("audio/pcm"); // PCM编码
format.setByteOrder(QAudioFormat::LittleEndian); // 小端字节序
format.setSampleType(QAudioFormat::SignedInt); // 有符号整数
// 检查格式支持
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
if (!info.isFormatSupported(format)) {
logMessage("警告:默认格式不支持,使用最近似格式");
format = info.nearestFormat(format); // 获取最接近的格式
}
// 记录音频格式信息
logMessage(QString("音频格式:%1Hz, %2位, %3声道")
.arg(format.sampleRate())
.arg(format.sampleSize())
.arg(format.channelCount()));
// 创建音频输入对象
m_audioInput = new QAudioInput(format, this);
}
功能分析:
音频格式配置:设置标准的PCM音频格式参数
硬件兼容性检查:检查默认音频设备是否支持指定格式
格式自适应:如果不支持则使用最接近的格式
日志记录:记录最终使用的音频格式信息
对象创建:创建QAudioInput对象用于音频输入
1.4 开始录音 AudioManager::startRecording()
void AudioManager::startRecording()
{
if (m_isRecording) return; // 防止重复开始
logMessage("开始录音...");
// 设置音频输入
setupAudioInput();
// 创建输出文件
QString filePath = QDir::currentPath() + "/input.wav";
m_outputFile = new QFile(filePath, this);
if (!m_outputFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
logMessage("错误:无法创建文件");
delete m_outputFile;
m_outputFile = nullptr;
return;
}
// 先写入空的WAV文件头
setupWavHeader(*m_outputFile, 0);
// 创建缓冲区
m_buffer = new QBuffer(this);
m_buffer->open(QIODevice::ReadWrite);
// 开始录音
m_audioInput->start(m_buffer);
m_isRecording = true;
// 启动计时器
m_timer->start(100); // 100ms间隔
m_audioDuration = 0;
// 更新状态
m_statusText = "正在录音...";
m_progress = 0;
m_timeText = "00:00";
// 发出属性变化信号
emit isRecordingChanged();
emit statusTextChanged();
emit timeTextChanged();
emit progressChanged();
logMessage("录音进行中");
}
功能分析:
状态检查:防止在录音状态下重复开始
资源初始化:依次初始化音频输入、输出文件、缓冲区
文件操作:创建WAV文件并写入初始文件头
缓冲区设置:创建内存缓冲区存储音频数据
启动录音:调用QAudioInput的start()方法开始录音
计时器启动:开始计时器用于更新UI
状态更新:更新所有相关状态变量
信号发射:通知QML界面更新显示
1.5 停止录音 AudioManager::stopRecording()
void AudioManager::stopRecording()
{
if (!m_isRecording) return; // 如果不在录音状态,直接返回
logMessage("停止录音...");
// 停止录音
m_audioInput->stop();
m_isRecording = false;
// 停止计时器
m_timer->stop();
// 写入音频数据到文件
if (m_buffer && m_outputFile) {
QByteArray audioData = m_buffer->data();
m_outputFile->seek(0); // 回到文件开头
setupWavHeader(*m_outputFile, audioData.size()); // 重新写入正确的文件头
m_outputFile->write(audioData); // 写入音频数据
m_outputFile->close();
logMessage(QString("录音完成,文件大小:%1 字节").arg(audioData.size()));
}
// 清理资源
if (m_buffer) {
m_buffer->close();
m_buffer->deleteLater();
m_buffer = nullptr;
}
if (m_outputFile) {
m_outputFile->deleteLater();
m_outputFile = nullptr;
}
if (m_audioInput) {
m_audioInput->deleteLater();
m_audioInput = nullptr;
}
// 更新状态
m_statusText = "录音完成";
m_progress = 100;
// 发出属性变化信号
emit isRecordingChanged();
emit statusTextChanged();
emit progressChanged();
}
功能分析:
状态检查:确保在录音状态下才执行停止操作
停止录音:调用QAudioInput的stop()方法
停止计时器:停止进度更新
文件处理:
获取缓冲区中的音频数据
重新写入正确的WAV文件头(包含实际数据大小)
写入音频数据并关闭文件
资源清理:使用deleteLater安全删除对象
状态更新:更新UI状态为完成
1.6 更新进度 AudioManager::updateProgress()
void AudioManager::updateProgress()
{
m_audioDuration += 100; // 每次增加100ms
int seconds = m_audioDuration / 1000; // 转换为秒
// 格式化时间显示:MM:SS
m_timeText = QString("%1:%2")
.arg(seconds / 60, 2, 10, QLatin1Char('0')) // 分钟,2位,补0
.arg(seconds % 60, 2, 10, QLatin1Char('0')); // 秒钟,2位,补0
m_progress = qMin(100, seconds); // 进度最大100%
// 发出变化信号
emit timeTextChanged();
emit progressChanged();
}
功能分析:
时间累计:每次调用增加100ms录音时间
时间格式化:将毫秒转换为MM:SS格式的字符串
进度计算:每秒增加1%进度,最大100%
信号发射:通知QML更新时间显示和进度条
1.7 WAV文件头设置 AudioManager::setupWavHeader()
void AudioManager::setupWavHeader(QFile &file, quint32 dataSize)
{
// WAV文件头结构
struct WavHeader {
char riff[4] = {'R','I','F','F'}; // "RIFF"标识
quint32 chunkSize; // 文件总大小-8
char wave[4] = {'W','A','V','E'}; // "WAVE"标识
char fmt[4] = {'f','m','t',' '}; // "fmt "标识
quint32 fmtChunkSize = 16; // fmt块大小
quint16 audioFormat = 1; // 音频格式:1=PCM
quint16 numChannels = 1; // 声道数
quint32 sampleRate = 16000; // 采样率
quint32 byteRate; // 字节率
quint16 blockAlign; // 块对齐
quint16 bitsPerSample = 16; // 位深度
char data[4] = {'d','a','t','a'}; // "data"标识
quint32 dataChunkSize; // 数据块大小
};
WavHeader header;
header.chunkSize = 36 + dataSize; // 文件总大小-8
header.byteRate = 16000 * 1 * 16 / 8; // 采样率 × 声道数 × 位深度 / 8
header.blockAlign = 1 * 16 / 8; // 声道数 × 位深度 / 8
header.dataChunkSize = dataSize; // 音频数据大小
// 写入文件头到文件
file.write(reinterpret_cast<const char*>(&header), sizeof(WavHeader));
}
功能分析:
WAV文件结构:定义标准的WAV文件头结构
参数计算:
chunkSize = 36 + 数据大小(文件总大小-8)
byteRate = 采样率 × 声道数 × 位深度 ÷ 8
blockAlign = 声道数 × 位深度 ÷ 8
二进制写入:将结构体以二进制形式写入文件
1.8 日志记录 AudioManager::logMessage()
void AudioManager::logMessage(const QString &message)
{
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);
// 添加换行符(如果不是第一条日志)
if (!m_logText.isEmpty()) {
m_logText += "\n";
}
m_logText += logEntry;
// 发出日志变化信号
emit logTextChanged();
// 同时输出到调试控制台
qDebug() << message;
}
功能分析:
时间戳生成:获取当前时间并格式化为hh:mm:ss
日志格式化:组合时间戳和消息内容
日志累积:在现有日志后追加新日志,用换行符分隔
信号通知:通知QML界面更新日志显示
调试输出:同时输出到qDebug便于调试
- Main函数分析
2.1 应用程序初始化 main()
int main(int argc, char *argv[])
{
// 启用高DPI缩放
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// 创建GUI应用程序
QGuiApplication app(argc, argv);
// 设置应用程序信息
app.setApplicationName("音频录音机");
app.setApplicationVersion("1.0");
app.setOrganizationName("MyCompany");
// 注册AudioManager类到QML系统
qmlRegisterType<AudioManager>("AudioRecorder", 1, 0, "AudioManager");
// 创建QML引擎
QQmlApplicationEngine engine;
// 创建全局AudioManager实例并设置为上下文属性
AudioManager *audioManager = new AudioManager(&app);
engine.rootContext()->setContextProperty("audioManager", audioManager);
// 加载QML文件
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
// 检查QML加载是否成功
if (engine.rootObjects().isEmpty()) {
return -1;
}
// 进入事件循环
return app.exec();
}
功能分析:
应用程序配置:设置高DPI缩放和应用信息
QML类型注册:将C++类注册到QML系统中
上下文属性设置:创建AudioManager实例并使其在QML中可用
QML加载:加载并显示QML界面
错误处理:检查QML加载是否成功
事件循环:启动Qt事件循环
- QML界面分析
3.1 主窗口结构
ApplicationWindow {
id: window
width: 600
height: 500
title: "音频录音机 - QML版本"
visible: true
// 背景渐变
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#2c3e50" }
GradientStop { position: 1.0; color: "#34495e" }
}
}
// ... 其他组件
}
功能分析:
窗口设置:定义窗口大小、标题和可见性
背景设计:使用渐变背景创造现代感界面
3.2 录音按钮实现
Button {
id: recordButton
text: audioManager.isRecording ? "停止录音" : "开始录音"
// ... 其他属性
background: Rectangle {
radius: 30
color: audioManager.isRecording ? "#e74c3c" : "#2ecc71"
// ... 其他属性
Behavior on color {
ColorAnimation { duration: 300 }
}
}
onClicked: {
if (audioManager.isRecording) {
audioManager.stopRecording()
} else {
audioManager.startRecording()
}
}
}
功能分析:
动态文本:根据录音状态显示不同文本
颜色切换:录音时显示红色,停止时显示绿色
动画效果:颜色变化时有300ms的过渡动画
点击处理:调用C++的start/stopRecording方法
3.3 数据绑定机制
// 状态文本绑定
Label {
text: audioManager.statusText // 自动绑定到C++属性
}
// 时间显示绑定
Label {
text: audioManager.timeText // 自动绑定到C++属性
}
// 进度条绑定
ProgressBar {
value: audioManager.progress // 自动绑定到C++属性
}
// 日志显示绑定
TextArea {
text: audioManager.logText // 自动绑定到C++属性
}
功能分析:
自动更新:当C++端属性变化并发出信号时,QML界面自动更新
双向通信:C++逻辑控制业务,QML界面负责显示和用户交互
- 程序架构总结
这个录音程序采用了典型的MVC架构:
Model:AudioManager类,负责所有录音逻辑和数据处理
View:QML界面,负责用户界面显示和交互
Controller:QML中的事件处理和数据绑定
数据流:
用户点击QML按钮
QML调用C++的录音方法
C++处理录音逻辑并更新属性
C++发出属性变化信号
QML自动更新界面显示
二、所有源码
audiomanager.h文件源码
#ifndef AUDIOMANAGER_H
#define AUDIOMANAGER_H
#include <QObject>
#include <QAudioInput>
#include <QAudioFormat>
#include <QFile>
#include <QBuffer>
#include <QTimer>
#include <QDateTime>
class AudioManager : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isRecording READ isRecording NOTIFY isRecordingChanged)
Q_PROPERTY(QString statusText READ statusText NOTIFY statusTextChanged)
Q_PROPERTY(QString timeText READ timeText NOTIFY timeTextChanged)
Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
Q_PROPERTY(QString logText READ logText NOTIFY logTextChanged)
public:
explicit AudioManager(QObject *parent = nullptr);
~AudioManager();
bool isRecording() const { return m_isRecording; }
QString statusText() const { return m_statusText; }
QString timeText() const { return m_timeText; }
int progress() const { return m_progress; }
QString logText() const { return m_logText; }
Q_INVOKABLE void startRecording();
Q_INVOKABLE void stopRecording();
private slots:
void updateProgress();
private:
void setupAudioInput();
void setupWavHeader(QFile &file, quint32 dataSize);
void logMessage(const QString &message);
QAudioInput *m_audioInput;
QFile *m_outputFile;
QBuffer *m_buffer;
QTimer *m_timer;
bool m_isRecording;
int m_audioDuration;
// Q_PROPERTY backing fields
QString m_statusText;
QString m_timeText;
int m_progress;
QString m_logText;
signals:
void isRecordingChanged();
void statusTextChanged();
void timeTextChanged();
void progressChanged();
void logTextChanged();
};
#endif // AUDIOMANAGER_H
audiomanager.cpp文件源码
#include "audiomanager.h"
#include <QAudioDeviceInfo>
#include <QDir>
#include <QDebug>
#include <cmath>
AudioManager::AudioManager(QObject *parent)
: QObject(parent)
, m_audioInput(nullptr)
, m_outputFile(nullptr)
, m_buffer(nullptr)
, m_timer(new QTimer(this))
, m_isRecording(false)
, m_audioDuration(0)
, m_statusText("就绪")
, m_timeText("00:00")
, m_progress(0)
, m_logText("")
{
connect(m_timer, &QTimer::timeout, this, &AudioManager::updateProgress);
logMessage("程序启动完成");
}
AudioManager::~AudioManager()
{
stopRecording();
}
void AudioManager::setupAudioInput()
{
// 设置音频格式:16kHz, 16位, 单声道
QAudioFormat format;
format.setSampleRate(16000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
// 检查格式支持
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
if (!info.isFormatSupported(format)) {
logMessage("警告:默认格式不支持,使用最近似格式");
format = info.nearestFormat(format);
}
logMessage(QString("音频格式:%1Hz, %2位, %3声道")
.arg(format.sampleRate())
.arg(format.sampleSize())
.arg(format.channelCount()));
m_audioInput = new QAudioInput(format, this);
}
void AudioManager::startRecording()
{
if (m_isRecording) return;
logMessage("开始录音...");
// 设置音频输入
setupAudioInput();
// 创建输出文件
QString filePath = QDir::currentPath() + "/input.wav";
m_outputFile = new QFile(filePath, this);
if (!m_outputFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
logMessage("错误:无法创建文件");
delete m_outputFile;
m_outputFile = nullptr;
return;
}
// 先写入空的WAV文件头
setupWavHeader(*m_outputFile, 0);
// 创建缓冲区
m_buffer = new QBuffer(this);
m_buffer->open(QIODevice::ReadWrite);
// 开始录音
m_audioInput->start(m_buffer);
m_isRecording = true;
// 启动计时器
m_timer->start(100);
m_audioDuration = 0;
// 更新状态
m_statusText = "正在录音...";
m_progress = 0;
m_timeText = "00:00";
emit isRecordingChanged();
emit statusTextChanged();
emit timeTextChanged();
emit progressChanged();
logMessage("录音进行中");
}
void AudioManager::stopRecording()
{
if (!m_isRecording) return;
logMessage("停止录音...");
// 停止录音
m_audioInput->stop();
m_isRecording = false;
// 停止计时器
m_timer->stop();
// 写入音频数据到文件
if (m_buffer && m_outputFile) {
QByteArray audioData = m_buffer->data();
m_outputFile->seek(0);
setupWavHeader(*m_outputFile, audioData.size());
m_outputFile->write(audioData);
m_outputFile->close();
logMessage(QString("录音完成,文件大小:%1 字节").arg(audioData.size()));
}
// 清理资源
if (m_buffer) {
m_buffer->close();
m_buffer->deleteLater();
m_buffer = nullptr;
}
if (m_outputFile) {
m_outputFile->deleteLater();
m_outputFile = nullptr;
}
if (m_audioInput) {
m_audioInput->deleteLater();
m_audioInput = nullptr;
}
// 更新状态
m_statusText = "录音完成";
m_progress = 100;
emit isRecordingChanged();
emit statusTextChanged();
emit progressChanged();
}
void AudioManager::updateProgress()
{
m_audioDuration += 100;
int seconds = m_audioDuration / 1000;
m_timeText = QString("%1:%2")
.arg(seconds / 60, 2, 10, QLatin1Char('0'))
.arg(seconds % 60, 2, 10, QLatin1Char('0'));
m_progress = qMin(100, seconds);
emit timeTextChanged();
emit progressChanged();
}
void AudioManager::setupWavHeader(QFile &file, quint32 dataSize)
{
// WAV文件头结构
struct WavHeader {
char riff[4] = {'R','I','F','F'};
quint32 chunkSize;
char wave[4] = {'W','A','V','E'};
char fmt[4] = {'f','m','t',' '};
quint32 fmtChunkSize = 16;
quint16 audioFormat = 1; // PCM
quint16 numChannels = 1;
quint32 sampleRate = 16000;
quint32 byteRate;
quint16 blockAlign;
quint16 bitsPerSample = 16;
char data[4] = {'d','a','t','a'};
quint32 dataChunkSize;
};
WavHeader header;
header.chunkSize = 36 + dataSize;
header.byteRate = 16000 * 1 * 16 / 8; // sampleRate * channels * bitsPerSample / 8
header.blockAlign = 1 * 16 / 8; // channels * bitsPerSample / 8
header.dataChunkSize = dataSize;
file.write(reinterpret_cast<const char*>(&header), sizeof(WavHeader));
}
void AudioManager::logMessage(const QString &message)
{
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);
if (!m_logText.isEmpty()) {
m_logText += "\n";
}
m_logText += logEntry;
emit logTextChanged();
qDebug() << message;
}
main.qml文件源码
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
ApplicationWindow {
id: window
width: 600
height: 500
title: "音频录音机 - QML版本"
visible: true
// 背景渐变
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#2c3e50" }
GradientStop { position: 1.0; color: "#34495e" }
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// 标题
Label {
text: "音频录音机"
Layout.alignment: Qt.AlignHCenter
color: "white"
font.pixelSize: 28
font.bold: true
}
// 状态显示区域
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 120
color: "#34495e"
radius: 10
border.color: "#3498db"
border.width: 2
ColumnLayout {
anchors.fill: parent
anchors.margins: 15
spacing: 10
// 状态标签
Label {
text: audioManager.statusText
Layout.fillWidth: true
color: "white"
font.pixelSize: 18
horizontalAlignment: Text.AlignHCenter
}
// 时间显示
Label {
text: audioManager.timeText
Layout.fillWidth: true
color: "#e74c3c"
font.pixelSize: 32
font.bold: true
horizontalAlignment: Text.AlignHCenter
}
// 进度条
ProgressBar {
id: progressBar
Layout.fillWidth: true
from: 0
to: 100
value: audioManager.progress
background: Rectangle {
implicitWidth: 200
implicitHeight: 6
color: "#2c3e50"
radius: 3
}
contentItem: Item {
implicitWidth: 200
implicitHeight: 4
Rectangle {
width: progressBar.visualPosition * parent.width
height: parent.height
radius: 2
gradient: Gradient {
GradientStop { position: 0.0; color: "#3498db" }
GradientStop { position: 1.0; color: "#2980b9" }
}
}
}
}
}
}
// 录音按钮
Button {
id: recordButton
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 200
Layout.preferredHeight: 60
text: audioManager.isRecording ? "停止录音" : "开始录音"
font.pixelSize: 18
font.bold: true
background: Rectangle {
radius: 30
color: audioManager.isRecording ? "#e74c3c" : "#2ecc71"
border.color: audioManager.isRecording ? "#c0392b" : "#27ae60"
border.width: 3
Behavior on color {
ColorAnimation { duration: 300 }
}
}
contentItem: Text {
text: recordButton.text
font: recordButton.font
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
onClicked: {
if (audioManager.isRecording) {
audioManager.stopRecording()
} else {
audioManager.startRecording()
}
}
}
// 日志区域
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "#1e272e"
radius: 10
border.color: "#7f8c8d"
border.width: 1
ColumnLayout {
anchors.fill: parent
spacing: 5
Label {
text: "操作日志"
color: "#bdc3c7"
font.pixelSize: 16
font.bold: true
Layout.leftMargin: 10
Layout.topMargin: 5
}
ScrollView {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 10
TextArea {
id: logTextArea
text: audioManager.logText
color: "#ecf0f1"
font.pixelSize: 12
font.family: "Consolas, Monaco, monospace"
wrapMode: Text.Wrap
readOnly: true
selectByMouse: true
background: Rectangle {
color: "transparent"
}
}
}
}
}
// 底部信息
Label {
text: "基于Qt5.14 QML的录音程序 | 支持WAV格式 | 16kHz 16位 单声道"
Layout.alignment: Qt.AlignHCenter
color: "#95a5a6"
font.pixelSize: 12
}
}
// 录音时的动画效果
Rectangle {
id: recordingIndicator
width: 20
height: 20
radius: 10
color: "#e74c3c"
visible: audioManager.isRecording
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: 15
SequentialAnimation on opacity {
running: recordingIndicator.visible
loops: Animation.Infinite
NumberAnimation { from: 0.3; to: 1.0; duration: 800 }
NumberAnimation { from: 1.0; to: 0.3; duration: 800 }
}
}
}
main.cpp文件源码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "audiomanager.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// 设置应用程序信息
app.setApplicationName("音频录音机");
app.setApplicationVersion("1.0");
app.setOrganizationName("MyCompany");
// 注册AudioManager类到QML
qmlRegisterType<AudioManager>("AudioRecorder", 1, 0, "AudioManager");
QQmlApplicationEngine engine;
// 创建全局AudioManager实例
AudioManager *audioManager = new AudioManager(&app);
engine.rootContext()->setContextProperty("audioManager", audioManager);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
if (engine.rootObjects().isEmpty()) {
return -1;
}
return app.exec();
}
三、效果演示
