欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Qt 音乐播放器项目

Qt 音乐播放器项目

2025/4/13 0:53:13 来源:https://blog.csdn.net/Suinnn/article/details/147029452  浏览:    关键词:Qt 音乐播放器项目

具体代码见:https://gitee.com/Suinnnnnn/MusicPlayer

文章目录

  • 0. 预备
  • 1. 界面
    • 1.1 各部位长度
    • 1.2 ui文件
    • 1.3 窗口前置设置
    • 1.4 设置QSS
  • 2. 自定义控件
    • 2.1 按钮
    • 2.2 推荐页面
    • 2.3 CommonPage
    • 2.4 滑杆
  • 3. 音乐管理
  • 4. 歌词界面
    • 4.1 ui文件
    • 4.2 LrcPage.h文件
  • 5. 音乐播放控制 + 持久化控制

0. 预备

各个类的作用:

ButtonForm: 自定义了按钮,其包含了图片,文字和动画效果
CommonPage:自定义页面,被“我的”之后的三个页面使用
CommonPageItem:歌曲的每一行就是一个该类的对象,包括编号,歌曲名,专辑名,时间,是否喜欢
CommonSlider:音乐进度条使用,表明当前播放到哪里,支持seek功能
CustomPlaylist:简单实现了在Qt6中删除的QMediaPlaylist
LrcPage:歌词页面
Music:用户直接交互的页面
MusicInfo:保存歌曲信息的类
MusicList:组织MusicInfo的类
RecBoxItemRecommendBox中的一个个单元
RecommendBox:推荐界面的简单实现
VolumBox:音量控制条,支持seek功能

1. 界面

1.1 各部位长度

image-20250303202055668

1.2 ui文件

image-20250303202127812

image-20250303202311424

1.3 窗口前置设置

music.cpp如下

#include "music.h"
#include <QGraphicsDropShadowEffect>
#include <QMouseEvent>
#include "ui_music.h"Music::Music(QWidget* parent): QWidget(parent), ui(new Ui::Music)
{ui->setupUi(this);initUI();
}void Music::initUI()
{// 设置无边框this->setWindowFlag(Qt::FramelessWindowHint);// 设置窗体透明,不然看不到阴影效果this->setAttribute(Qt::WA_TranslucentBackground);// 添加阴影效果QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect(this);shadowEffect->setOffset(0, 0);           // 设置阴影距离shadowEffect->setColor(QColor(0, 0, 0)); // 设置阴影颜色shadowEffect->setBlurRadius(100);         // 设置阴影模糊半径this->setGraphicsEffect(shadowEffect);   // 给 QWidget设置阴影
}void Music::mousePressEvent(QMouseEvent* event)
{// 如果按下左键,记录dragPosif (event->button() == Qt::LeftButton) {QPoint p1     = event->globalPosition().toPoint();QPoint p2     = geometry().topLeft();this->dragPos = p1 - p2;// qDebug() << dragPos;return;}// 其余事件让父类处理QWidget::mousePressEvent(event);
}void Music::mouseMoveEvent(QMouseEvent* event)
{// 如果按下左键并移动,那么就让窗口移动if (event->buttons() == Qt::LeftButton) {QPoint p1 = event->globalPosition().toPoint();QPoint p2 = this->dragPos;this->move(p1 - p2);// qDebug() << "dragPos: " << p2 << "\nglobalPos: " << p1;return;}// 其余事件让父类处理QWidget::mouseMoveEvent(event);
}Music::~Music()
{// qDebug() << "Music::~Music() called";delete ui;
}void Music::on_close_btn_clicked()
{// 关闭窗口this->close();
}

1.4 设置QSS

widget.ui中删除部分空间的背景颜色,设置QSS。

2. 自定义控件

2.1 按钮

自定义一个ButtonForm,由几个QWidgetQLabel组成
image-20250307223547243

buttonform.h

