【基于Qt的在线音乐播放器】

基于Qt的在线音乐播放器

项目功能:

本在线音乐播放器的功能在于创建一个音乐播放器页面,可以实现搜索功能通过HTTP协议获取网络中数据并解析出来,播放搜索到的歌曲并展示相关信息。效果如图:
在这里插入图片描述

相关类及功能

  1. Musicinterface 视图类;
    主要功能:初始化窗口;设置按钮功能和窗口内容的显示;
  2. HttpHandle 网络连接类;
    主要功能:连接网络、发送请求、接收数据、用JSON解析数据;
  3. MusicPlayer 音乐播放类;
    主要功能:播放音乐、修正歌词;
  4. GraphicsView 动画展示类;
    主要功能:在窗口显示专辑图片,让唱片随音乐播放转动,音乐停止而停止;唱针随音乐播放拨动到唱片上,音乐停止归位;

Musicinterface 视图类

初始化歌曲列表窗口

void Musicinterface::initTableWidget()
{
    ui->tableWidget_musicList->setStyleSheet("selection-background-color:pink; selection-color: white;"); //设置tableWidget QSS样式表,背景为红色,字体为白色
    ui->tableWidget_musicList->hideColumn(0);	//隐藏第一列
    ui->tableWidget_musicList->hideColumn(1);	//隐藏第2列
    ui->tableWidget_musicList->setEditTriggers(QAbstractItemView::NoEditTriggers); //设置不可编辑
    ui->tableWidget_musicList->setSelectionBehavior(QAbstractItemView::SelectRows);//一次选一行
    ui->tableWidget_musicList->setFocusPolicy(Qt::NoFocus);	//去掉虚线框
    ui->tableWidget_musicList->setSortingEnabled(true);	//增加表头排序功能

    ui->tableWidget_musicList->horizontalHeader()->setFixedHeight(30);    //固定列头高度为30像素
    ui->tableWidget_musicList->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(170,170,255);}");   //设置tableWidget列头QSS样式表,背景为天蓝
    ui->tableWidget_musicList->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//表头拉伸
    ui->tableWidget_musicList->horizontalHeader()->setHighlightSections(false);       //选中列头不在高亮

    ui->tableWidget_musicList->verticalHeader()->hide();//隐藏行头
    ui->tableWidget_musicList->verticalHeader()->setHighlightSections(false);         //选中行头不在高亮
}

初始化歌词窗口

void Musicinterface::initTextEdit()
{
    QPalette palette = ui->textEdit_musicWord->palette();			//获取textEdit的调色板
    palette.setColor(QPalette::Highlight, QColor(Qt::transparent));	//将选中区域背景改为透明
    palette.setColor(QPalette::HighlightedText, QColor(Qt::red));	//将选中区域的文字改为红色
    ui->textEdit_musicWord->setPalette(palette);				//将调色板设置到textEdit
    ui->textEdit_musicWord->setReadOnly(true);				//设置为只读
    ui->textEdit_musicWord->document()->setDefaultTextOption(QTextOption(Qt::AlignHCenter));	//文字居中显示
}

Qss

界面美化也可以用Qss实现

  1. 什么是Qss:Qt样式表,他是Qt提供的一种用来自定义控件外观的机制。
  2. 控件的构成:
    1)Margin:控件最外围的透明区域,总为透明;
    2)Border:控件最外围的边框;
    3)Padding:控件的外边框内显示区域的空白区;
    4)Content:控件最内显示区;
  3. 语法规则
    示例:
border-color: rgb(255, 170, 255);
border-radius:5px;
color: rgb(170, 0, 255);
QLineEdit
{
/*边界1像素 实线 颜色rgb  或者border:none 没有边界*/
border:1px solid rgb(180, 180, 180);
//背景的颜色
background: rgb(230,230,230);
//边角4像素圆滑
border-radius: 24px;
}
/*鼠标滑动到LineEditor上面的时候*/
QLineEdit::hover
{
 //字体的颜色
  color:green
  border-color:rgb(50,480,40);
  background-color:green;
}

1)选择器:如果不设置选择器,可能会对全局所有同类型的控件生效;此项目中常用ID选择器:#myButton匹配所有ID为“myButton”的控件,ID实际上是该控件的ObjectName;

#pushButton_playNext//ID
{
border-image: url(:/Image/next1.png);
}

