嵌入式 QT 基于mplayer的音乐播放器

news2025/4/5 5:40:35

1、实现功能

2、音乐播放界面

2.1 界面程序 

2.1.1 界面控制初始化

2.1.2 控件风格程序

3、 歌曲列表界面

3.1.1 在 widget.h 定义 QListWidge 对象指针

3.1.2 在 memberInit 函数中添加 QlistWidge 初始化

3.1.3 在 setMusicPlayStyle 函数中设置其风格

4、音乐播放功能的实现

4.1 获取本地歌曲

4.1.1 显示歌曲列表

5、Qprocess 实现 MPlayer

5.1 QT使用进程

5.2  QProcess 调用 MPlayer 方法

6、Mplayer命令大全

7、实现播放和暂停功能 

7.1 播放器初始化

7.2 播放函数实现

7.3 播放按钮功能实现

8、获取总时间和当前时间

8.1 在 MaplayerInit 函数中添加相关槽函数

8.2 在开始播放槽函数发送获取总时间命令

8.3 在获取数据槽函数中获取总时间和当前时间

8.4 在定时器槽函数中发送获取当前时间命令

8.5  在 playMusic 函数中启动定时器

8.6 在 updatePlayPauseBtnStateSlot 函数中添加定时器开关

9、双击播放功能

9.1 关联信号与槽

9.2 编写槽函数

10、音量设置

10.1 关联信号与槽

10.2 实现槽函数

11、歌曲切换

11.1 播放模式设置

11.1.1 使用枚举定义播放模式(类内定义枚举)

11.1.2 关联信号与槽

11.1.3 实现槽函数

11.2 上一曲

11.2.1 关联信号与槽

11.2.2 实现槽函数

11.2.3 槽函数发送自定义播放内容

11.2.4 关联自定义信号与槽

11.2.5 实现自定义的信号与槽函数,更新显示播放内容

11.3 自动切换

11.3.1  关联槽函数

11.3.2  实现槽函数

12、快进快退

12.1 关联拖动滑块信号与槽

12.2 实现拖动滑块信号与槽

12.3 关联松开滑块信号与槽

12.4 实现松开滑块信号与槽

13、隐藏播放器

13.1 关联槽函数

13.2 实现槽函数

14、保存歌曲

14.1 定义Settings对象指针

14.2 添加列表中保存歌曲

14.3 加载保存歌曲

15、删除歌曲


1、实现功能

音乐播放所实现的功能有:
1 ) 播放 / 暂停功能,
2 ) 上一首 / 下一首,
3 ) 设置音量,
4 ) 设置播放模式,
5 ) 双击歌曲列表的歌曲名可以播放音乐,
6 ) 播放音乐的时候可以显示要播放的歌曲名。
7 ) 显示当前播放的时间以及当前音乐的总时间。
8 ) 滑动改变音乐的播放进度

2、音乐播放界面

   其界面的布局是采用 ui 界面进行布局,没有使用程序代码进行布局的,在程序中,当然也可以使用 程序代码界面进行布局,此处部分是 ui 程序代码布局,播放列表使用程序布局。

2.1 界面程序 

2.1.1 界面控制初始化