#ifndef BUTTONFORM_H
#define BUTTONFORM_H#include <QFrame>
#include <QPropertyAnimation>
namespace Ui
{
class ButtonForm;
}class ButtonForm : public QFrame
{Q_OBJECT
public:explicit ButtonForm(QWidget* parent = nullptr);void set_id_and_icon_and_text(int id, const QString& icon, const QString& text);void clear_backgrond_color() const;int  get_page_id() const;void show_animation();void hide_animation();~ButtonForm();
protected:void mousePressEvent(QMouseEvent* event);private:void                set_animation_and_start(QPropertyAnimation* animation, int duration,QRect rect1, QRect rect2, QRect rect3, int loopCount);Ui::ButtonForm*     ui;int                 pageID;          // 当前页面的ID, 用于翻页QPropertyAnimation* line1_animation; // 动画QPropertyAnimation* line2_animation;QPropertyAnimation* line3_animation;QPropertyAnimation* line4_animation;QPropertyAnimation* line5_animation;
signals:void clicked(int pageID);
};#endif // BUTTONFORM_H

接着将music.ui中的左侧QWidget替换为ButtonForm

运行结果如下,点击不同的按钮可以跳转到不同的QWidget,且按钮有动画效果

image-20250313214533199

2.2 推荐页面

music.ui中的recommend_page下添加一个QScrollArea类型的scrollArea,里面有三个QLabel和两个RecommendBox

image-20250313214119841

RecommendBox.ui如下

image-20250313214155057

RecommendBox.h

#ifndef RECOMMENDBOX_H
#define RECOMMENDBOX_H#include <qjsonarray.h>
#include <QWidget>namespace Ui
{
class RecommendBox;
}class RecommendBox : public QWidget
{Q_OBJECTpublic:explicit RecommendBox(QWidget* parent = nullptr);void init_rec_box(QJsonArray pics);void set_col_and_row(int row, int col);~RecommendBox();private slots:void on_left_btn_clicked();void on_right_btn_clicked();
private:void              add_rec_item();void              del_rec_item();Ui::RecommendBox* ui;int               row           = 2;int               column        = 5;int               current_group = 0; // 当前是第几组int               group_count;       // 一共有多少组QJsonArray        pics_path_and_text;
};#endif // RECOMMENDBOX_H

RecBoxItem.ui如下

image-20250313214254346

RecBoxItem.h如下

#ifndef RECBOXITEM_H
#define RECBOXITEM_H#include <QPropertyAnimation>
#include <QWidget>namespace Ui
{
class RecBoxItem;
}class RecBoxItem : public QWidget
{Q_OBJECTpublic:explicit RecBoxItem(QWidget* parent = nullptr);bool eventFilter(QObject* watched, QEvent* event);void set_icon_and_text(const QString& icon, const QString& text);~RecBoxItem();private:Ui::RecBoxItem*                                                         ui;std::function<void(QPropertyAnimation*, int, QRect, QRect, QRect, int)> set_animation_and_start;
};#endif // RECBOXITEM_H

运行结果如下,支持换页,而且选中图片会有动画效果

image-20250313214420275

2.3 CommonPage

由于需要上传文件,所以现在把之前的我的播客改为上传音乐

image-20250318201535927

CommonPage被播客, 我喜欢, 最近播放, 上传音乐所使用。
下面是CommonPage.ui

image-20250318201704598

下面是CommonPage.h(目前)

#ifndef COMMONPAGE_H
#define COMMONPAGE_H#include <QFrame>namespace Ui
{
class CommonPage;
}class CommonPage : public QFrame
{Q_OBJECTpublic:explicit CommonPage(QWidget* parent = nullptr);void init_my_like();void init_podcast();void init_recent_play();void init_upload_music();~CommonPage();
private:void            set_podcast();void            reset_up_widget(const QString& title = "xxx");Ui::CommonPage* ui;
};#endif // COMMONPAGE_H

CommonPage使用了CommonPageItem,下面是CommonPageItem.ui

image-20250318201815958

CommonPageItem.h如下

