我用 Qt 写了一个音乐播放器:能播歌、能显示歌词,还能记住歌单
最近用 Qt 做了一个小项目音乐播放器。说实话它不算什么特别厉害的大工程但做完之后还挺有成就感的。因为麻雀虽小五脏俱全能添加歌曲、播放暂停、上一首下一首、调音量、拖进度条、切播放模式还能显示歌词、同步高亮甚至关掉软件再打开歌单还能从数据库里恢复回来。这篇文章就简单总结一下这个项目顺便贴几段我觉得比较典型的代码给同样在学 Qt 的朋友一个参考。一、这个播放器能干啥先列一下功能大概就是这些添加本地 MP3 文件播放、暂停、停止上一首、下一首拖动进度条跳转调节音量切换播放模式单曲播放、单曲循环、顺序播放、循环播放、随机播放显示歌曲名、歌手加载 .lrc 歌词文件并跟着播放进度高亮用 SQLite 保存歌单下次打开自动恢复清空播放列表同时清空数据库。界面是用 Qt Designer 画的主要控件有List itemplaylistWidget歌曲列表lyricsWidget歌词列表progressSlider播放进度条volumeSlider音量条一堆按钮添加、播放、暂停、停止、上一首、下一首、模式、清空。二、项目文件大概怎么分的这个项目文件不多结构也比较清楚List itemmain.cpp程序入口创建 QApplication 和主窗口musicplayer.h / musicplayer.cpp主窗口逻辑负责界面、播放控制、歌词同步、信号槽连接musicplayer.ui界面设计文件dbhelper.h / dbhelper.cpp数据库管理负责保存和读取歌单lyricthread.h / lyricthread.cpp歌词解析线程后台解析 .lrc 文件musicplayer.proqmake 工程文件里面加了 multimedia 和 sql 模块。.pro 里比较关键的是这一句QT core gui multimedia sql因为要播放音乐所以需要 multimedia要保存歌单所以需要 sql。三、几个我觉得挺有意思的点1. 用 SQLite 记住歌单以前写播放器最烦的就是每次打开都要重新添加歌曲。这次我用 SQLite 做了持久化。数据库里只建了一张表字段就一个path歌曲路径。m_db QSqlDatabase::addDatabase(“QSQLITE”);m_db.setDatabaseName(“musicdatabase.db”);if (!m_db.open()) {qDebug() “数据库打开失败:” m_db.lastError().text();return;}initTable();建表也很简单query.exec(“CREATE TABLE IF NOT EXISTS songlist(path VARCHAR(200) PRIMARY KEY)”);这里 path 是主键所以同一首歌不会重复插入。添加歌曲的时候用 INSERT OR IGNOREquery.prepare(“INSERT OR IGNORE INTO songlist(path) VALUES(?)”);query.addBindValue(path);return query.exec();这样就算用户手滑选了同一首歌也不会重复添加。2. 歌词解析放到线程里歌词文件虽然不大但为了不让界面卡顿我还是把它放到了 QThread 里。LyricThread 继承自 QThread重写 run()在里面读文件、解析时间戳和歌词。歌词格式一般是这样的[00:28.28]一生要走多远的路程解析核心代码大概是这样QStringList parts line.split(‘]’);QStringList timeParts parts[0].split(‘:’);int minutes timeParts[0].mid(1).toInt();double seconds timeParts[1].toDouble();qint64 timestamp qRound64((minutes * 60 seconds) * 1000);QString lyricText parts[1].trimmed();if (!lyricText.isEmpty()) {m_lyrics.insert(timestamp, lyricText);}这里用 QMapqint64, QString 存歌词键是毫秒时间戳值是歌词文本。QMap 会自动按时间排序后面根据播放进度找当前歌词就很方便。另外还做了编码处理先按 UTF-8 解码如果发现乱码再尝试 GBK 或 GB18030。这个在中文歌词文件里挺实用的。3. 歌词同步靠定时器歌词高亮不是播放器自动给的而是我自己用 QTimer 定时查。每 100 毫秒查一次当前播放位置然后找到对应的歌词行。connect(lyricsTimer, QTimer::timeout,this, MusicPlayer::updateLyricsOnTime);高亮逻辑大概是这样qint64 position player-position();int row ui-lyricsWidget-currentRow();QList times m_lyrics.keys();while (row times.size() - 1 position times[row 1]) {row;}while (row 0 position times[row]) {row–;}如果行号变了就高亮新行并滚动到中间QListWidgetItem *item ui-lyricsWidget-item(row);ui-lyricsWidget-setCurrentItem(item);ui-lyricsWidget-scrollToItem(item, QAbstractItemView::PositionAtCenter);4. 信号槽把控件和功能串起来Qt 的信号槽真的挺方便。这个项目里按钮、播放器、播放列表、歌词线程、定时器基本都靠信号槽连接。connect(ui-playButton, QPushButton::clicked,this, MusicPlayer::onPlay);connect(lyricThread, LyricThread::lyricsLoaded,this, MusicPlayer::onLyricsLoaded);connect(lyricsTimer, QTimer::timeout,this, MusicPlayer::updateLyricsOnTime);播放列表切换歌曲时也要更新歌名、歌手和歌词connect(playlist, QMediaPlaylist::currentMediaChanged,this, MusicPlayer::onCurrentMediaChanged);5. 添加歌曲时顺手去重添加歌曲的时候我先检查播放列表里有没有这个路径有就跳过没有才添加。playlist-addMedia(QUrl::fromLocalFile(file));ui-playlistWidget-addItem(fileInfo.fileName());dbHelper.addSong(file);数据库那边也有主键和 INSERT OR IGNORE 兜底所以重复添加这个问题基本不用担心。6. 启动时恢复歌单程序启动时从数据库里读路径然后只加载那些文件还存在的歌曲QStringList list dbHelper.getSongList();for (QString path : list) {if (QFile::exists(path)) {playlist-addMedia(QUrl::fromLocalFile(path));ui-playlistWidget-addItem(QFileInfo(path).fileName());}}这样就算有些歌被删了或者移动了也不会显示一堆无效项。四、踩过的小坑做这个项目也遇到了一些小问题比如添加重复歌曲后来在播放列表和数据库两边都做了去重拖动进度条时和播放进度更新打架用 isSliderDown() 判断用户是不是正在拖歌词文件编码乱码先 UTF-8再 GBK/GB18030歌词文件不存在线程也会发信号界面显示“未找到歌词文件”播放出错在状态栏提示不反复操作播放器避免异常。这些问题都不算特别难但解决之后确实对 Qt 的理解更实在了。五、最后来个 200 字总结这次用 Qt 做音乐播放器最大的收获不是写了多少代码而是把信号槽、数据库、多线程、定时器这些知识串起来了。播放控制靠 QMediaPlayer 和 QMediaPlaylist歌单持久化靠 SQLite歌词解析放到 QThread 里界面不卡歌词高亮用 QTimer 每 100 毫秒查一次播放位置。过程中也踩了坑比如重复添加、歌词编码、拖动进度条冲突等。虽然项目不大但麻雀虽小五脏俱全对 Qt 的理解更实在了。以后还想加封面、搜索、在线歌词让它更像一个完整播放器。#职坐标 #AI转型 #学习分享。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →