【QQmusic】复习笔记第四章分点讲解

4.1 音乐加载

功能概述

该部分实现了从本地磁盘加载音乐文件到程序中,并在界面上显示的功能。通过QFileDialog类创建文件选择对话框,用户可选择多个音乐文件,程序筛选出有效音频文件后,交由MusicList类管理,并更新到本地音乐页面显示。

核心实现步骤

1. 使用QFileDialog创建文件对话框

通过QFileDialog类实现文件选择功能,设置对话框的标题、打开模式、文件筛选条件等:

QFileDialog fileDialog(this);
fileDialog.setWindowTitle("添加本地音乐"); // 设置对话框标题
fileDialog.setAcceptMode(QFileDialog::AcceptOpen); // 设置为打开文件模式
fileDialog.setFileMode(QFileDialog::ExistingFiles); // 允许选择多个现有文件
2. 筛选音频文件(MIME类型过滤)

使用QMimeDatabase检测文件类型,仅加载支持的音频格式(如MP3、FLAC):

QStringList mimeList;
mimeList << "audio/mpeg" << "audio/flac"; // 支持的MIME类型
fileDialog.setMimeTypeFilters(mimeList); // 设置文件过滤器
3. 设置默认打开目录

指定对话框默认打开的目录,方便用户快速找到音乐文件:

QDir dir(QDir::currentPath());
dir.cdUp(); // 切换到上一级目录
QString musicPath = dir.path() + "/QQMusic/musics/";
fileDialog.setDirectory(musicPath); // 设置默认目录
4. 处理用户选择的文件

用户点击“打开”后,获取选中的文件路径列表,切换到本地音乐页面,并将文件交由MusicList类解析和管理:

if (fileDialog.exec() == QFileDialog::Accepted) {
    ui->stackedWidget->setCurrentIndex(4); // 切换到本地音乐页面(索引4)
    QList<QUrl> urls = fileDialog.selectedUrls(); // 获取选中的文件URL
    musicList.addMusicByUrl(urls); // 交由MusicList处理
    ui->localPage->reFresh(musicList); // 更新本地音乐列表显示
}

关键类与方法

QFileDialog类
  • 作用:创建文件选择对话框,支持用户选择单个或多个文件。
  • 核心方法
    • setAcceptMode():设置对话框模式(打开/保存)。
    • setFileMode():设置文件选择模式(单个文件、多个文件、目录等)。
    • setMimeTypeFilters():通过MIME类型过滤文件,确保仅显示音频文件。
    • selectedUrls():获取用户选中的文件URL列表。
QMimeDatabase类
  • 作用:检测文件的MIME类型,验证是否为音频文件。
  • 核心方法
    • mimeTypeForFile():获取文件的MIME类型,用于过滤无效文件(如非音频文件)。

代码逻辑详解

void QQMusic::on_addLocal_clicked() {
    QFileDialog fileDialog(this);
    fileDialog.setWindowTitle("添加本地音乐");
    fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
    fileDialog.setFileMode(QFileDialog::ExistingFiles);
    
    // 设置MIME过滤器,仅显示MP3和FLAC文件
    QStringList mimeList;
    mimeList << "audio/mpeg" << "audio/flac";
    fileDialog.setMimeTypeFilters(mimeList);
    
    // 设置默认目录
    QDir dir(QDir::currentPath());
    dir.cdUp();
    QString musicPath = dir.path() + "/QQMusic/musics/";
    fileDialog.setDirectory(musicPath);
    
    if (fileDialog.exec() == QFileDialog::Accepted) {
        // 切换到本地音乐页面
        ui->stackedWidget->setCurrentIndex(4);
        // 获取选中的文件URL
        QList<QUrl> urls = fileDialog.selectedUrls();
        // 将文件添加到MusicList中
        musicList.addMusicByUrl(urls);
        // 更新本地音乐页面显示
        ui->localPage->reFresh(musicList);
    }
}

功能扩展点

  • 支持更多音频格式:在MIME过滤器中添加更多支持的类型(如audio/wav)。
  • 进度提示:在加载大量文件时显示进度条,提升用户体验。
  • 重复文件检测:通过文件路径或MD5校验避免重复加载同一文件。

通过以上步骤,实现了本地音乐文件的加载、筛选和界面显示,为后续的播放、收藏、历史记录等功能奠定了数据基础。

4.2 MusicList类详解

一、类的定义与核心作用

MusicList类是项目中管理音乐列表的核心数据结构,主要负责以下功能:

  1. 统一管理音乐对象:存储所有加载的音乐文件(Music实例),支持添加、查找、筛选等操作。
  2. 格式筛选与唯一性保证:过滤无效文件格式,避免重复加载相同音乐。
  3. 与数据库交互:实现音乐信息的持久化存储与读取(后续结合数据库模块)。
二、核心成员变量
成员变量类型说明
musicListQVector<Music>存储所有音乐对象,利用QVector的顺序存储和快速访问特性。
musicPathsQSet<QString>记录已加载音乐的文件路径,避免重复添加同一文件(基于路径唯一性)。
三、核心功能实现
1. 音乐文件加载与格式筛选
  • 功能:从文件路径列表中筛选有效音频文件(MP3/FLAC),创建Music对象并添加到列表。
  • 实现步骤
    1. 遍历文件路径:通过addMusicByUrl方法接收QList<QUrl>类型的文件路径。
    2. MIME类型检测:使用QMimeDatabase检测文件类型,仅保留audio/mpeg(MP3)和audio/flac(FLAC)格式。
      QMimeDatabase db;  
      QMimeType mime = db.mimeTypeForFile(musicUrl.toLocalFile());  
      if (mime.name() != "audio/mpeg" && mime.name() != "audio/flac") continue;  
      
    3. 唯一性检查:通过musicPaths集合判断文件是否已加载,避免重复添加。
      if (musicPaths.contains(musicPath)) continue;  
      musicPaths.insert(musicPath);  
      
    4. 创建Music对象:为有效文件创建Music实例,自动解析元数据(名称、歌手、专辑等)。
2. 高效查找与遍历支持
  • 通过musicId查找音乐:未来可添加findMusicById方法(文档中后续扩展),通过遍历musicList匹配唯一ID,确保快速定位音乐对象。
  • 范围遍历支持:重载begin()end()方法,支持范围for循环遍历,方便与界面模块(如CommonPage)交互。
    iterator begin() { return musicList.begin(); }  
    iterator end() { return musicList.end(); }  
    
3. 数据持久化准备(后续扩展)
  • 数据库表映射:后续可通过MusicList将音乐信息写入数据库(如musicInfo表),包含musicId、名称、歌手、路径、收藏状态等字段。
  • 批量操作:提供writeToDB()readFromDB()方法,实现程序退出时保存音乐列表、启动时恢复数据(见文档6.3节数据库部分)。
四、关键代码示例
// MusicList.h 核心声明  
class MusicList {  
public:  
    void addMusicByUrl(const QList<QUrl>& urls); // 加载音乐文件  
    iterator begin(); iterator end(); // 支持范围遍历  
private:  
    QVector<Music> musicList; // 存储音乐对象  
    QSet<QString> musicPaths; // 已加载文件路径集合(避免重复)  
};  

// MusicList.cpp 添加音乐逻辑  
void MusicList::addMusicByUrl(const QList<QUrl>& urls) {  
    for (const auto& url : urls) {  
        QString musicPath = url.toLocalFile();  
        if (musicPaths.contains(musicPath)) continue; // 跳过已加载文件  
        QMimeDatabase db;  
        QMimeType mime = db.mimeTypeForFile(musicPath);  
        if (mime.name() != "audio/mpeg" && mime.name() != "audio/flac") continue; // 筛选有效格式  
        Music music(url); // 创建Music对象,自动解析元数据  
        musicList.push_back(music);  
        musicPaths.insert(musicPath); // 记录路径  
    }  
}  
五、与其他模块的协作
  1. Music类协作
    • 每个音乐文件对应一个Music对象,MusicList存储这些对象,依赖Music类解析元数据(如parseMediaMetaData方法)和标记状态(isLikeisHistory)。
  2. 与界面交互
    • CommonPage页面(如“本地下载”)通过MusicList筛选对应条件的歌曲(如未被收藏的本地文件),更新界面显示。
  3. 与数据库交互
    • 程序启动时通过readFromDB()从数据库加载历史数据,退出时通过writeToDB()保存当前状态,确保数据持久化。