//控件初始化
void Widget::memberInit()
{
 QIcon scanIcon(QPixmap(":/music/resource/player/player_icon/open.png"));
 QIcon palyIcon(QPixmap(":/music/resource/player/player_icon/list.png"));
 ui->scanLocalFileBtn->setIconSize(QSize(40,40));
 ui->scanLocalFileBtn->setIcon(scanIcon);
 ui->showPlayListBtn->setIconSize(QSize(40,40));
 ui->showPlayListBtn->setIcon(palyIcon);
 QIcon sequenceIcon(QPixmap(":/music/resource/player/player_icon/列表循
环.png"));
 QIcon onceIcon(QPixmap(":/music/resource/player/player_icon/单曲循环.png"));
 QIcon rangeIcon(QPixmap(":/music/resource/player/player_icon/随机播放.png"));
 QIcon onceSignle(QPixmap(":/music/resource/player/player_icon/单曲一次.png"));
 QIcon stepPlay(QPixmap(":/music/resource/player/player_icon/顺序播放.png"));
 ui->playModelCbox->addItem(stepPlay,"顺序播放");
 ui->playModelCbox->addItem(onceSignle,"单曲一次");
 ui->playModelCbox->addItem(onceIcon,"单曲循环");
 ui->playModelCbox->addItem(sequenceIcon,"循环播放");
 ui->playModelCbox->addItem(rangeIcon,"随机播放");
 //音量滑条的范围 40~100 声音的范围(测试 40 以上有声音)
 ui->volumeSlider->setRange(0,100);
 //设置音量滑条的初始值为 70
ui->volumeSlider->setValue(50);
}

2.1.2 控件风格程序

void Widget::setMusicPlayStyle()
{
//设置播放器背景图片 通过调色板实现
 QString musicBack = ":/music/resource/player/播放器背景.png";
 playerPalette.setBrush(QPalette::Background,QBrush(QPixmap(musicBack)));
 this->setPalette(playerPalette);
 this->setAutoFillBackground(true);
 //上一首
 ui->preSongBtn->setStyleSheet("border-image:
url(:/music/resource/player/player_icon/prev.png);"
 "border-radius:10px;"
 );
 //下一首
 ui->nextSongBtn->setStyleSheet("border-image:
url(:/music/resource/player/player_icon/next.png);"
 "border-radius:10px;"
 );
 //播放
 ui->playBtn->setStyleSheet("border-image:
url(:/music/resource/player/player_icon/play.png);"
 "border-radius:10px;"
 );
 //播放模式
 ui->playModelCbox->setStyleSheet("background: url(:/music/resource/player/播放
器背景.png);"
 "border: 1px solid gray;"
 "border-radius:5px;"
 "min-width: 6em;"
 "font: 15px;"
 "color: #FFFFFF;"
 );
 //改变播放滑条和音量滑条的样式表
 
ui->playProgressSlider->setStyleSheet("QSlider::groove:horizontal{border:0px;height
:4px;}"
 "QSlider::subpage:horizontal{background:red;}"
 "QSlider::addpage:horizontal{background:lightgray;}"
 
"QSlider::handle:horizontal{background:white;width:10px;border-radius:5px;margin:-
3px 0px -3px 0px;}");
ui->volumeSlider->setStyleSheet( "QSlider::groove:horizontal{border:0px;height:4px;
}"
 "QSlider::subpage:horizontal{background:red;}"
 "QSlider::addpage:horizontal{background:lightgray;}"
 
"QSlider::handle:horizontal{background:white;width:10px;border-radius:5px;margin:-
3px 0px -3px 0px;}");
 //扫描歌曲 播放列表 返回 关闭 透明
 ui->scanLocalFileBtn->setStyleSheet("background: transparent;"
 "border: 1px solid gray;"
 "border-radius:6px;"
 "color: #FFFFFF;"
 "font:16px;"
 "min-width: 4em;"
 );
 ui->showPlayListBtn->setStyleSheet("background: transparent;"
 "border: 1px solid gray;"
 "border-radius:6px;"
 "color: #FFFFFF;"
 "font:16px;"
 "min-width: 4em;"
 );
 ui->closeBtn->setStyleSheet("background: transparent;"
 "border: 1px solid gray;"
 "border-radius:6px;"
 "color: #FFFFFF;"
 "font:16px;"
 "min-width: 4em;"
 );
 ui->blackBtn->setStyleSheet("background: transparent;"
 "border: 1px solid gray;"
 "border-radius:6px;"
 "color: #FFFFFF;"
 "font:16px;"
 "min-width: 4em;"
 );
 //歌曲名
 ui->songNameLb->setStyleSheet("font:18px;""color:#FFFFFF;");

3、 歌曲列表界面

 使用程序创建 QListWidget 控件来显示歌曲列表。

3.1.1 widget.h 定义 QListWidge 对象指针

QListWidget *showPlayList;

3.1.2 memberInit 函数中添加 QlistWidge 初始化

 //显示列表初始化
 showPlayList = new QListWidget; //列表显示 --通过程序创建控制
 showPlayList->setMinimumSize(400,500);
 showPlayList->setMaximumSize(400,500);
 showPlayList->setWindowTitle("播放列表");
 showPlayList->setWindowIcon(QIcon(QPixmap(":/music/resource/image/music.png")));
 //设置显示列表显示的位置跟音乐部件的显示位置同一个高度,在音乐部件的后面,x 坐
标为音乐部件的宽度
 this->move(500,300);
 showPlayList->move(this->width()+510,this->y());
playListBtnState = true; //false 隐藏,true 显示
 showPlayList->show();

3.1.3 setMusicPlayStyle 函数中设置其风格

 //设置显示列表的背景
 showPlayList->setStyleSheet("QListWidget{color:#D4D4D4;"
 "font:17px;"
 "backgroundimage:url(:/music/resource/player/playerlist/播放器列表背景.png);}");

4、音乐播放功能的实现

  因为是基于 Linux 平台下实现 Qt 的音乐播放,如果利用 Qt 现有的提供的多媒体播放器 QMediaPlayer 进行实现的话, 只能是用于 Windows 平台下的实现,是不支持 Linux 平台下, 因为 Qt5 多媒体播放使用QMediaPlayer,而 QMediaPlayer 使用 gstreamer 后端来播放媒体, gstreamer 又不能移植到 arm 。 所以最后决 定在 Qt5 上,使用 QProcess 直接调 MPlayer 的方式来实现音乐播放功能。 在这里将列举出该程序中使用到的核心功能。

4.1 获取本地歌曲

/****************************************************
* 函数原型:void getLocalFileSots()
* 函数功能:获取本地音乐文件槽函数
*****************************************************/
void Widget::getLocalFileSots()
{
 //获取选择的文件目录名
 QString fileNameDir = QFileDialog::getExistingDirectory(this,"选择音乐文件
","../music");
 if(fileNameDir.isEmpty())
 {
 return;
 }
 //对目录进行扫描操作,需要目录类,利用目录路径构造目录类对象
 //1.利用 带路径的目录名 构造目录类对象
 QDir dir(fileNameDir);
 //2.编写 过滤的文件格式
 QStringList formatList;
 formatList << "*.mp3" << "*.mp4" << "*.wma";
 //3. 创建文件信息列表
 //QFileInfoList infoList;
 //4. 按照过滤的文件格式扫描文件,选择符合格式的文件
 infoList = dir.entryInfoList(formatList);
 if(infoList.isEmpty())
 {
 return;
 }
 //显示列表显示歌名的时候,前面加显示图标,显示图标的位置
 QIcon musicMap(QPixmap(":/music/resource/player/playerlist/音符 1.png"));
 for(int i = 0; i < infoList.count(); i++)
 {
 //需要检测是否重复
 //媒体播放内容列表需要带路径的文件名
 playListString << infoList[i].filePath();
 //显示给用户的列表只需要显示文件名,并在显示列表前面加图标
 QListWidgetItem *songNameList = new
QListWidgetItem(musicMap,infoList[i].fileName());
songNameList->setSizeHint(QSize(400,40));
 showPlayList->addItem(songNameList);
 //打印文件路径 和文件名
 //qDebug() << infoList[i].fileName(); //不带路径的文件名
 //qDebug() << infoList[i].filePath(); //带路径的文件名
 }
 showPlayList->setCurrentRow(0);
}

4.1.1 显示歌曲列表

/****************************************************dddddd
* 函数原型:void updatePlayFileListBtnStateSlots()
* 函数功能:更新显示文件列表的状态
* 该显示文件列表要么显示,要么被隐藏
* 说明:
* playListBtnState == true 显示列表
* playListBtnState == false 隐藏列表
*****************************************************/
void Widget::updatePlayFileListBtnStateSlots()
{
 //跟以前状态相反
 playListBtnState = !playListBtnState;
 QIcon showIcon(QPixmap(":/music/resource/player/player_icon/列表.png"));
 QIcon hideIcon(QPixmap(":/music/resource/player/player_icon/隐藏列表.png"));
 switch (playListBtnState)
 {
 case true:
 showPlayList->show(); //显示
 ui->showPlayListBtn->setIcon(hideIcon);
 break;
 case false:
 showPlayList->hide(); //隐藏
 ui->showPlayListBtn->setIcon(showIcon);
 break;
 }
}

5、Qprocess 实现 MPlayer

5.1 QT使用进程

 程序:

Widget::Widget(QWidget *parent) :
 QWidget(parent),
 ui(new Ui::Widget)
{
 ui->setupUi(this);
 myProcess = new QProcess(this);
 connect(myProcess,SIGNAL(readyRead()),this,SLOT(doSlotProcessReadyRead()));
}

void Widget::on_btnExec_clicked()
{
 QString cmd = ui->lEditCmd->text();
 QString arg = ui->lEditArg->text();
 QStringList arrayArg = arg.split(" "); //获取所有的参数
 myProcess->start(cmd,arrayArg);
}

void Widget::doSlotProcessReadyRead(){
 while(!myProcess->atEnd()){
 //readLine 返回的是 QByteArray,转为 QString ,replace("\n","")去掉\n
 QString str = QString(myProcess->readLine()).replace("\n","");
 ui->textEdit->append(str);
 }
}

5.2  QProcess 调用 MPlayer 方法

我们只需要 new 一个 QProcess, 然后通过 QProcess start 方法启动 mplayer 线程即可.如下:
process = new QProcess();
QStringList args;
args << "-slave"; // 使用 slave 模式
args << "-quiet"; // 不要输出冗余信息
_process->start("mplayer", args); // 启动该进程,并传入参数 args

 注意:这里的 mplayer 需要添加绝对路径, 也就是你当前 mplayer 所存放的位置

然后接下来这样,便可以启动 mplayer 。如果我们要控制 mplaer 呢,使用 QProcess write 方法,往 mplayer 写入命令即可,比如如下:
process->write("pause\n");

 如果要读取 mplayer 发出来的消息呢,使用 QProcess readLine 方法,方式如下:

_process->readLine(data,200);

 这样便可以读取一行 mplayer 发送出来的信息,然后我们只需要解析这个 data 即可。

6、Mplayer命令大全

loadfile name     // 加载媒体文件 name
volume 100 1    // 设置音量,中间位音量的大小 0-100
seek 50 1          // 设置进度, 50 代表进度大小 ( 百分比 ) 0-100 //0 is a relative seek of +/- <value>                  seconds (default).
//1 is a seek to <value> % in the movie.
//2 is a seek to an absolute position of <value> seconds.
mute 1/0 // 静音开关
pause // 播放 / 暂停
get_time_length // 获取播放文件的总时间,以秒为单位
seek value // 定位, valu 为秒数
get_percent_pos // 获取播放的百分比
get_time_pos // 获取当前播放的位置,以秒为单位
get_file_name // 获取当前播放的媒体文件名
get_meta_album // 获取专辑
get_meta_artist // 获取艺术家
get_meta_comment // 获取评论
get_meta_genre // 获取流派
get_meta_title // 获取标题
get_meta_year // 获取年份

7、实现播放和暂停功能 

7.1 播放器初始化

在头文件 musicplay 中,添加相关成员的声明之后,然后在源文件中实现,首先是对播放器的初始化如下:
//初始化播放器
void Widget::MaplayerInit()
{
 //创建播放进程
 playProcess = new QProcess(this);
 playProcess->setProcessChannelMode(QProcess::MergedChannels);
 program = "/usr/bin/mplayer"; //X86 架构下
 //program = "/usr/local/bin/mplayer"; //mplayer 命令路径,一定要正确,不然无法
播放 –ARM 架构
 playState = false; //播放状态
 playBtnState = false; //播放按钮状态
playIndex = 0; //播放内容下标,跟显示列表的内容要保持一致
}

7.2 播放函数实现

void Widget::playMusic()
{
 //如果没有加载播放内容
 playProcess->close();
 args.clear();
 args << "-slave"; //使用 slave 模式
 args << "-quiet"; //不要输出冗余信息
 args << playListString[playIndex];
 qDebug() << " playListString[playIndex]: " << playListString[playIndex];
 QString str = "正在播放: " + showPlayList->currentItem()->text();
 ui->songNameLb->setText(str); //显示正在播放的音乐
playProcess->start(program,args); //开始播放
 playProcess->write("volume 70 1\n");
playState = true;
}

7.3 播放按钮功能实现

/****************************************************
* 函数原型:void updatePlayPauseBtnStateSlot()
* 函数功能:更新播放暂停按钮状态
*****************************************************/
void Widget::updatePlayPauseBtnStateSlot()
{
 playBtnState = !playBtnState;
 switch(playBtnState)
 {
 case true:
 if(!playState) {
 playMusic();
 }
 else
 {
 playProcess->write("pause\n");
 }
 ui->playBtn->setStyleSheet("border-image:
url(:/music/resource/player/player_icon/暂停.png);"
 "border-radius:10px;"
 );
 break;
 case false:
 playProcess->write("pause\n");
 ui->playBtn->setStyleSheet("border-image:
url(:/music/resource/player/player_icon/播放.png);"
 "border-radius:10px;"
 );
 break;
}
}

8、获取总时间和当前时间

8.1 MaplayerInit 函数中添加相关槽函数

//初始化播放器
void Widget::MaplayerInit()
{
 //关联相关的信号,进程启动成功,出错,有数据可读等
connect(playProcess,SIGNAL(started()),this,SLOT(onStartedSlot()));
connect(playProcess,SIGNAL(error(QProcess::ProcessError)),this,SLOT(onErrorSlot()))
;
connect(playProcess,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadDataSlot()));
connect(playProcess,SIGNAL(finished(int)),this,SLOT(onPlayProcessFinishedSlot()));
//创建定时器--定时器的主要功能, 就是用于定时获取当前音乐的播放时间, 然后进行
转换为当前的播放时间
timer = new QTimer(this);
 connect(timer,SIGNAL(timeout()),this,SLOT(onTimeoutSlot()));
}

8.2 在开始播放槽函数发送获取总时间命令

void Widget::onStartedSlot()
{
 qDebug() << "准备播放";
 //获取播放文件的长度以毫秒为单位
 playProcess->write("get_time_length\n");
}

8.3 在获取数据槽函数中获取总时间和当前时间

void Widget::onReadDataSlot()
{
 while (playProcess->bytesAvailable()) {
 QByteArray arr = playProcess->readLine();
 QString str = QString::fromLocal8Bit(arr);
 if(str.contains("ANS_LENGTH"))
 {
 //获取时间总长度
 qDebug() << "::"<<str;
 str.remove("\n");
 str.remove("\r");
 str.remove("ANS_LENGTH=");
 qDebug() << "str = " << str ;
 int t = str.toDouble();
 int m = t / 60;
 int s = t % 60;
 tlength = m * 60 + s;
 qDebug() << "tlength " << tlength;
 //设置播放进度的总数值
 ui->playProgressSlider->setMinimum(0);
 ui->playProgressSlider->setMaximum(tlength);
 ui->playProgressSlider->setSingleStep(1);
 QString timeLength;
 timeLength.sprintf("%02d:%02d",m,s);
 ui->totleTimeLb->setText(timeLength);
 }

if(str.contains("ANS_TIME_POSITION") )
 {
 //获取当前时间
 //qDebug() << "当前时间:: " << str ;
 str.remove("\n");
 str.remove("\r");
 double t = QString(str.split("=").at(1)).toDouble();
 //显示当前时间
 int m = t / 60;
 int s = (int)(t+0.5) % 60; //四舍五入防止少时间
 QString timePos;
 timePos.sprintf("%02d:%02d",m,s);
 ui->currentTimeLb->setText(timePos);
 int now = (m * 60 + s);
 //double slider = now *100 /tlength;
 //设置播放进度当前滑条的值
 ui->playProgressSlider->setValue(now);
 }
 }
}

8.4 在定时器槽函数中发送获取当前时间命令

void Widget::onTimeoutSlot()
{
 //获取当前播放的位置,以秒为单位
 playProcess->write("get_time_pos\n");
}

8.5  playMusic 函数中启动定时器

void Widget::playMusic()
{
 …..
 // 定时器开启
timer->start(100);
}

8.6 updatePlayPauseBtnStateSlot 函数中添加定时器开关

void Widget::updatePlayPauseBtnStateSlot()
{
 playBtnState = !playBtnState;
 switch(playBtnState)
 {
 case true:
 if(!playState) {
 playMusic(); //第一次播放
 }
 else
 {
 playProcess->write("pause\n");
timer->start(); →暂停后再次播放时启动定时器
 }
…
 break;
 case false:
 playProcess->write("pause\n");
 timer->stop();→暂停播放时关闭定时器
 ….
 break;
}}

9、双击播放功能

9.1 关联信号与槽

//显示列表用户双击信号关联指定音乐文件的槽函数
connect(showPlayList,SIGNAL(doubleClicked(QModelIndex)),this,SLOT(doubleClickedToPl
ayerMusicSlots()));

9.2 编写槽函数

/****************************************************
* 函数原型:void loadMusicToPlayerSlots()
* 函数功能:加载指定音乐到播放列表槽函数
* 用户通过双击选择需要播放的音乐
*****************************************************/
void Widget::doubleClickedToPlayerMusicSlots()
{
 //获取显示列表的当前行
 int row = showPlayList->currentRow();
 playIndex = row;
 playState = false; //双击之后需要播放音乐
 playBtnState = false; //无论之前是什么状态,双击之后必须播放
 updatePlayPauseBtnStateSlot(); //更新播放暂停按钮—实现播放
}

10、音量设置

10.1 关联信号与槽

//音量改变信号
connect(ui->volumeSlider,SIGNAL(sliderMoved(int)),this,SLOT(setVolumeSlot(int)));

10.2 实现槽函数

void Widget::setVolumeSlot(int volume)
{
 QString str = QString("volume ");
 if(playProcess->isOpen())
 {
 QString buf;
 buf.setNum(volume);
str += buf + " 1\n";
 //playProcess->write("volume %d 1\n",volume);
 playProcess->write(str.toLocal8Bit());
 }
}

11、歌曲切换

11.1 播放模式设置

11.1.1 使用枚举定义播放模式(类内定义枚举)

public:
 typedef enum
 {
 ListPlay = 0, //顺序播放
 SingleSongOnce = 1, //单曲一次
 SingleSongLoop = 2, //单曲循环
 PlayListLoop = 3, //列表循环
 RandomPlay = 4, //随机播放
}PlayModel;

11.1.2 关联信号与槽

//模式选择关联 更新播放按钮槽函数
connect(ui->playModelCbox,SIGNAL(currentIndexChanged(int)),this,SLOT(updatePlayMode
Slots(int)));

11.1.3 实现槽函数

/****************************************************
* 函数原型:void updatePlayModeSlots(int index)
* 函数功能:更新播放模式,获取用户选择的模式
* 用户选择的模式跟系统的模式顺序关联
*****************************************************/
void Widget::updatePlayModeSlots(int index)
{
 switch (index)
 {
 case 0:
 //顺序播放
 playModel = ListPlay;
 break;
 case 1:
 //单曲一次
 playModel = SingleSongOnce;
 break;
 case 2:
 //单曲循环
 playModel = SingleSongLoop;
 break;
case 3:
 //循环播放
 playModel = PlayListLoop;
 break;
 case 4:
 //随机播放
 playModel = RandomPlay;
 break;
 }
}

11.2 上一曲

11.2.1 关联信号与槽

//上一首
connect(ui->preSongBtn,SIGNAL(clicked(bool)),this,SLOT(playPrevSongSlot()));

11.2.2 实现槽函数

void Widget::playPrevSongSlot()
{
if(playIndex > 0)
 {
 if(playModel == RandomPlay) //随机播放
 {
 qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()));//获取时间因子
 playIndex = qrand() %(playListString.count()-1); //产生
playListString.count()-1 以内的随机数
 }
 else
 {
 playIndex--;
 }
 playState = false; //点击上一首之后需要播放音乐
 playBtnState = false; //无论之前是什么状态,点击上一首之后必须播放
 updatePlayPauseBtnStateSlot();
 //发送播放内容改变信号
 emit playConcentChanged(playIndex);
 }else{//回到最后一首
 if(playModel == RandomPlay) //随机播放
 {
 qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()));//获取时间因子
 playIndex = qrand() %(playListString.count()-1); //产生
playListString.count()-1 以内的随机数
 }
 else
 {
 playIndex=playListString.count()-1;
}
 playState = false; //点击上一首之后需要播放音乐
 playBtnState = false; //无论之前是什么状态,点击上一首之后必须播放
 updatePlayPauseBtnStateSlot();
 //发送播放内容改变信号
 emit playConcentChanged(playIndex);
 }

11.2.3 槽函数发送自定义播放内容

signals:
 void playConcentChanged(int index); //播放内容改变信号,播放内容改变,发送改变的
下标

11.2.4 关联自定义信号与槽

//媒体内容列表行改变(下一首、上一首、一首歌播放结束) 关联 显示列表行改变槽函数
connect(this,SIGNAL(playConcentChanged(int)),this,SLOT(updateShowPlayListRow(int)))
;
//双击当前行 播放内容改变(下一首、上一首、一首歌播放结束) 歌曲名更新
connect(showPlayList,SIGNAL(doubleClicked(QModelIndex)),this,SLOT(updateSongTitleSl
ot()));

11.2.5 实现自定义的信号与槽函数,更新显示播放内容

/****************************************************
* 函数原型:void updateShowPlayListRow(int row)
* 函数功能:更新显示列表行的槽函数
* 根据播放内容列表的行改变更新显示列表的行
* 播放内容列表 0 行对应播放列表第一行
*****************************************************/
void Widget::updateShowPlayListRow(int row)
{
 showPlayList->setCurrentRow(row+1);
 //更新歌曲标题
 //内容列表 改变, 歌曲名更新
 updateSongTitleSlot();
}
//内容列表 改变, 歌曲名更新槽函数
void Widget::updateSongTitleSlot()
{
 //当前项的文本
 QString name = showPlayList->currentItem()->text();
 QString str = "正在播放: " + name;
 ui->songNameLb->setText(str);
}

下一首原理一样。

11.3 自动切换

当一首歌曲播放完成之后,要根据当前的播放模式进行相应的设置。