#ifndef COMMONPAGEITEM_H
#define COMMONPAGEITEM_H
#include <QFrame>namespace Ui
{
class CommonPageItem;
}class CommonPageItem : public QFrame
{Q_OBJECTpublic:explicit CommonPageItem(QWidget* parent = nullptr);const QRect& get_page_item_geometry() const;void         set_num(int num);void         enterEvent(QEnterEvent* event);void         leaveEvent(QEvent* event);void         mouseDoubleClickEvent(QMouseEvent* event);~CommonPageItem();private:void                set_background_color(const QString& color);bool                is_played() const;int                 play_num = -1;Ui::CommonPageItem* ui;
};#endif // COMMONPAGEITEM_H

2.4 滑杆

CommonSlider.ui,音乐进度条使用

image-20250318202343565

CommonSlider.h

#ifndef COMMONSLIDER_H
#define COMMONSLIDER_H#include <QFrame>namespace Ui
{
class CommonSlider;
}/* 愿意是想让进度条和音量条都用该类,后来发现不如再写一个类。此类仅用于进度条 */
class CommonSlider : public QFrame
{Q_OBJECTpublic:explicit CommonSlider(QWidget* parent = nullptr);void init_progress_bar();void init_volume_btn();~CommonSlider();private:Ui::CommonSlider* ui;
};#endif // COMMONSLIDER_H

VolumeBox.ui,音量按钮使用

image-20250318202432773

VolumeBox.h

#ifndef VOLUMEBOX_H
#define VOLUMEBOX_H#include <QFrame>namespace Ui
{
class VolumeBox;
}class VolumeBox : public QFrame
{Q_OBJECTpublic:explicit VolumeBox(QWidget* parent = nullptr);~VolumeBox();
private:Ui::VolumeBox* ui;
};#endif // VOLUMEBOX_H

music.cpp中给音量按钮添加事件过滤器

