开发动机
因为之前下载到本地磁盘的音频文件因版权问题不能在所用播放器上播放, 又舍不得删掉那几百首周董的歌, 加上之前又用过Qt,索性就自己做一个本地的音乐播放器好了。
开发环境
win10、Qt5.9.3、Qt Creator4.4.1
效果图
大致效果如下:
图片都是上酷狗扣的,着实有点随便/(ㄒoㄒ)/~~。
添加界面:
部分代码
如下:
底部控件布局
//初始化函数
void BottomFrame::init()
{
/*************************这里是BottomFrame的整体配置*********************************/
//设置底部固定高度
setFixedHeight(120);
setStyleSheet(QString("QFrame{Background:rgb(255, 255, 255, 0);"
"background-color:rgba(0,0,0,0);"
"border-color:rgb(225,225,225);"
"border-width:0px;border-style: none;}")); //solid
//设置鼠标追踪
setMouseTracking(true);
/*************************这里是成员变量的初始化*********************************/
//需要一个垂直布局
vLayout = new QVBoxLayout(this);
//需要一个水平布局
hLayout = new QHBoxLayout;
//设置布局与控件的左右边距
//hLayout->setMargin(1);
//音频播放列表
mdPlayList = new QMediaPlaylist(this);
//mdPlayList->addMedia(QUrl::fromLocalFile("D:\\QTapplication\\KuGouMusicPlayer\\mp3\\yequ.mp3"));
//设置播放模式(顺序播放,单曲循环,随机播放等)
mdPlayList->setPlaybackMode(QMediaPlaylist::Loop);
//playlist->setCurrentIndex(1);
my_player = new QMediaPlayer(this);
my_player->setPlaylist(mdPlayList);
my_player->setVolume(50);
//时间歌曲名框架
timeAndNameWidget = new QWidget(this);
timeAndNameWidget->setFixedSize(96, 64);
//时间歌曲名布局
timeAndNameLayout = new QVBoxLayout();
//音乐时间
songTime = new MyButton();
songTime->setParent(timeAndNameWidget);
//时长按钮初始化
songTime->setText("-/-");
//滚动字幕
scrollText = new MyScrollLabel();
scrollText->setParent(timeAndNameWidget);
//将这两个控件添加到时间歌曲名布局中去
timeAndNameLayout->addWidget(scrollText);
timeAndNameLayout->addWidget(songTime);
timeAndNameWidget->setLayout(timeAndNameLayout);
//上一曲
pre = new MyButton(BEFORE_IMAGE_PATH, BEFORE_PRESSED_IMAGE_PATH);
pre->setParent(this);
pre->setToolTip("上一曲");
//下面是代替方案
// pre = new QPushButton(this);
// //设置按钮大小
// pre->setFixedSize(BUTTON_WIDTH32, BUTTON_HEIGHT32);
// //设置按钮图标
// pre->setIcon(QIcon(BEFORE_PRESSED_IMAGE_PATH));
// //设置图标大小
// pre->setIconSize(pre->size());
// //pre->setStyleSheet("QPushButton{Background:rgba(255, 255, 255, 50)}");
// //设置默认提示
// //设置鼠标放在按钮上时的图标
// pre->setCursor(Qt::PointingHandCursor);
//播放
play = new MyButton(PLAY_IMAGE_PATH, PLAY_PRESSED_IMAGE_PATH);
play->setParent(this);
play->setToolTip("播放");
//下一曲
next = new MyButton(NEXT_IMAGE_PATH, NEXT_PRESSED_IMAGE_PATH);
next->setParent(this);
next->setToolTip("下一曲");
//音量
voice = new MyButton(VOICE_IMAGE_PATH, VOICE_PRESSED_IMAGE_PATH);
voice->setParent(this);
voice->setToolTip("音量");
//音乐列表
list = new MyButton(LIST_IMAGE_PATH, LIST_PRESSED_IMAGE_PATH);
list->setParent(this);
list->setToolTip("列表");
//音量滑动块
voiceSlider = new QSlider(Qt::Horizontal);
//设置滑动块大小
voiceSlider->resize(32, 32);
//隐藏滑动块关闭
//voiceSlider->setWindowFlag(Qt::FramelessWindowHint);
//设置滑动条控件的最小值
voiceSlider->setMinimum(0);
//设置滑动条控件的最大值
voiceSlider->setMaximum(100);
//设置滑动条控件的值:my_player的音量大小
voiceSlider->setValue(my_player->volume());
//设置滑动条样式
voiceSlider->setStyleSheet(voiceSliderStyle);
//提升:优化滑动块样式
//自定义滑动块风格在configfile.h中
//设置为自定义样式
//音乐进度滑动块
songSlider = new QSlider(Qt::Horizontal, this);
songSlider->setValue(0);
//滑动块进度为当前音乐播放进度
songSlider->setValue(my_player->position());
//设置样式
songSlider->setStyleSheet(songSliderStyle);
/************************这里进行信号连接******************************************************/
//连接上一曲信号,点击上一曲时,TopFrame中songList中的item也要相应改变。
//自定义上一曲按钮信号,点击时触发,并携带上一首歌曲索引
connect(pre, QPushButton::clicked, this, BottomFrame::getPreClicked);
//连接播放play按钮信号
connect(play, QPushButton::clicked, this, BottomFrame::getPlayOrPauseClicked);
//连接下一曲信号
//也可声明槽函数代替lambda表达式
connect(next, QPushButton::clicked, this, BottomFrame::getNextClicked);
//滑动随音乐进度改变
connect(my_player, QMediaPlayer::positionChanged, this, BottomFrame::updateSongSliderPosition);
//切换歌曲时,进度条总长度随歌曲总时长改变
connect(my_player, QMediaPlayer::durationChanged, this, BottomFrame::updateSongSliderDuration);
//移动滑动块调整音乐进度, 会有卡顿. 所以这里使用slideerMoved信号而不是valueChanged信号
connect(songSlider, QSlider::sliderMoved, this, BottomFrame::updateMyPlayerPosition);
//音量滑动块生改变时,my_player音量随之改变
connect(voiceSlider, QSlider::valueChanged, [&](){
my_player->setVolume(voiceSlider->value());
});
//播放完毕自动切换下一首
connect(my_player, QMediaPlayer::mediaChanged, this, [&]{
emit BottomFrame::nextClickedSignal(mdPlayList->currentIndex());
});
/***********************这里设计BottomFrame中控件的布局**************************************/
//将按钮添加到水平布局中
hLayout->addStretch(0.7);
hLayout->addSpacing(15);
//hLayout->addStretch(0.7);//
//hLayout->addSpacing(20);
hLayout->addWidget(timeAndNameWidget);
hLayout->addStretch(0.7);
hLayout->addStretch(1);
hLayout->addSpacing(25);
hLayout->addWidget(pre);
hLayout->addStretch(0.7);//
hLayout->addWidget(play);
hLayout->addStretch(0.7);//
hLayout->addWidget(next);
hLayout->addStretch(1);//
hLayout->addWidget(voice);
hLayout->addStretch(0.7);//
hLayout->addWidget(voiceSlider);
hLayout->addStretch(0.7);//
hLayout->addWidget(list);
hLayout->addStretch(0.7);//
//将布局添加到frame中
//垂直布局
vLayout->addWidget(songSlider);
vLayout->addLayout(hLayout);
//初始化音量滑动块定时器
//音量滑动块定时器
voiceTimer = new QTimer(this);
}
上部控件布局
TopFrame::TopFrame(QWidget *parent) : QFrame(parent)
{
/************这里是TopFrame整体配置****************************/
//设置固定宽度
resize(1280, 660);
setStyleSheet(QString("QFrame{Background:rgb(255, 255, 255, 0);" //第四个参数设置控件背景透明度, 0表示完全透明
"background-color:rgba(0,0,0,0);"
"border-color:rgb(225,225,225);"
"border-width:0px;border-style: solid;}"));
//topFrame 框架内的文字风格
//字体对象
QFont f;
//字体风格
f.setFamily("FangSong"); //仿宋:FangSong 宋体:SimSun
//字体大小
f.setPointSize(20);
//字体下划线
f.setUnderline(true);
/*****************这里是类成员初始化**************************************/
//水平布局:topFrame总体布局
topHlayout = new QHBoxLayout(this);
//左侧框架:歌词框架
lyricFrame = new QFrame(this);
//左侧框架水平布局
lyricHlayout = new QHBoxLayout(lyricFrame);
//左侧框架里的控件
//歌词控件
lyrics = new QLabel("暂无歌词", lyricFrame);
lyrics->setFont(f);
//lyrics->move((lyricFrame->width() - lyrics->width()) / 2, (lyricFrame->height() - lyrics->height()) / 2);
//右侧框架
listFrame = new QFrame(this);
//右侧框架需要固定尺寸
listFrame->setFixedSize(300, 660);
//右侧框架垂直布局
listVlayout = new QVBoxLayout(listFrame);
//右侧框架上半部分水平布局
listTopHlayout = new QHBoxLayout;
//右侧框架里的控件
//添加按钮
add = new MyButton(ADD_NORMAL_PATH, ADD_PRESSED_PATH);
add->setParent(listFrame);
//add->setText("添加歌曲");
//删除按钮
del = new MyButton(DELETE_NORMAL_PATH, DELETE_PRESSED_PATH);
del->setParent(listFrame);
//del->setText("删除歌曲");
//隐藏按钮
hide = new MyButton(HIDE_NORMAL_PATH, HIDE_PRESSED_PATH);
hide->setParent(listFrame);
//hide->setText("隐藏");
//歌曲列表控件
songList = new QListWidget(listFrame);
f.setPointSize(11);
songList->setFont(f);
//songList->addItem(QString("test"));
songList->resize(300, 600);
//歌曲列表滑动块风格
songList->verticalScrollBar()->setStyleSheet(songListSliderStyle);
songList->horizontalScrollBar()->setStyleSheet(songListSliderStyle);
//添加完成后默认隐藏歌曲列表
listFrame->hide();
/********************下面将控件添加到对应布局中区*****************************/
//添加到歌词框架布局中去
lyricHlayout->addStretch(1);
lyricHlayout->addWidget(lyrics);
lyricHlayout->addStretch(1);
//添加到右侧框架上半部分水平布局
listTopHlayout->addWidget(add);
listTopHlayout->addWidget(del);
//添加到右侧框架上半部分水平布局
listTopHlayout->addWidget(hide);
//将右侧框架上半部分水平布局放入右侧垂直布局中
listVlayout->addLayout(listTopHlayout);
//将歌曲列表控件添加到右侧布局中
listVlayout->addWidget(songList);
//listVlayout->addStretch(1);
/********************下面是信号连接***************************************/
//建立右侧按钮信号连接
//点击hide按钮隐藏歌曲列表songList
connect(hide, QPushButton::clicked, [&]{
listFrame->hide();
//listVlayout->addStretch(2);
});
//点击添加歌曲按钮弹出文件手动添加, 得到歌曲路径并触发addClicked 自定义信号。该信号在BottomFrame中被接收
connect(add, QPushButton::clicked, [&]{
//添加多个音频文件
//参数:父对象, 浮动框标题,默认打开路径, 过滤器
QStringList songListPath = QFileDialog::getOpenFileNames(this,QString::fromLocal8Bit("文件"), "D:\\music"); // QString::fromLocal8Bit("音频文件(*.mp3")
if (!songListPath.isEmpty())
{
for (int i = 0; i < songListPath.size(); i++)
{
QString songPath = QDir::toNativeSeparators(songListPath.at(i));
//mdPlayList->addMedia(QUrl::fromLocalFile(path));
//每得到一个路径,抛出一个信号,在mainWidget中接收
emit TopFrame::addClickedSignal(songPath);
//QString songName =path.split("\\").last();
songList->addItem((songPath.split("\\").last()).split(".").first());
}
}
});
//点击del按钮,清空歌曲列表,同时清空bottomFrame中的歌单mdPlayList
connect(del, QPushButton::clicked, [&]{
songList->clear();
emit TopFrame::delClickedSignal();
});
//双击songList中的选项时发出双击信号
connect(songList, QListWidget::itemDoubleClicked, this, getItemDoubleClicked);
//最后将左右两个框架添加到总体水平布局中
topHlayout->addWidget(lyricFrame);
topHlayout->addWidget(listFrame);
}
部分功能实现
播放/暂停
void BottomFrame::getPlayOrPauseClicked()
{
if (mdPlayList->isEmpty())
{
qDebug() << "歌曲列表为空,请添加歌曲!";
return;
}
//测试:歌曲长度
//qDebug() << my_player->duration();
//qDebug() << my_player->bufferStatus();
if (my_player->state() == 0 || my_player->state() == 2) //播放停止状态 或 播放暂停状态
{
play->setIcon(QIcon(STOP_IMAGE_PATH));
play->setIconSize(play->size());
play->setToolTip("播放");
//qDebug() << my_player->position();
//my_player->setPosition(my_player->position());
my_player->play();
//更新歌曲信息
emit BottomFrame::updateSongInfo(mdPlayList->currentIndex());
}
else if (my_player->state() == 1) //播放状态
{
play->setIcon(QIcon(PLAY_PRESSED_IMAGE_PATH));
play->setIconSize(play->size());
play->setToolTip("暂停");
my_player->pause();
}
}
上一曲
void BottomFrame::getPreClicked()
{
//得到当前列表歌曲数量
//int songNum = mdPlayList->mediaCount();
//得到当前播放歌曲索引
int curIndex = mdPlayList->currentIndex();
//判断当前播放歌曲是否是第一首
if (--curIndex < 0)
{
//是则播放第一首
curIndex = 0;
}
//不是则播放上一首
//设置列表当前音乐索引
mdPlayList->setCurrentIndex(curIndex);
//播放当前索引音乐
//如果当前播播放状态为未播放,下一曲后设置播放按钮图标
if (my_player->state() == 0 || my_player->state() == 2)
{
//qDebug() << "something";
play->setIcon(QIcon(STOP_PRESSED_IMAGE_PATH)); //emit
play->setIconSize(play->size());
//play->setToolTip("暂停");
}
//播放歌曲
my_player->play();
//最后手动触发自定义上一曲按钮信号
emit BottomFrame::preClickedSignal(curIndex);
}
下一曲
void BottomFrame::getNextClicked()
{
//获取当前list歌曲数量
int songNum = mdPlayList->mediaCount();
//获得当前播放歌曲索引
int curIndex = mdPlayList->currentIndex();
//判断当前播放歌曲是否是最后一首
if (++curIndex >= songNum)
{
//是则回到第一首
curIndex = 0;
}
//不是则播放下一曲
//设置当前音乐
mdPlayList->setCurrentIndex(curIndex);
//滚动到当前音乐
//如果当前播播放状态为未播放,下一曲后设置播放按钮图标
if (my_player->state() == 0 || my_player->state() == 2)
{
play->setIcon(QIcon(STOP_IMAGE_PATH));
play->setIconSize(play->size());
}
//播放当前音乐
my_player->play();
//最后手动触发下一曲按钮信号
//qDebug() << "BottomFrame::getNextClicked index: " << curIndex;
emit BottomFrame::nextClickedSignal(curIndex);
}
播放进度控制
//切换歌曲时触发,更新当前播放歌曲播放时间
void BottomFrame::updateSongSliderDuration(qint64 duration)
{
//测试:当前歌曲总时长
//qDebug() << "歌曲总时长:" << duration;
//更改进度条
songSlider->setRange(0,duration);//根据播放时长来设置滑块的范围
songSlider->setEnabled(duration>0);
songSlider->setPageStep(duration/1000);//以及每一步的步数,一秒一步
//更改底部时长按钮的内容QString songTime
songTime->setText("00:00/" + getSongTime(duration));
}
//接收歌曲位置改变,那么滑块的位置也要变
void BottomFrame::updateSongSliderPosition(qint64 position)
{
songSlider->setValue(position);
//显示当前时间进度songTime
QString curPostion = getSongTime(position);
//歌曲总时长
QString duration = getSongTime(my_player->duration());
//更新底部时长按钮文本
songTime->setText(curPostion + "/" + duration);
}
//移动滑动块,改变歌曲进度
void BottomFrame::updateMyPlayerPosition(int value)
{//前面设置了setRange(0,duration),所以得到的value是ms,毫秒
//防止卡顿先暂停歌曲
my_player->pause();
//setPosition(), 设置当前播放位置
my_player->setPosition(value);
//然后更新底部时长按钮
//歌曲总时长
QString duration = getSongTime(my_player->duration());
//显示当前时间进度songTime
QString curPostion = getSongTime(value);
//更新底部时长按钮文本
songTime->setText(curPostion + "/" + duration);
//设置完后继续播放
my_player->play();
}
篇幅有限,仅展示部分主要模块代码。
后面会有完整代码下载地址。
总结
已实现功能:歌曲添加/删除、歌曲列表显示/隐藏、播放/暂停、上/下一曲、音量/播放进度控制、播放信息显示等。
因最初的想法就是播放自己本地的音频文件的内存不大的程序,所以搜索、下载等内存需求较大的网络模块部分暂不考虑(各位可以按需添加)。此外一些其他的像模式切换、更换背景等扩展功能也不在此添加(因为抠图太费眼睛了[😂]),这些均可通过Qt提供的类库实现(QMediaPlayer实现播放模式选择、QPushButton按钮实现背景选择)。
源码下载
百度网盘:
链接:https://pan.baidu.com/s/1rDH3dshRVxXRnlMvV3zPfQ
提取码:yyoy