六、设计优势
  1. 格式兼容性:通过MIME类型检测,支持多种音频格式扩展(可新增支持audio/wav等)。
  2. 性能优化:使用QSet实现O(1)时间复杂度的重复文件检测,避免冗余加载。
  3. 代码复用:统一管理音乐数据,为“我喜欢”“最近播放”等功能提供底层数据支持,减少重复逻辑。

通过MusicList类,项目实现了对音乐文件的高效管理、格式筛选和状态维护,为后续播放控制、界面显示和数据持久化奠定了基础。

4.3 Music类解析

一、类的定义与核心作用

Music类是项目中描述单个音乐文件的核心数据模型,负责封装音乐的元数据(如名称、歌手、专辑等)、状态信息(收藏、历史播放)及文件路径,是音乐管理、播放控制和数据持久化的基础。具体作用包括:

  1. 数据封装:统一管理音乐文件的基本信息和状态,便于界面显示和逻辑处理。
  2. 元数据解析:自动提取音乐文件的元数据(标题、作者等),处理缺失信息并设置默认值。
  3. 状态标记:支持标记音乐是否被收藏(isLike)或播放过(isHistory),为“我喜欢”“最近播放”等功能提供数据支持。
  4. 唯一性保证:通过UUID生成唯一标识,避免重复添加同一首歌曲。
二、核心成员变量
成员变量类型说明
isLikebool标记音乐是否被收藏(默认false),界面通过此值显示“小心心”图标。
isHistorybool标记音乐是否被播放过(默认false),用于“最近播放”页面筛选数据。
musicNameQString歌曲名称,解析自文件元数据或文件名,默认“歌曲未知”。
singerNameQString歌手名称,支持多歌手用逗号分隔,默认“歌手未知”。
albumNameQString专辑名称,默认“专辑名未知”。
durationqint64歌曲总时长(毫秒),用于播放进度和时长显示。
musicIdQString唯一标识(UUID生成),确保同一歌曲多次加载时视为同一对象,避免重复。
musicUrlQUrl歌曲文件在磁盘中的路径,用于播放和数据库存储。
三、构造函数与初始化
  1. 默认构造函数

    Music::Music() : isLike(false), isHistory(false) {}  
    
    • 初始化收藏和历史状态为未标记状态。
  2. 带路径的构造函数

    Music::Music(const QUrl &url) : isLike(false), isHistory(false), musicUrl(url) {  
        musicId = QUuid::createUuid().toString(); // 生成UUID保证唯一性  
        parseMediaMetaData(); // 解析元数据(标题、歌手、专辑、时长)  
    }  
    
    • UUID生成:使用QUuid::createUuid()生成唯一ID(如550e8400-e29b-41d4-a716-446655440000),避免重复添加相同歌曲。
    • 元数据解析:调用parseMediaMetaData()提取文件元数据,处理缺失信息(如盗版歌曲设置默认值)。
四、元数据解析(核心功能)

通过QMediaPlayer解析音乐文件的元数据,处理可能缺失的信息:

void Music::parseMediaMetaData() {  
    QMediaPlayer player;  
    player.setMedia(musicUrl);  
    while (!player.isMetaDataAvailable()) {  
        QCoreApplication::processEvents(); // 保持界面响应,避免卡死  
    }  
    if (player.isMetaDataAvailable()) {  
        musicName = player.metaData("Title").toString().trimmed();  
        singerName = player.metaData("Author").toStringList().join(",").trimmed();  
        albumName = player.metaData("AlbumTitle").toString().trimmed();  
        duration = player.duration();  
    }  
    // 处理空值情况  
    if (musicName.isEmpty()) musicName = "歌曲未知";  
    if (singerName.isEmpty()) singerName = "歌手未知";  
    if (albumName.isEmpty()) albumName = "专辑名未知";  
}  
  • 处理逻辑:若元数据缺失(如无标题),设置默认值(如“歌曲未知”)。
  • 线程安全:通过QCoreApplication::processEvents()处理事件循环,确保解析时界面可交互。