2)背景属性:background 一次可以设置多个属性,用空格隔开background:<color> <image> <position> <repeat>;也可以分开设置border-image: url(:/Image/next1.png);background-color: rgb(170, 170, 255);
3) 边框属性:border 一次可以设置多个属性,用空格隔开;/*边界1像素 实线 颜色rgb 或者border:none 没有边界*/ border:1px solid rgb(180, 180, 180);color边框颜色、style边框风格、image控件图片、radius设置圆角;
4)字体属性:可以直接在选择字体对话框或者属性栏填写;
5)文本属性:color:设置文本颜色;text-align:设置文本对齐方式;text-transform: 设置文本大小写;word-spacing:设置单词间距离;

  1. 伪状态
    hover: 鼠标悬停在指定控件时所使用的样式;
    pressed: 控件被鼠标按下时的样式;
    checked:控件被选中时的样式;
    unchecked:控件未被选中时的样式;
    示例:
#pushButton_playLast:pressed//被按下时的状态
{
	border-image: url(:/Image/last2.png);
}

搜索歌曲

//歌曲列表
void Musicinterface::on_lineEdit_search_returnPressed()
{
    ui->tableWidget_musicList->clearContents();		//清除表内所有数据(不包含表头数据和设置的其他属性)
    ui->tableWidget_musicList->setRowCount(0);                   //设置当前行数,为0

    QString searchData = ui->lineEdit_search->text();//获取输入搜索框的内容
    httpHandle->searchMusicList(searchData);//搜索歌曲列表
}

按钮功能设置

将按钮右键转到槽并实现该函数;

  1. 双击播放歌曲设置
//双击歌曲播放
void Musicinterface::on_tableWidget_musicList_cellDoubleClicked(int row, int column)
{
    Q_UNUSED(column)
    QString musicId = ui->tableWidget_musicList->item(row,0)->text();//获取row行0列数据
    QString musicPicId = ui->tableWidget_musicList->item(row,1)->text();//row行0列
    QString musicUrl = httpHandle->searchMusicUrl(musicId);//获取歌曲播放网址
    QString lyric = httpHandle->searchMusicLyric(musicId);//获取歌词
    QString picUrl = httpHandle->searchMusicPicUrl(musicPicId);//查找图片地址
    picUrl.replace("300y300","1200y1200");//放大图片
    QByteArray picData = httpHandle->getSearch(picUrl);
    QImage pic = QImage::fromData(picData);
    ui->graphicsView->reflushMusicPic(pic);//展示专辑图片
    ui->textEdit_musicWord->setText(musicPlayer->parsingLyrics(lyric));//显示歌词

    musicPlayer->setMedia(QUrl(musicUrl));//获取图片
    musicPlayer->play();//播放音乐
    ui->graphicsView->animationStart();//播放动画
}
  1. 播放与暂停按钮设置:声明一个槽函数,当音乐播放状态发生改变时,触发槽函数改变当前图标状态,在按钮转到槽函数中实现两种音乐播放状态的改变;
 //音乐播放状态改变与暂停播放按钮状态
   connect(musicPlayer,SIGNAL(stateChanged(QMediaPlayer::State)),this,SLOT(slotReflushPlayButtonIcon(QMediaPlayer::State)));
//播放、停止按钮的设计
void Musicinterface::slotReflushPlayButtonIcon(QMediaPlayer::State state)
{
    if (state == QMediaPlayer::PlayingState)
    {
        ui->pushButton_musicPlay->setStyleSheet("#pushButton_musicPlay\
        {\
        border-image: url(:/Image/pause1.png); }\
        #pushButton_musicPlay:pressed\
        {\
        border-image: url(:/Image/pause2.png);\
        }");
    }
    else
    {
        ui->pushButton_musicPlay->setStyleSheet("#pushButton_musicPlay\
        {\
        border-image: url(:/Image/play1.png); }\
        #pushButton_musicPlay:pressed\
        {\
        border-image: url(:/Image/play2.png);\
        }");
    }

}
//暂停与播放
void Musicinterface::on_pushButton_musicPlay_clicked()
{
    int row = ui->tableWidget_musicList->currentRow();	//获取当前选中行
    //获取当前播放状态
    //播放或暂停
    if (musicPlayer->state() == QMediaPlayer::PlayingState)
    {
        musicPlayer->pause();
        ui->graphicsView->animationEnd();
    }
    else if (musicPlayer->state() == QMediaPlayer::PausedState)
    {
        int row2 = ui->tableWidget_musicList->currentRow();	//获取当前选中行
        if (row == row2)//如果此时选中行与暂停前一样
        {
             ui->graphicsView->animationStart();
            musicPlayer->play();
        }
        else
        {
             on_tableWidget_musicList_cellDoubleClicked(row2,0);
        }
    }
    else //如果都不是,就播放当前选中的歌,如果没有选中就退出
    {
        int row = ui->tableWidget_musicList->currentRow();	//获取当前选中行
        //如果没有选中就直接退出
        if (row == -1)
        {
            return ;
        }
        else
        {
            on_tableWidget_musicList_cellDoubleClicked(row,0);
        }
    }
}
  1. 上一曲、下一曲切换设置(包括按键状态切换和歌曲切换,这里只写歌曲切换代码)