11.3.1  关联槽函数

connect(playProcess,SIGNAL(finished(int)),this,SLOT(onPlayProcessFinishedSlot()));

11.3.2  实现槽函数

void Widget::onPlayProcessFinishedSlot()
{
 //定时器关闭
 timer->stop();
 playBtnState = false;
 //更新播放暂停按钮
 switch(playBtnState)
{
 case true:
 ui->playBtn->setStyleSheet("border-image:
url(:/music/resource/player/player_icon/暂停.png);"
 "border-radius:10px;"
 );
 break;
 case false:
 ui->playBtn->setStyleSheet("border-image:
url(:/music/resource/player/player_icon/播放.png);"
 "border-radius:10px;"
 );
 break;
 }
 if(ui->playProgressSlider->value() != ui->playProgressSlider->maximum())
 {
 return;
 }
 //如果 if 语句不成立,则说明,一首歌曲播放完成,而不是播放到一半点击下一首
 //qDebug() << "一首歌曲播放完成.......";
 //一首歌曲播放完成之后,根据播放模式选择要播放的音乐
 switch (playModel)
 {
 case ListPlay: //顺序播
 if(playIndex == playListString.count()-1)
 {
 //播放滑条清空。。。。。。
 ui->playProgressSlider->setValue(0);
 playState = false; //播放当前播放的内容,则停止播放
 ui->songNameLb->setText("无歌曲");//显示无歌曲名
 //当前时间和总共时间清零
 ui->currentTimeLb->setText("00:00");
 ui->totleTimeLb->setText("00:00");
 //如果点击播放,默认播放的是第一首歌曲
 playIndex = 0;
 //设置当前行为第行
 showPlayList->setCurrentRow(playIndex);
 return;
 }
 playIndex++;
 break;
 case SingleSongOnce: //单曲一次
 //播放当前播放的内容,则停止播放
 ui->playProgressSlider->setValue(0);
ui->songNameLb->setText("无歌曲");//显示无歌曲名
 //当前时间和总共时间清零
 ui->currentTimeLb->setText("00:00");
 ui->totleTimeLb->setText("00:00");
 playState = false; //点击播放的时候,可以继续以当前的内容进行播放
 return;
 case SingleSongLoop: //单曲循环
 break;
 case PlayListLoop: //循环播放
 if(playIndex == playListString.count()-1)
 {
 playIndex = 0;
 }
 playIndex++;
 break;
 case RandomPlay: //随机播放
 qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()));//获取时间因子
 playIndex = qrand() %(playListString.count()-1);
 break;
 }
 playState = false; //点击下一首之后需要播放音乐
 playBtnState = false; //无论之前是什么状态,点击下一首之后必须播放
 updatePlayPauseBtnStateSlot();
 //发送播放内容改变信号
emit playConcentChanged(playIndex);
}

12、快进快退

12.1 关联拖动滑块信号与槽

//快进快退拖动槽函数
connect(ui->playProgressSlider,SIGNAL(sliderMoved(int)),this,SLOT(updateSongSeekSlo
t(int)));

12.2 实现拖动滑块信号与槽

void Widget::updateSongSeekSlot(int seek)
{
disconnect(playProcess,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadDataSl
ot()));//断开读取数据的槽,避免拖动时重新设置了滑块的值
 moveSliderVal = seek; //记录滑块的值
}

12.3 关联松开滑块信号与槽

//快进快退松开槽函数
connect(ui->playProgressSlider,SIGNAL(sliderReleased()),this,SLOT(updateSongSeekRel
easedSlot()));

12.4 实现松开滑块信号与槽

void Widget::updateSongSeekReleasedSlot()
{
 qDebug()<< "Released";
 int seek = (moveSliderVal * 100) / tlength;//百分比的进度
 playProcess->write("pause\n");
 QString str = QString("seek ");
 if(playProcess->isOpen())
 {
 QString buf;
 buf.setNum(seek);
 str += buf + " 1\n";
 playProcess->write(str.toLocal8Bit());
} 
connect(playProcess,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadDataSlot()));
重新连接读取数据的槽
}
14.13 关闭音乐 
1. 关闭事件
void Widget::closeEvent(QCloseEvent *event)
{
 //点击部件的叉号的时候,会自动调用关闭事件函数
 //在关闭事件函数里面做一次释放
 //进程关闭
 playProcess->close();
 //清空音乐显示列表
 showPlayList->clear();
 //清空播放内容列表
 playListString.clear();
 //显示列表部件关闭
 showPlayList->close();
 //发送销毁信号
 qDebug() << "关闭程序。。。。。。";
 //emit musicDestroyed(true);
}

13、隐藏播放器

13.1 关联槽函数

connect(ui->hideBtn,SIGNAL(clicked(bool)),this,SLOT(hideMusicPlayerSlots()));

13.2 实现槽函数

void Widget::hideMusicPlayerSlots()
{
 //设置显示列表状态
 playListBtnState = true;
 //更新显示列表状态
 updatePlayFileListBtnStateSlots();
this->hide();
}

14、保存歌曲

用户对应用程序经常有这样的要求:要求它能记住它的 settings ,比如窗口大小,位置,一些别的设置,还有 一个经常用的,就是 recent files ,等等这些都可以通过 Qsettings 来实现。
当我们创建一个 Qsettings 的对象时,我们需要传递给它两个参数,第一个是你公司或者组织的名称,第二个事你的应用程序的名称。