bool Music::eventFilter(QObject* obj, QEvent* event)
{if (obj == ui->play_volume_btn || obj == volume_box) {if (event->type() == QEvent::Enter) {// 鼠标进入时显示 volume_boxQPoint btnPos    = ui->play_volume_btn->mapToGlobal(QPoint(0, 0)); // 获取全局坐标QPoint targetPos = btnPos - QPoint(10, volume_box->height());      // 按钮上方volume_box->move(targetPos);volume_box->show();} else if (event->type() == QEvent::Leave) {// 鼠标离开时隐藏 VolumeBox (如果超过了300ms)hide_timer->start(300);}}return QWidget::eventFilter(obj, event);
}

3. 音乐管理

设置此按钮的槽函数

image-20250318202730525

点击后需要添加我们选中的文件,使用QFileDialog。使用MusicInfo类保存所有音乐的信息。使用MusicList类来管理这些Music

MusicInfo.h,储存音乐信息

#ifndef MUSICINFO_H
#define MUSICINFO_H#include <QString>
#include <QUrl>
#include <QUuid>class MusicInfo
{
public:MusicInfo(const QUrl& url);// 禁止拷贝(根据需要实现移动语义)MusicInfo(const MusicInfo&)            = delete;MusicInfo& operator=(const MusicInfo&) = delete;/* 必须声明移动构造函数,作用如下* 1. 明确告知编译器生成移动构造函数* 2. 允许容器使用移动语义操作对象* 3. 保留类的不可拷贝特性* */MusicInfo(MusicInfo&&)            = default;MusicInfo& operator=(MusicInfo&&) = default;bool       is_valid();void       parse_metadata();QUuid      getUuid() const;bool    getIs_like() const;void    setIs_like(bool newIs_like);bool    getIs_recent_play() const;void    setIs_recent_play(bool newIs_recent_play);bool    getIs_valid_flag() const;void    setIs_valid_flag(bool newIs_valid_flag);QString getMusic_name() const;QString getAlbum_name() const;qint64  getMusic_time() const;private:void    check_valid();QUuid   uuid;                   // uuid, 防止重复QUrl    url;                    // 歌曲的URLQString music_name;             // 音乐名称QString album_name;             // 专辑名称qint64  music_time;             // 音乐持续时间(ms)bool    is_like        = false; // 是否喜欢bool    is_recent_play = false; // 是否是最近播放bool    is_valid_flag  = false; // 是否是音频文件
};#endif                              // MUSICINFO_H

MusicList.h,管理一个个MusicInfo

#ifndef MUSICINFO_H
#define MUSICINFO_H#include <QString>
#include <QUrl>
#include <QUuid>class MusicInfo
{
public:MusicInfo(const QUrl& url);// 禁止拷贝(根据需要实现移动语义)MusicInfo(const MusicInfo&)            = delete;MusicInfo& operator=(const MusicInfo&) = delete;/* 必须声明移动构造函数,作用如下* 1. 明确告知编译器生成移动构造函数* 2. 允许容器使用移动语义操作对象* 3. 保留类的不可拷贝特性* */MusicInfo(MusicInfo&&)            = default;MusicInfo& operator=(MusicInfo&&) = default;bool       is_valid();void       parse_metadata();QUuid      getUuid() const;bool    getIs_like() const;void    setIs_like(bool newIs_like);bool    getIs_recent_play() const;void    setIs_recent_play(bool newIs_recent_play);bool    getIs_valid_flag() const;void    setIs_valid_flag(bool newIs_valid_flag);QString getMusic_name() const;QString getAlbum_name() const;qint64  getMusic_time() const;private:void    check_valid();QUuid   uuid;                   // uuid, 防止重复QUrl    url;                    // 歌曲的URLQString music_name;             // 音乐名称QString album_name;             // 专辑名称qint64  music_time;             // 音乐持续时间(ms)bool    is_like        = false; // 是否喜欢bool    is_recent_play = false; // 是否是最近播放bool    is_valid_flag  = false; // 是否是音频文件
};#endif                              // MUSICINFO_H

Music.h中增加处理点击爱心按钮的槽函数

void handle_item_like_btn_clicked(bool is_like, const QUuid& uuid);

4. 歌词界面

解析.lrc文件

4.1 ui文件

image-20250406194553897

4.2 LrcPage.h文件

#ifndef LRCPAGE_H
#define LRCPAGE_H
#include <QFrame>
#include <QPropertyAnimation>namespace Ui
{
class LrcPage;
}// 歌词的一行
struct LyricLine {LyricLine(qint64 time_p, QString text_p): time(time_p), text(text_p){}qint64  time; // 时间(ms)QString text; // 歌词
};class LrcPage : public QFrame
{Q_OBJECTpublic:explicit LrcPage(QWidget* parent = nullptr);bool    parse_lrc_file(const QString& lrc_filename);void    push_line_to_lrc_vector(qint64 time, QString text);QString get_lrc_filename_by_url(const QUrl& url);void    set_ui_label(qint64 time);void    set_name_and_singer(const QString& music, const QString& singer);void    clear_lyric_lines();~LrcPage();
private slots:void on_quit_clicked();
private:int                 get_lyric_lines_index_by_time(qint64 time);QString             get_lyric_lines_line_by_index(int index);Ui::LrcPage*        ui;QPropertyAnimation* lrc_animation; // 歌词界面隐藏动画QVector<LyricLine>  lyric_lines;   // 歌词的所有内容,由一行行的结构体组成
};#endif                                 // LRCPAGE_H

5. 音乐播放控制 + 持久化控制

使用QMediaPlayer类,由于Qt6中删除了QPlayList类,所以我自定义了一个CustomPlayList

#ifndef CUSTOMPLAYLIST_H
#define CUSTOMPLAYLIST_H#include <QList>
#include <QMediaPlayer>
#include <QObject>
#include <QRandomGenerator>
#include <QUrl>class CustomPlaylist : public QObject
{Q_OBJECTpublic:enum PlaybackMode {Sequential, // 顺序播放SingleLoop, // 单曲循环播放Random      // 随机播放};Q_ENUM(PlaybackMode)explicit CustomPlaylist(QMediaPlayer* player, QObject* parent = nullptr);// 基本操作void addMedia(const QUrl& url);void removeMedia(int index);void clear();void next();void previous();void setCurrentIndex(int index);// 获取信息int  currentIndex() const;QUrl currentMedia() const;int  mediaCount() const;// 播放模式PlaybackMode playbackMode() const;void         setPlaybackMode(PlaybackMode mode);signals:void currentIndexChanged(int index);void mediaAdded(int index);void mediaRemoved(int index);void playbackModeChanged(PlaybackMode mode);private slots:void handleMediaStatusChanged(QMediaPlayer::MediaStatus status);private:QList<QUrl>   m_mediaList;         // 存储媒体项int           m_currentIndex = -1; // 当前播放索引PlaybackMode  m_playbackMode = Sequential;QMediaPlayer* m_player;            // 关联的 QMediaPlayerQList<int>    m_randomOrder;       // 随机播放顺序缓存void updateRandomOrder();          // 更新随机播放顺序int  getNextIndex() const;         // 根据模式获取下一个索引int  getPreviousIndex() const;     // 根据模式获取上一个索引
};
#endif                                 // CUSTOMPLAYLIST_H

使用QSQLITE数据库,用于持久化操作,下面是music.h文件

#ifndef MUSIC_H
#define MUSIC_H#include <QAudioOutput>
#include <QJsonArray>
#include <QMediaPlayer>
#include <QPropertyAnimation>
#include <QSqlDatabase>
#include <QWidget>
#include "CommonPage.h"
#include "CustomPlaylist.h"
#include "LrcPage.h"
#include "VolumeBox.h"QT_BEGIN_NAMESPACE
namespace Ui
{
class Music;
}
QT_END_NAMESPACEclass Music : public QWidget
{Q_OBJECTpublic:Music(QWidget* parent = nullptr);void       initUI();void       initPlayer();void       initStaticToolTip();void       initSqlLite();void       initMusicList();void       connect_signals_and_slots();void       mousePressEvent(QMouseEvent* event);void       mouseMoveEvent(QMouseEvent* event);bool       eventFilter(QObject* obj, QEvent* event);void       play_music_from_index(CommonPage* page, int index);QJsonArray get_random_recommend_pic() const;~Music();
private slots:// 形如on_xxx是ui文件生成的, 其余是自己写的void handle_button_form_clicked(int pageID);void handle_item_like_btn_clicked(bool is_like, const QUuid& uuid);void handle_upload_music();void handle_play_state_changed(QMediaPlayer::PlaybackState new_state);void handle_play_mode_changed(CustomPlaylist::PlaybackMode mode);void handle_play_all_music_btn_clicked(CommonPage* page);void handle_double_clicked_song(CommonPage* page, int index);void handle_current_index_changed(int new_index);void handle_volume_changed(int volume);void handle_duration_changed(qint64 duration);void handle_position_changed(qint64 duration);void handle_slider_pos_changed(double ratio);void handle_meta_data_changed();void on_close_btn_clicked();void on_stop_btn_clicked();void on_next_song_btn_clicked();void on_prev_song_btn_clicked();void on_play_mode_btn_clicked();void on_play_volume_btn_clicked();void on_my_like_btn_clicked();void on_lrc_btn_clicked();void on_music_pic_btn_clicked();void on_min_btn_clicked();
private:void                      hide_volume_box();static QString            transform_million_second(qint64 msecond);void                      set_like_btn_icon(bool is_like);MusicList::const_iterator get_now_play_music_info_by_index(int index);void                      quit_progress();
private:Ui::Music* ui;// 由于窗口没有了默认边框,所以要想移动,需要有该属性,其值为 全局的鼠标位置-widget左上位置QPoint              dragPos;VolumeBox*          volume_box;QTimer*             hide_timer;QMediaPlayer*       player;CustomPlaylist*     play_list;QAudioOutput*       audio_output;CommonPage*         play_page;       // 当前播放bool                is_mute = false; // 是否静音, false表示没被静音qint64              total_duration;  // 当前音乐的总时长LrcPage*            lrc_page;        // 歌词页面QPropertyAnimation* lrc_animation;   // 歌词界面显示动画QSqlDatabase        db;              // 数据库驱动
};
#endif                                   // MUSIC_H

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词