//上一曲
void Musicinterface::on_pushButton_playLast_clicked()
{
    //获取当前选中行
    int row = ui->tableWidget_musicList->currentRow();	//获取当前选中行
    //如果没有选中就直接退出
    if (row == -1)
    {
        return ;
    }
    //获取上一行的行号,如果是第一行,则获取最后一行
    int endRow = ui->tableWidget_musicList->rowCount();	//获取当前共有多少行
    if (row == 0)
    {
        row = endRow - 1;
    }
    else
        row--;
    //播放歌曲
    on_tableWidget_musicList_cellDoubleClicked(row,0);//调用双击播放函数
    //选中上一行
    ui->tableWidget_musicList->selectRow(row);		//选中第line行
}
  1. 随机播放与顺序播放设置:当一首音乐播完时,触发播放顺序函数,查看当前标志位状态并播放下一首歌曲;
   //链接音乐播放状态与循环顺序按钮
    connect(musicPlayer,SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),this,SLOT(slotPlay(QMediaPlayer::MediaStatus)));

//随机播放和顺序播放
void Musicinterface::slotPlay(QMediaPlayer::MediaStatus state)
{
    if (state == QMediaPlayer::EndOfMedia)
    {
        if (playOrder)
        {
             on_pushButton_playNext_clicked();
        }
        else
        {
            int row = ui->tableWidget_musicList->rowCount();	//获取当前共有多少行
            int n = qrand() % row;
            on_tableWidget_musicList_cellDoubleClicked(n, 0);
            ui->tableWidget_musicList->selectRow(row);		//选中第line行
        }
    }
    else if (state == QMediaPlayer::InvalidMedia)
    {
        QMessageBox msgBox;
        msgBox.setWindowIcon(QIcon(":/Image/icon1.png"));
        msgBox.setWindowTitle("CloudMusic");
        msgBox.setIcon(QMessageBox::Warning);
        msgBox.setText("Do ont have Sources");
        msgBox.setInformativeText("Next Song");
        msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
        msgBox.setDefaultButton(QMessageBox::Yes);
        int ret = msgBox.exec();
        if (ret == QMessageBox::Yes)
        {
            on_pushButton_playNext_clicked();
        }
    }
}
//随机数生成
 QTime time;
 time = QTime::currentTime();
 qsrand(time.msecsSinceStartOfDay());
 int row = ui->tableWidget_musicList->rowCount();	//获取当前共有多少行
 int n = qrand() % row;
  1. 进度条设置

//歌曲时长
void Musicinterface::slotReflushEndTime(qint64 ms)//时长
{
    ui->horizontalSlider_progressBar->setMaximum(ms);//进度条设置
    QTime time = QTime::fromMSecsSinceStartOfDay(ms);
    //显示总时长
    ui->label_endTime->setText(time.toString("mm:ss"));//将ms 转化为分钟:秒
}

//歌曲当前时间
void Musicinterface::slotReflushStartTime(qint64 ms)//当前值
{
    ui->horizontalSlider_progressBar->setValue(ms);//进度条设置
    QTime time = QTime::fromMSecsSinceStartOfDay(ms);
    //显示当前时长
    ui->label_startTime->setText(time.toString("mm:ss"));//将ms 转化为分钟:秒
}
void Musicinterface::on_horizontalSlider_progressBar_sliderMoved(int position)
{
    musicPlayer->setPosition(position);//进度条可拖动
}
  1. 歌词滚动展示 : 从网络中解析出来的歌词上带有时间;
//歌词滚动
void Musicinterface::slotLyricChange(int line)
{
    QTextCursor tc2 = ui->textEdit_musicWord->textCursor();//获取当前光标
    int pos2 = ui->textEdit_musicWord->document()->findBlockByLineNumber(line).position();//在textEdit中找到指定行位置
    tc2.setPosition(pos2, QTextCursor::MoveAnchor);	 //将光标设置到指定位置
    tc2.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);//光标设置成选中一整行,并且保持此状态
    ui->textEdit_musicWord->setTextCursor(tc2);//将设置好的光标属性放到textEdit中
}