14.1 定义Settings对象指针

QSettings *configIniWrite; //配置文件写指针
QSettings *configIniRead; //配置文件读指针
QSettings *configIniDelete; //配置文件删除指针

14.2 添加列表中保存歌曲

void Widget::getLocalFileSots()
{
…
configIniWrite = new QSettings("PlayList.ini", QSettings::IniFormat);
 for(int i = 0; i < infoList.count(); i++)
 {
 …
 //保存当前歌曲
 configIniWrite->setValue("/song/"+ QString::number(i), 
infoList[i].filePath());//保存歌曲列表
… 
 }
 showPlayList->setCurrentRow(0);
 playListBtnState = true;
showPlayList->show();
}

14.3 加载保存歌曲

打开列表保存的歌曲 写一个函数,在构造函数中调用
void Widget::loadPlayListSong()
{
 //打开用户保存的歌曲列表
 configIniRead = new QSettings("PlayList.ini", QSettings::IniFormat);
QIcon musicMap(QPixmap(":/music/resource/player/playerlist/音符 1.png"));
 for(int i=0; ; ++i) //将所有的播放列表显示在列表控件中
 {
//100 是写进的值,所以返回的值也是 100
 if(configIniRead->value("/song/"+QString::number(i),100).toInt()!=100)
 {
 QString path =
configIniRead->value("/song/"+QString::number(i)).toString();
 //媒体播放内容列表需要带路径的文件名
 playListString << path;
 QFileInfo fileInfo;
 fileInfo = QFileInfo(path);
 // QString fileName = fileInfo.fileName();//文件名带后缀
 QString fileName = fileInfo.baseName();//文件名不带后缀
 // QString fileSuffix = fileInfo.suffix();//文件后缀
 //显示给用户的列表只需要显示文件名,并在显示列表前面加图标
 QListWidgetItem *songNameList = new
QListWidgetItem(musicMap,fileName);
 songNameList->setSizeHint(QSize(400,40));
 showPlayList->addItem(songNameList);
 }
 else
 {
 break;
 }
 }
 showPlayList->setCurrentRow(0);
}

15、删除歌曲