五、唯一性与状态管理
  1. 唯一性保证

    • 使用UUID作为musicId,通过QUuid::createUuid()生成全局唯一标识,避免同一文件多次加载时创建重复对象。
    • MusicList类中通过musicMap哈希表(键为musicId,值为索引)实现O(1)时间复杂度的快速查找。
  2. 状态标记

    • setIsLike(bool)setIsHistory(bool)方法更新收藏和历史状态,界面(如ListItemBox)通过getIsLike()getIsHistory()获取状态并显示对应图标(如红色小心心表示已收藏)。
六、与其他模块的交互
  1. 数据库持久化

    • insertMusicToDB():将音乐信息写入数据库(见6.3.2节),包括musicId、名称、歌手、路径、状态等字段。
    • MusicList类通过readFromDB()从数据库加载歌曲时,创建Music对象并填充数据。
  2. 界面显示

    • CommonPage页面(如“我喜欢”)通过Musicget方法获取歌曲名称、歌手、专辑等信息,填充到ListItemBox中显示。
    • 收藏功能:点击“小心心”时,调用setIsLike(true)标记歌曲,并更新数据库和界面。
  3. 播放控制

    • 提供musicUrlQMediaPlayer加载音频文件,支持播放、暂停、进度调节等操作。
七、关键代码示例
// 生成唯一UUID  
musicId = QUuid::createUuid().toString();  

// 解析元数据并处理空值  
if (player.metaData("Title").toString().isEmpty()) {  
    musicName = "歌曲未知";  
}  

// 状态标记  
void setIsLike(bool isLike) { this->isLike = isLike; }  
bool getIsLike() { return isLike; }  
八、总结

Music类是项目的核心数据载体,实现了以下核心功能:

  1. 数据封装:统一管理歌曲元数据和状态,确保信息一致性。
  2. 唯一性:通过UUID避免重复加载,提升数据管理效率。
  3. 元数据处理:自动解析文件信息,处理异常情况,保证界面正确显示。
  4. 持久化支持:与数据库交互,实现歌曲信息的保存和恢复。

该类的设计为“我喜欢”“最近播放”等功能提供了底层数据支持,是音乐管理、播放控制和界面显示的基础。

4.4 音乐分类

一、功能概述

QQMusic通过CommonPage类实现“我喜欢”“本地下载”“最近播放”三个页面的音乐分类显示。核心逻辑是通过枚举类型区分页面类型,结合音乐对象的状态标记(如是否收藏、是否播放过)进行数据过滤,并在界面上动态更新对应列表。

二、页面类型枚举定义

CommonPage类中定义枚举PageType,明确区分三种页面类型,确保每个页面知道自己需要显示的音乐类型:

enum PageType {  
    LIKE_PAGE,       // 我喜欢页面  
    LOCAL_PAGE,      // 本地下载页面  
    HISTORY_PAGE     // 最近播放页面  
};  
  • 作用:通过枚举值(如LIKE_PAGE)标记当前页面类型,后续根据类型过滤音乐数据。
三、核心成员变量
成员变量类型说明
pageTypePageType标记当前页面属于哪种类型(如LIKE_PAGE),决定数据过滤逻辑。
musicListOfPageQVector<QString>存储当前页面的音乐ID列表(仅保存musicId,而非完整对象,节省内存)。
四、初始化与类型设置

QQMusicinitUi()方法中,为三个页面设置类型和初始UI:

ui->likePage->setMusicListType(PageType::LIKE_PAGE);  
ui->likePage->setCommonPageUI("我喜欢", ":/images/ilikebg.png");  
ui->localPage->setMusicListType(PageType::LOCAL_PAGE);  
ui->localPage->setCommonPageUI("本地音乐", ":/images/localbg.png");  
ui->recentPage->setMusicListType(PageType::HISTORY_PAGE);  
ui->recentPage->setCommonPageUI("最近播放", ":/images/recentbg.png");  
  • setMusicListType:设置页面类型,触发后续数据过滤逻辑。
  • setCommonPageUI:设置页面标题和背景图片,统一页面风格。