HttpHandle 网络连接类

  1. Qt 实现HTTP协议:HTTP协议数据格式为:请求行、头部行、和附属体;请求数据的方式共有七种,常用的有Get和Post两种;
  2. 常用的类有:
    1)QNetworkRequest 网络连接请求类
    2)QNetworkAccessManager 网络访问管理器,使应用程序发送网络请求并接收响应
    3)QNetworkReply 网络回复类 接收网络数据
  3. Get方法时默认的HTTP请求方法,是将请求信息作为URL的一部分发送给Web服务器,存在安全隐患,而且有的服务器可能对URL的数据长度有限制;
QByteArray HttpHandle::getSearch(const QString &url)
{

    QNetworkRequest networkRequest(url);//网址
    QEventLoop eventloop;//事件循环
    QNetworkReply *networkReply = networkAccessManager->get(networkRequest);
    connect(networkReply,SIGNAL(finished()),&eventloop,SLOT(quit()));//接收信息结束,事件循环停止
    eventloop.exec(QEventLoop::ExcludeUserInputEvents);//阻止未接收完第一次信息时的第二次搜索

    return networkReply->readAll();
}

  1. Post方法是在向Web服务器提交表单数据时,将请求部分放在附属体中发送给服务器;
//将Url,头信息、请求方式封装成函数
//返回值是从网站接收到的数据
QByteArray HttpHandle::postSearch(const QString body)
{

    QNetworkRequest networkRequest(QUrl("https://l-by.cn/yinyue/api.php"));//网址
    networkRequest.setRawHeader(QByteArray("Content-Type"),QByteArray("application/x-www-form-urlencoded; charset=UTF-8"));//Head

    QEventLoop eventloop;//事件循环
    QNetworkReply *networkReply = networkAccessManager->post(networkRequest,body.toUtf8());
    connect(networkReply,SIGNAL(finished()),&eventloop,SLOT(quit()));//接收信息结束,事件循环停止
    eventloop.exec(QEventLoop::ExcludeUserInputEvents);//阻止未接收完第一次信息时的第二次搜索

    return networkReply->readAll();
}

  1. JSON解析数据
    1)QJsonParseError 用于json解析期间报错
    2)QJsonDocument 用于写入或读取json文档数据
    3)QJsonObject 封装了一个json类型对象,可用来获取json对象里的值
    示例:

//搜索框查询函数,歌曲列表
void HttpHandle::searchMusicList(const QString &searchData)
{
    //拼接字符串.arg()
    QString body = QString("types=search&count=30&source=netease&pages=1&name=%1").arg(searchData);
    QByteArray musicData = postSearch(body);//将Url,头信息、请求方式封装成函数调用

    QJsonParseError error;//错误码
    QJsonDocument jsonDocument = QJsonDocument::fromJson(musicData,&error);
    if (error.error != QJsonParseError::NoError)
    {
        qDebug() << "解析音乐列表JSON数据失败!错误码:" << error.errorString();
        return ;
    }

    //解析
    QJsonArray musicInfoArray = jsonDocument.array();//大数组
    for (int i = 0; i < musicInfoArray.size(); ++i)//遍历
    {
        QJsonObject musicObject = musicInfoArray[i].toObject();//对象
        QStringList musicInfo;//字符串
        QString id = QString::number(musicObject["id"].toInt());//解析id
        QString picId = musicObject["pic_id"].toString();//解析pic_id (字符串类型)
        QString name = musicObject["name"].toString();//解析歌名

        QJsonArray artistArray = musicObject["artist"].toArray();//解析歌手(多人)
        QString artist;
        for (int j = 0; j < artistArray.size(); ++j)//遍历歌手
        {
            artist = artist + artistArray[j].toString() + ",";//拼接在一起XX,XX,XX
        }
        artist.chop(1);//删除最后一个“,”
        QString album = musicObject["album"].toString();//解析专辑
        musicInfo << id << picId << name << artist << album;//将所有解析到的信息拼在一起
        emit signalShowMusicInfoList(musicInfo);//发送信号和信息给

    }
}
//歌曲播放网址
QString HttpHandle::searchMusicUrl(const QString &id)
{
    //拼接字符串.arg()
    QString body = QString(" types=url&id=%1&source=netease").arg(id);
    QByteArray musicData = postSearch(body);//将Url,头信息、请求方式封装成函数调用

    QJsonParseError error;//错误码
    QJsonDocument jsonDocument = QJsonDocument::fromJson(musicData,&error);
    if (error.error != QJsonParseError::NoError)
    {
        qDebug() << "解析音乐地址JSON数据失败!错误码:" << error.errorString();
        return QString();
    }

    QJsonObject jsonobject = jsonDocument.object();
    return jsonobject["url"].toString();
}
//歌词
QString HttpHandle::searchMusicLyric(const QString &id)
{
    //拼接字符串.arg()
    QString body = QString("types=lyric&id=%1&source=netease").arg(id);
    QByteArray musicData = postSearch(body);//将Url,头信息、请求方式封装成函数调用

    QJsonParseError error;//错误码
    QJsonDocument jsonDocument = QJsonDocument::fromJson(musicData,&error);
    if (error.error != QJsonParseError::NoError)
    {
        qDebug() << "解析音乐歌词JSON数据失败!错误码:" << error.errorString();
        return QString();
    }

    QJsonObject jsonobject = jsonDocument.object();
    QString musiclyric = jsonobject["lyric"].toString();

    return musiclyric;
}
//专辑图片
QString HttpHandle::searchMusicPicUrl(const QString &picId)
{
    //拼接字符串.arg()
    QString body = QString(" types=pic&id=%1&source=netease").arg(picId);
    QByteArray picData = postSearch(body);//将Url,头信息、请求方式封装成函数调用
    QJsonParseError error;//错误码
    QJsonDocument jsonDocument = QJsonDocument::fromJson(picData,&error);
    if (error.error != QJsonParseError::NoError)
    {
        qDebug() << "解析音乐歌词JSON数据失败!错误码:" << error.errorString();
        return QString();
    }
    QJsonObject jsonobject = jsonDocument.object();
    return jsonobject["url"].toString();

}