void Widget::deleteSongBtnSlots()
{
 configIniDelete = new QSettings("PlayList.ini",QSettings::IniFormat);//打开播放
列表配置文件
 int currentRow = showPlayList->currentRow();
 if( currentRow >= 0)
 {
 for (int i=currentRow; ; i++) //删除配置文件的内容
 {
 if(configIniDelete->value("/song/"+QString::number(i+1),100).toInt() !=
100) //如果下一行存在播放文件
 {
 
configIniDelete->setValue("/song/"+QString::number(i),configIniDelete->value("/song
/"+QString::number(i+1)).toString());
}
 else
 {
 configIniDelete->remove("/song/"+QString::number(i));
 break;
 }
 }
 QListWidgetItem* item = showPlayList->takeItem(currentRow); //删除显示列表的
内容
 delete item;
 showPlayList->update(); //更新显示
 playListString.removeAt(currentRow); //删除播放列表的内容
}
}

完成!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/567976.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第四章 程序的控制结构

文章目录 第四章 程序的控制结构4.1 程序的三种控制结构4.1.1 程序流程图4.1.2 程序控制结构基础4.1.3 程序控制结构扩展 4.2 程序的多分支结构4.2.1 单分支结构&#xff1a;if4.2.2 二分支结构&#xff1a;if-else4.2.3 多分支结构&#xff1a;if-elif-else4.2.4 判断条件及组…

Github Copilot AI配对开发者编程,提升项目建设进度

Github Copilot是什么&#xff1f; GitHub Copilot 是结对编程的虚拟版本。结对编程是一种常见的敏捷软件开发技术 —— 即两个开发人员在同一个项目上并肩协作&#xff0c;轮流编写代码并检查合作伙伴的输出。 Copilot 可以支持十几种语言&#xff0c;与 Python、JavaScript、…

一文揭秘高效稳定的 Apache Doris 内存管理机制

作者&#xff1a;SelectDB 高级研发工程师、Apache Doris Committer 邹新一 背景 Apache Doris 作为基于 MPP 架构的 OLAP 数据库&#xff0c;数据从磁盘加载到内存后&#xff0c;会在算子间流式传递并计算&#xff0c;在内存中存储计算的中间结果&#xff0c;这种方式减少了频…

Vue2创建脚手架小案例

Vue CLI是一个官方提供的命令行工具&#xff0c;用于快速创建Vue.js项目和管理项目依赖项。下面是使用Vue CLI创建Vue.js项目的基本步骤&#xff1a; 首先&#xff0c;确保已安装Node.js和npm包管理器。可以在终端输入以下命令来检查它们的版本&#xff1a; node -v npm -v如…

MySQL备份

MySQL的备份方式有哪几种&#xff1f;分别如何实现&#xff1f; 目录 一、数据的备份类型 1、数据的备份类型根据其自身的特性主要分为以下几组&#xff1a; 二、MySQL备份数据的方式 三、常见的备份工具 1、一般情况下, 我们需要备份的数据分为以下几种 2、备份工具 3…

【2023 · CANN训练营第一季】昇腾AI入门课(TensorFlow)第三章——AI应用开发

1.具备编程经验 本课程中的示例代码、练习涉及C&C语言、Python语言的如下基础知识&#xff0c;建议您在学习本课程前先学习这部分内容 1.C&C语言 a.变量、基本数据类型、指针、引用、const限定符等 b&#xff0c;字符串和数组 c.表达式&#xff0c;包括赋值运算、条件…

回收站中怎么找回误删除的文件?这几种方法很实用

当我们在电脑上操作文件的时候&#xff0c;难免会有不小心删除文件的情况发生。这个时候&#xff0c;我们可以打开回收站来找回误删除的文件。但是&#xff0c;有时候我们也会误将回收站清空。那么&#xff0c;该怎样才能找回已经误删除的文件呢&#xff1f;在这里提供了回收站…

免费搭建个人stable-diffusion绘画(干货教程)

目前AI绘画主流的模型有Midjourney、Stable Diffusion、DALLE&#xff0c;最火的当属Midjorney和Stable Diffusion&#xff0c;但是由于Midjourney没有开源且要付费&#xff0c;我今天主要分享Stable Diffusion的部署和使用方法的分享。 大家应该面对的一个不可避免的问题&…

kubernetes-informer机制

一、概念 informer 是 client-go 中的核心工具包&#xff0c;在kubernetes中&#xff0c;各个组件通过HTTP协议跟 API Server 进行通信。如果各组件每次都直接和API Server 进行交互&#xff0c;会给API Server 和ETCD造成非常大的压力。在不依赖任何中间件的情况下&#xff0…

Git简单使用介绍

Git作用 版本控制&#xff08;版本迭代&#xff09;&#xff0c;多人开发&#xff0c;没有版本控制&#xff0c;每修改一下文件就需要备份 常用的版本控制器&#xff1a;Git 和SVN 主要区别&#xff1a; SVN是集中式版本控制系统&#xff0c;版本库是集中放在中央服务器的&a…

半导体芯片划片机怎么使用

使用半导体芯片划片机的方法如下&#xff1a; 准备工作&#xff1a;清洁设备&#xff0c;核对晶圆数量和批次信息&#xff0c;确保晶圆完好无破损。 粘贴晶圆片&#xff1a;将待切割的晶圆片粘贴到蓝膜上&#xff0c;并将蓝膜框架放入划片机。 划片开始&#xff1a;实时清除划…

想劝大家别去外包,干了5年,彻底废了......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近5年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落&#xff01; 而我已经在一个企业干了四…

什么是SQL Server 审核

IT 安全专业人员需要定期监视和审核 SQL Server 中的所有活动&#xff0c;以确保关键数据的完整性和机密性不会受到损害。手动监视服务器上的所有这些活动是一个忙碌的过程。为了使管理员的工作更轻松一些&#xff0c;Microsoft 提供了 SQL Server 审核功能来帮助管理员有效地审…

vue不同开发方式引用vue文件讲解

上面这个图是vue构建后dist目录的文件&#xff0c;这里面包含了各种开发环境所需要的vue文件 一、开发环境和生产环境 主要分为带有prod和不带prod的版本&#xff1b; 带有prod为生产环境版本并进行了代码压缩&#xff0c;没有运行中不会有警告、错误等详细的提示信息 不带pr…

《程序员面试金典(第6版)》面试题 02.07. 链表相交(查找节点操作,哈希表,双指针(先走n步法,交替遍历法))

题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 题目传输门&#xff1a;面试题 02.07. 链表相交 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结…

Python——openpyxl读取Excel表格(读取、单元格修改、单元格加底色)

首先python读取Excel的库有很多&#xff0c;包括xlwings&#xff0c;pandas&#xff0c;xlrd等等&#xff0c;我比较常用的是openpyxl&#xff0c;以及pandas&#xff0c;当然还有喜欢将数据量比较大的Excel转为csv格式再读取。 今天我们用openpyxl来读取excel文件&#xff0c…

【Python爬虫】urllib模块:强大的网络爬虫利器,让你轻松获取万千信息!

前言&#xff1a; Python爬虫是一种获取互联网信息的技术&#xff0c;它可以自动化地从网站上抓取数据并进行处理。Python爬虫的优点在于它可以快速地获取大量数据&#xff0c;并且可以自动化地进行数据处理和分析。在Python爬虫中&#xff0c;urllib模块是一个非常重要的模块&…

python---变量(3)

求字符串的长度 使用len来求字符串中有几个字符 字符串的拼接 此时是把a2字符串拼接到a1字符串的末尾&#xff0c;得到更大的字符串&#xff0c;对于原来的a1和a2是没有影响的&#xff01; 不能把字符串和数字混合相加&#xff01; 这个时候程序就会报错&#xff0c;不能…

深入理解Linux虚拟内存管理

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核&#xff08;一&#xff09; 深入理解 Linux 内核&#xff08;二&#xff09; Linux 设备驱动程序&#xff08;一&#xff09; Linux 设备驱动程序&#xff08;二&#xff09; Linux 设备驱动程序&#xff08;三&#xf…

智慧档案馆库房环境要求中需要做到几防?

《档案馆工作通则&#xff08;国档发〔1983〕14号&#xff09;》要求档案库房必须坚固适用&#xff0c;并应具有抗震、防盗、防火、防水、防潮、防尘、防虫、防鼠、防高温、防强光等设施&#xff1b;《第13号国家档案局令《机关档案管理规定》》要求 做好防火、防盗、防紫外线、…