五、音乐数据过滤逻辑

CommonPage::addMusicToMusicPage方法中,根据pageType过滤MusicList中的音乐:

void CommonPage::addMusicToMusicPage(MusicList &musicList) {  
    musicListOfPage.clear(); // 清空旧数据,避免重复  
    for (auto& music : musicList) {  
        switch (pageType) {  
            case LOCAL_PAGE:  
                // 本地页面直接添加所有音乐(无过滤,显示所有本地加载的音乐)  
                musicListOfPage.push_back(music.getMusicId());  
                break;  
            case LIKE_PAGE:  
                // 喜欢页面仅添加标记为“喜欢”的音乐  
                if (music.getIsLike()) {  
                    musicListOfPage.push_back(music.getMusicId());  
                }  
                break;  
            case HISTORY_PAGE:  
                // 历史页面仅添加标记为“已播放”的音乐  
                if (music.getIsHistory()) {  
                    musicListOfPage.push_back(music.getMusicId());  
                }  
                break;  
        }  
    }  
}  
  • 过滤逻辑
    • 本地下载:直接添加所有本地加载的音乐(无过滤)。
    • 我喜欢:仅添加isLiketrue的音乐。
    • 最近播放:仅添加isHistorytrue的音乐。
六、界面数据更新

CommonPage::reFresh方法中,根据过滤后的musicListOfPage更新界面显示:

  1. 遍历音乐ID:通过musicIdMusicList中查找对应的Music对象。
  2. 创建列表项:为每个音乐创建ListItemBox,设置歌曲名称、歌手、专辑和收藏状态(小心心图标)。
for (auto musicId : musicListOfPage) {  
    auto it = musicList.findMusicById(musicId);  
    if (it != musicList.end()) {  
        ListItemBox* item = new ListItemBox(ui->pageMusicList);  
        item->setMusicName(it->getMusicName());  
        item->setSinger(it->getSingerName());  
        item->setAlbumName(it->getAlbumName());  
        item->setLikeIcon(it->getIsLike()); // 设置收藏状态图标  
        // 添加到QListWidget  
        QListWidgetItem* listItem = new QListWidgetItem(ui->pageMusicList);  
        listItem->setSizeHint(QSize(ui->pageMusicList->width(), 45));  
        ui->pageMusicList->setItemWidget(listItem, item);  
    }  
}  
七、信号与槽关联

通过信号槽机制,当用户点击“播放全部”或双击歌曲时,通知QQMusic类更新播放列表:

  1. 播放全部按钮
// CommonPage中播放全部按钮的信号发射  
connect(ui->playAllBtn, &QPushButton::clicked, this, [=]() {  
    emit playAll(pageType); // 发射当前页面类型,告知QQMusic播放对应页面的音乐  
});  
  1. QQMusic处理逻辑
// QQMusic中处理播放全部的槽函数  
void QQMusic::onPlayAll(PageType pageType) {  
    CommonPage* page = getPageByType(pageType); // 根据类型获取对应页面  
    page->addMusicToPlayer(musicList, playList); // 将页面音乐添加到播放列表  
    player->play(); // 开始播放  
}  
八、核心优势
  1. 代码复用
    • 通过CommonPage统一管理三个页面的布局和逻辑,仅通过pageType区分功能,减少重复代码(如列表显示、按钮交互)。
  2. 数据隔离
    • 每个页面仅存储对应音乐的musicId,通过唯一ID关联完整Music对象,高效且节省内存。
  3. 灵活过滤
    • 利用Music类的isLikeisHistory属性,轻松实现不同页面的筛选(如仅显示收藏或历史播放的音乐)。
  4. 界面统一
    • 使用自定义控件ListItemBox统一显示格式,确保三个页面的视觉和交互一致,提升用户体验。
九、总结

音乐分类功能通过枚举标记页面类型,结合数据过滤和界面更新逻辑,实现了“我喜欢”“本地下载”“最近播放”的高效管理。核心在于通过CommonPage复用代码,利用状态标记筛选数据,确保不同页面快速展示对应内容,同时通过信号槽机制与播放模块联动,提升整体功能的一致性和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云小逸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值