MusicPlayer 音乐播放类

  1. 继承了QMediaPlayer类,用Qt的多媒体播放类来实现音乐的播放
  2. 在该类中我们自己要实现的时歌词的修整,因为在网络中解析出来的歌词时一长串的,并且开头带有时间;
QString MusicPlayer::parsingLyrics(QString lyric)
{
    QStringList lyricList = lyric.split('\n');//以回车为界拆分
    QString newLyric;//修改好的歌词

    lyricTime.clear();//清空容器中的时间
    int line = 0;//初始化行号

    for (int i = 0 ; i < lyricList.size(); ++i)
    {
        QStringList lyricLineList = lyricList[i].split(']');//以】为界分割时间和歌词
        if (lyricLineList.size() != 2 || lyricLineList[1].size() < 1)
        {
            continue;//跳过空歌词
        }
        QString lyricLine = lyricLineList[1].trimmed();//去掉首尾空格
        newLyric = newLyric + lyricLine + "\r\n";

        QString time = lyricLineList[0].remove('[');//删除时间多余的【
        if (time.size() == 8)//老歌时间格式(如:蒋大为)
        {
            time = time + '0';//普通歌时间是9位
        }
        QTime ms = QTime::fromString(time,"mm:ss.zzz");//将时间转换为毫秒
        lyricTime[ms.msecsSinceStartOfDay()] = line ++;//写入容器
    }
    return newLyric;
}
  1. 设置初始音量 setVolume(50);

GraphicsView 动画展示类

  1. 该类还包含两个类:
DiskItem *diskItem;//唱片动画
MagnetNeedle *magnetNeedle;//唱针动画
  1. 窗口展示
void GraphicsView::reflushMusicPic(const QImage &pic)
{

    image = pic;//展示的专辑图片
    QImage argb32Image = makeARGB32Image(pic);
    QImage circleImage = makeCircleImage(argb32Image);
    QImage diskImage = makeDiskImage(circleImage);//重叠成圆形

    diskImage = diskImage.scaled(300,300);
    diskItem->setImage(diskImage);//将圆形图片设置为动画的图片
    update();//更新图片
}

两个动画类

1.继承QPropertyAnimation类,而QPropertyAnimation也继承了QObject;

DiskItem::DiskItem(QObject *parent) : QObject(parent) ,
    animation(new QPropertyAnimation(this,"anagle",this))
{
    animation->setDuration(5000);//周期
    animation->setStartValue(0);//起始角度
    animation->setEndValue(360);//结束角度
    animation->setLoopCount(-1);//循环转到
}
QRectF DiskItem::boundingRect() const//修改坐标轴起始位置
{
    return QRectF(image.width() / -2.0, image.height() / -2.0, image.width(), image.height());
}
void DiskItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

    painter->setRenderHint(QPainter::SmoothPixmapTransform);
    painter->drawImage(boundingRect(),image);

}
void DiskItem::setImage(const QImage &musicImage)
{
    image = musicImage;//设置动画图片
    update();
}
void DiskItem::setAnimationStart()
{
    animation->start();//音乐开始播放时开始转动
}
void DiskItem::setAnimationEnd()
{
    animation->stop();//音乐暂停停止转动
}
  • 7
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值