QT 仿网易云项目

这是我第一次不看别人视频,而是直接看源码仿写项目.之前都是看着别人的教学视频一步一步跟着敲的.

这里我按照当时解析源码进行的视线过程列出来,代码会在文章最后给出链接.

主界面展示

主界面布局

左边的列表部分不用控件摆放,通过代码去实现.所以主界面的控件摆放如下:

主界面主要由上中下三个部分组成,上面是一个顶部widget,中间是两个widget,下面是一个底部导航栏widget:

运行效果如下:

去掉自带标题栏

请看Qt天气预报项目章节中讲过

顶部widget1

写一个初始化顶部widget的函数Initwd(),在构造时调用.

主界面中间左侧widget2

给左侧加一个垂直布局:

我们可以用到QVBoxLayout这个类:

通过setContentsMargins 方法设置垂直布局的上下左右边距:

将QListWidget控件放入垂直布局中:

给QListWidget添加item

设置样式:

然后我们设置隐藏列表滚动条:

cpp 复制代码
    scro->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //垂直滚动条隐藏
    scro->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //水平滚动条隐藏

主界面右侧主体部分widget3

在widget3中布一个垂直布局,创建一个QStackWiget,将其放入widget3中.

cs 复制代码
void MainWindow::InintStack()
{
    QVBoxLayout *vlay=new QVBoxLayout(ui->widget3);
    vlay->setContentsMargins(0,0,0,0);
    
    //创建QStackWidget
    stack=new QStackedWidget(ui->widget3);
    vlay->addWidget(stack);
    
    widget1=new widget_1(stack);
    widget2=new widget_2(stack);
    widget3=new widget_3(stack);
    widget4=new widget_4(stack);
    widget5=new widget_5(stack);
    widget6=new widget_6(stack);
    widget7=new widget_7(stack);
    widget8=new widget_8(stack);
    widget9=new widget_9(stack);
    widget10=new widget_10(stack);
    widget11=new widget_11(stack);
    widget12=new widget_12(stack);
    
    stack->addWidget(widget1);
    stack->addWidget(widget2);
    stack->addWidget(widget3);
    stack->addWidget(widget4);
    stack->addWidget(widget5);
    stack->addWidget(widget6);
    stack->addWidget(widget7);
    stack->addWidget(widget8);
    stack->addWidget(widget9);
    stack->addWidget(widget10);
    stack->addWidget(widget11);
    stack->addWidget(widget12);
    
    stack->setCurrentIndex(5);
    list2->setCurrentRow(0);
    
    
}

搜索界面的实现

字段

1.新建一个classHeader.h头文件

2.定义一个songInfo类,类内四个四个属性

classHeader.h

cpp 复制代码
class songInfo
{
public:
    QString musicId;  //歌曲ID,用来后面使用API播放音乐
    QString musicName; //歌曲名
    int musicDuration; //音乐时长
    QString  artistsName; //创作者名称
};

界面

searchwidget.ui

这个是搜索界面歌曲播放列表每一行的框架:

主界面的功能实现:

searchwidget.cpp

cs 复制代码
#ifndef SEARCHWIDGET_CPP
#define SEARCHWIDGET_CPP
#include "searchwidget.h"
#include "ui_searchwidget.h"
#include<QScrollBar>

SearchWidget::SearchWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::mainWidget)
{
    ui->setupUi(this);

    ui->musicName->setMaximumWidth(300);
    ui->tipString->setAlignment(Qt::AlignBottom);
    ui->tipString->setText("的相关搜索如下");
    //ui->tipString->adjustSize();
    ui->tabWidget->setCurrentIndex(0);

    this->setStyleSheet(R"(
        QWidget#widget{
            background-color: qconicalgradient(cx:0.665746, cy:0.216, angle:0, stop:0
                rgba(86, 132, 183, 75), stop:1 rgba(166, 193, 255, 35));
        }
        QLabel#musicName{
            font:34px "宋体" black;
            font-weight:bold;
        }
        QLabel#tipString{
            font:20px "宋体" black;
            color:rgba(0,0,0,100);
        }
        QTabWidget{
            font:20px "宋体" black;
        }
        QTabBar::tab{
            background-color:rgba(0,0,0,0);
        }
        QTabBar::tab:hover{
            color:rgba(100,54,200,225);
        }
        QTabBar::tab:selected{
            font:26px;
            color:rgba(131,55,197,200);
        }
        QTabWidget::pane{
            background-color:rgba(0,0,0,0);
            border: 1px solid white;
        }
        QListWidget{
            border:none;
            font:18px "宋体";
            background-color:rgba(0,0,0,0);
        }
        QListWidget::item{
            height:60px;
            border-radius:10px;
            padding-left:20px;
            padding-right:20px;
            margin-top:2px;
            margin-bottom:2px;

            margin-left:10px;
            margin-right:10px;
        }
        QListWidget::item:hover{
            background-color: rgba(255,255,255,100);
        }
        QListWidget::item:selected{
            background-color: rgba(131,255,197,100);
        }
    )");
    /*
        对于QTabWidget的样式表设置好像格外的复杂,不知道为什么background-color一直设置不上
    */

    ui->listWidget->setFocusPolicy(Qt::NoFocus);
    ui->listWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    ui->listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

}

SearchWidget::~SearchWidget()
{
    delete ui;
}

void SearchWidget::updataListWidget()
{
    deleteItem();

    //qDebug()<<songList.size();
    for(int q=0;q<songList.size();q++){
        QListWidgetItem* item=new QListWidgetItem(ui->listWidget);
        ui->listWidget->addItem(item);

        songItem* itemWidget=new songItem(ui->listWidget);
        ui->listWidget->setItemWidget(item,itemWidget);

        itemWidget->getLablel()->setText(QString::number(q+1)+".");
        itemWidget->getSongLabel()->setText(songList[q].musicName);
        itemWidget->getNameLabel()->setText(songList[q].artistsName);
        int minutes=songList[q].musicDuration/60000,seconds=(songList[q].musicDuration%60000)/1000;
        QString tim=QString::asprintf("%02d:%02d",minutes,seconds);
        itemWidget->getDurationLabel()->setText(tim);

        //qDebug()<<songList[q].artistsName;
    }

}

void SearchWidget::deleteItem()
{
    for(int q=ui->listWidget->count()-1;q>=0;q--){
        QListWidgetItem* item = ui->listWidget->takeItem(q); // 从列表中移除项但不删除
        if (item) {
            QWidget* widget = ui->listWidget->itemWidget(item); // 获取关联的 QWidget
            delete widget; // 删除 QWidget
            delete item; // 删除 QListWidgetItem
        }
    }
}

QLabel *SearchWidget::getSongLab()
{
    return ui->musicName;
}

void SearchWidget::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
{
    emit listDoublePress(ui->listWidget->row(item));
}


#endif // SEARCHWIDGET_CPP

1.给歌曲名称设置最大宽度为300

2.设置文本方式为底部对齐,并设置文本

3.设置当前标签页为0

4.设置表样式

5.设置ListWidget的焦点策略,使其失去焦点,并且隐藏水平和垂直滚动条

6.updataListWidget(刷新Listwidget)

  1. 调用 deleteItem() 来删除列表中的所有项。(每次更新之前就把之前的全部删了)
  2. 遍历 songList,为每个歌曲创建一个 QListWidgetItem 和一个 songItem(自定义的 QWidget 子类)。
  3. 设置歌曲信息,包括歌曲编号、歌名、歌手名称和时长。

7.deleteItem

1.用for循环遍历QListWidget的每一行

2.调用QListWidget的takeItem()函数,可以把ListWidget的每一行都移除掉

3.并删除与该项关联的 QWidget 和 QListWidgetItem。

8.on_listWidget_itemDoubleClicked

1.on_listWidget_itemDoubleClicked 是一个槽函数,当 ui->listWidget 中的项被双击时调用。它发射一个 listDoublePress 信号,并传递双击项的行号。

然后我们需要通过点击下方widget4来与中间主体widget3产生交互.

比如点击上一曲或者下一曲就播放widget3歌单列表上对应的歌曲,点击暂停按钮就停止播放.双击widget3歌单列表上某一首歌,就会播放歌曲,并且widget4上播放按钮变为启动样式.

上一曲下一曲功能的实现

连接信号槽

cs 复制代码
 void MainWindow::InintStack()
{
    connect(ui->btn_pre,&QPushButton::clicked,this,&MainWindow::upSong);
    connect(ui->btn_pre,&QPushButton::clicked,this,&MainWindow::downSong);
}

一.首先在切换歌曲之前我们需要判断一下当前的歌曲播放模式,对于不同的播放模式,我们的切换歌曲方式也是不同的。

二.

如果当前播放模式是循环模式,那么我们就正常从播放列表中进行上下歌曲切换。

如果当前是单曲模式,那么不做任何操作。

如果当前是随机播放模式,那么我们就调用随机函数在播放列表随便切一首歌曲。

三.在切换歌曲之后我们需要刷新一下下方widget界面,让其显示我们切的歌曲的信息和界面。

四.最后我们调用一下PlaySong()函数播放我们新切的歌曲。

槽函数

cs 复制代码
//上一曲
void MainWindow::upSong()
{
    ui->btn_Play->setIcon(QIcon(":/images/newPix/zanting.png"));
    if(typePlay==XUNHUAN)
    {
        nowIndex-=1;
        if(nowIndex<0)nowIndex=songList.size()-1;
    }
    else if(typePlay==DANQU) //单曲
    {
    }
    else //随机
    {
        nowIndex=QRandomGenerator::global()->bounded(-1,songList.size());
    }
    
    //更新下方widget4
    ui->label_3->setText(songList[nowIndex].musicName);
    ui->label_4->setText("00:00");
    //1分钟等于6000毫秒
    int minutes=songList[nowIndex].musicDuration/6000,second=(songList[nowIndex].musicDuration%60000)/1000;
    QString tim=QString::asprintf("%2d:%2dd",minutes,second);
    ui->label_5->setText(tim);
    PlaySong(nowIndex);
}

//下一曲
void MainWindow::downSong()
{
    ui->btn_Play->setIcon(QIcon(":/images/newPix/zanting.png"));
    if(typePlay==XUNHUAN)
    {
        nowIndex+=1;
        if(nowIndex>songList.size()-1)nowIndex=0;
    }
    else if(typePlay==DANQU) //单曲
    {
    }
    else //随机
    {
        nowIndex=QRandomGenerator::global()->bounded(-1,songList.size());
    }
    
    //更新下方widget4
    ui->label_3->setText(songList[nowIndex].musicName);
    ui->label_4->setText("00:00");
    //1分钟等于6000毫秒
    int minutes=songList[nowIndex].musicDuration/6000,second=(songList[nowIndex].musicDuration%60000)/1000;
    QString tim=QString::asprintf("%2d:%2dd",minutes,second);
    ui->label_5->setText(tim);
    PlaySong(nowIndex);
}

双击实现播放

连接信号槽

cs 复制代码
 void MainWindow::InintStack()
{
    connect(searWid,&SearchWidget::listDoublePress,this,&MainWindow::wid_12_listDouble);  //双击搜索界面歌单列表歌曲
    connect(searWid,&SearchWidget::listDoublePress,this,&MainWindow::wid_6_listDouble);  //双击本地歌单列表歌曲
}

槽函数

cs 复制代码
//搜索界面双击list播放列表歌曲 在线播放
void MainWindow::wid_12_listDouble(int index)
{
    ui->btn_Play->setIcon(QIcon(":/images/newPix/zanting.png"));
    is_start=0;
    
    is_online_play=true;  //本次列表的歌曲为在线播放
    nowIndex=index;
    
    //清空本类中维护的List
    songList.clear();
    //删除本类的ListWidgetItem
    
    //刷新songList的元素
    songList.resize(searWid->songList.size());
    for(int i=0;i<searWid->songList.size();i++)
    {
        songList[i]=searWid->songList[i];
    }
    
    //刷新QListWidget
    
    //更新下方widget4
    ui->label_3->setText(songList[index].musicName);
    ui->label_4->setText("00:00");
    int minutes=songList[index].musicDuration/60000,seconds=(songList[index].musicDuration%60000)/1000;
    QString tim=QString::asprintf("%02d:%02d",minutes,seconds);
    ui->label_5->setText(tim);
    
    if(ui->widget_4->isHidden()) ui->widget_4->show();
    //播放音乐
    PlaySong(index);
    
}


//搜索界面双击list播放列表歌曲 本地播放
void MainWindow::wid_6_listDouble(int index)
{
    ui->btn_Play->setIcon(QIcon(":/images/newPix/zanting.png"));
    is_start=0;
    
    is_online_play=false;        //本次列表的歌曲为本地播放
    nowIndex=index;
    //清空本类中维护的QList
    songList.clear();
    //删除本类的ListWidgetItem
    
    //刷新SongList的元素
    songList.resize(widget6->songList.count());    //重新设置大小
    for(int q=0;q<widget6->songList.size();q++){
        songList[q]=widget6->songList[q];
    }
    //刷新QListWidget
    
    //更新下方widget_4
    ui->label_3->setText(songList[index].musicName);
    ui->label_4->setText("00:00");
    int minutes=songList[index].musicDuration/60000,seconds=(songList[index].musicDuration%60000)/1000;
    QString tim=QString::asprintf("%02d:%02d",minutes,seconds);
    ui->label_5->setText(tim);
    
    if(ui->widget_4->isHidden()) ui->widget_4->show();
    //播放音乐
    PlaySong(index);
}

下方widget

设置布局样式:

cs 复制代码
void MainWindow::InitBottom()
{
    soundW=new soundwidget(this);
    connect(soundW->slider,&QSlider::valueChanged,this,&MainWindow::soundChange);
    soundW->hide();

    ui->widget4->setFixedHeight(75);
    ui->widget4->hide();

    ui->btn_list->setIcon(QIcon(":/images/newPix/liebiao1.png"));
    ui->btn_list->setIconSize(QSize(24,24));

    ui->btn_Volume->setIcon(QIcon(":/images/newPix/shengyi1.png"));
    ui->btn_Volume->setIconSize(QSize(24,24));

    ui->btn_friend->setIcon(QIcon(":/images/newPix/haoyou1.png"));
    ui->btn_friend->setIconSize(QSize(24,24));

    ui->btn_yinxiao->setIcon(QIcon(":/images/newPix/yinxiao1.png"));
    ui->btn_yinxiao->setIconSize(QSize(24,24));

    ui->btn_quality->setIcon(QIcon(":/images/newPix/yinzhi1.png"));
    ui->btn_quality->setIconSize(QSize(28,28));

    ui->btn_pre->setIcon(QIcon(":/images/newPix/down/shang1.png"));
    ui->btn_pre->setIconSize(QSize(24,24));

    ui->btn_Next->setIcon(QIcon(":/images/newPix/down/xia1.png"));
    ui->btn_Next->setIconSize(QSize(24,24));

    ui->btn_Play->setIcon(QIcon(":/images/newPix/down/kaishi.png"));
    ui->btn_Play->setIconSize(QSize(36,36));

    ui->btn_lyrics->setIcon(QIcon(":/images/newPix/dow/ci1.png"));
    ui->btn_lyrics->setIconSize(QSize(24,24));

    ui->btn_songModen->setIcon(QIcon(":/images/newPix/down/danqu1.png"));
    ui->btn_songModen->setIconSize(QSize(18,18));

    ui->btn_collection->setIcon(QIcon(":images/newPix/down_left/xihuan1.png"));
    ui->btn_collection->setIconSize(QSize(18,18));

    ui->btn_comments->setIcon(QIcon(":/images/newPix/down_left/pinglun1 (2).png"));
    ui->btn_comments->setIconSize(QSize(18,18));

    ui->btn_Load->setIcon(QIcon(":/images/newPix/left_down/xiazai1.png"));
    ui->btn_Load->setIconSize(QSize(18,18));

    ui->btn_more->setIcon(QIcon(":/images/newPix/down_left/genduo1.png"));
    ui->btn_more->setIconSize(QSize(18,18));

 //   ui->label_2->setPixmap(QPixmap(":/re/images/NewMusic/Dis04.png"));
    ui->label_2->setScaledContents(true);
    ui->label_2->setFixedSize(QSize(45,45));

    ui->label_3->setStyleSheet("font:16px \"宋体\"");
    ui->label_3->setFixedWidth(160);

    ui->label_4->setFixedWidth(45);
    ui->label_4->setAlignment(Qt::AlignRight| Qt::AlignVCenter);
    ui->label_5->setFixedWidth(45);

    ui->widget4->setStyleSheet(R"(
        QPushButton{
            border:none;
        }
        QPushButton#btn_list:hover{
            icon:url(:/images/newPix/liebiao2.png);
        }
        QPushButton#btn_Volume:hover{
            icon:url(:/images/newPix/shengyin2.png);
        }
        QPushButton#btn_friend:hover{
            icon:url(:images/newPix/haoyou2.png);
        }
        QPushButton#btn_yinxiao:hover{
            icon:url(:/images/newPix/yinxiao2.png);
        }
        QPushButton#btn_quality:hover{
            icon:url(:/images/newPix/yinzhi2.png);
        }
        QPushButton#btn_lyrics:hover{
            icon:url(:/images/newPix/down/ci2.png);
        }
        QPushButton#btn_pre:hover{
            icon:url(:/images/newPix/down/shang2.png);
        }
        QPushButton#btn_Next:hover{
            icon:url(:/images/newPix/down/xia2.png);
        }
        QPushButton#btn_songModen:hover{
            icon:url(:/images/newPix/down/danqu2.png);
        }
        QPushButton#btn_collection:hover{
            icon:url(:/images/newPix/down_left/xihuan2.png);
        }
        QPushButton#btn_comments:hover{
            icon:url(:/images/newPix/down_left/pinglun2 (2).png);
        }
        QPushButton#btn_Load:hover{
            icon:url(:/images/newPix/down_left/xiazai2.png);
        }
        QPushButton#btn_more:hover{
            icon:url(:/images/newPix/down_left/genduo2.png);
        }
        QWidget#widget4{
            background-color: rgb(245, 255, 255);
        }
        QLabel#label_4 ,#label_5{
            font:14px "宋体";
            color:grey;
        }
    )");

}

mainwindow.h

音乐播放列表部分的声明:

首先定义一个QList用来存放当前播放的音乐列表.

然后定义一个变量nowIndex表示当前光标指向哪一行

再定义一个QMediaPlay对象mainPlay

最后声明一个PlaySong()函数用来实现播放歌曲

cpp 复制代码
//音乐播放列表
private:
    QList<songInfo>songList;  //主界面维护一个QList,存储当前播放的音乐列表
    int nowIndex=0;
    QMediaPlayer *mainPlay;

    void PlaySong(int index);

歌曲播放列表是一个QListWidget,其中每一行都是一个QListWidgetItem,由四个字段组成:歌曲ID,歌曲名,音乐时长,创作者名称 :

我们后面获取歌曲信息后,将其转化为字符串,然后赋值给这四个字段PlaySong()函数的实现如下:

cpp 复制代码
void MainWindow::PlaySong(int index)
{
    int count=songList[nowIndex].musicDuration;
    ui->progressBar->setRange(0,count);
    if(is_online_play)  //如果要播放的音乐在线
    {
        QString url;

        //通过网易云API传入之前返回的歌曲的ID实现播放
        url=QString("https://music.163.com/song/media/outer/url?id=%1.mp3").arg(songList[index].musicId);
         qDebug()<<"loading procedure: "+url;
        mainPlay->setSource(QUrl(url));
         mainPlay->play();
        ui->btn_bofang->setIcon(QIcon(":/iamges/kaishi"));
         is_start=1; //该变量定义为全局变量,为1表示在播放状态
    }
}

mainwindow.h

cpp 复制代码
//下方widget
private:
    enum playType{DANQU,XUNHUAN,SUIJI};
    playType typePlay=XUNHUAN;
    
    void InitBottom();
    bool statrt=true;
    soundwidget *sounddW;

在.h文件中定义一个枚举类型变量playType,枚举的三个参数分别为单曲,循环,随机.用来表示播放列表播放歌曲的三种方式.

playType的默认值设为XUNHUAN

对初始化下方widget函数进行声明

定义一个布尔变量start ,表示当前是否在播放歌曲状态.创建一个soundwidget 类,这个类实际上就是我们点击音量按钮之后弹出来的音量条的那个白色窗体

soundwidget窗体的实现

一.我们设置一个40×150大小的白色窗体,然后我们给它设一个垂直布局。

soundwidget.cpp

cpp 复制代码
#include "soundwidget.h"
#include<QVBoxLayout>
#include<QEvent>

soundWidget::soundWidget(QWidget *parent)
    : QWidget{parent}
{

    this->setFixedSize(40,150);

    //构造时会给此类设置一个父组件,如果时默认的Qt::widget,有父组件则会成为该父组件的一个界面组件,
    //而不是一个窗口;
    //设置为Qt::window则无论有无父组件都是一个窗口
    this->setWindowFlag(Qt::Window);
    this->setWindowFlag(Qt::FramelessWindowHint);   //无上方标题栏



    slider=new QSlider(this);
    QVBoxLayout* lay=new QVBoxLayout(this);
    lay->addWidget(slider);
    lay->setAlignment(Qt::AlignCenter);


    slider->setRange(0,100);
    slider->setOrientation(Qt::Vertical);      //设置成垂直方向
    slider->setTickPosition(QSlider::NoTicks); //设置为无刻度

    this->setStyleSheet(R"(
        soundWidget{
            background-color:white;
            border-radius:10px;
        }
    )");

}

二.在soundwidget.h引入定义一个QSlider类型变量slider.QSlisder class是一个滑块类,这样我们就可以通过滑块来调节音量:

然后我们对虚函数leaveEvent()进行重写.leaveEvent()

soundwidget.h

cpp 复制代码
#ifndef SOUNDWIDGET_H
#define SOUNDWIDGET_H

#include <QSlider>


class soundwidget:public QWidget
{
public:
    
    explicit soundwidget(QWidget *parent=nullptr);
public:
    QSlider *slider;
signals:
protected:
    virtual void leaveEvent(QEvent *event) override;  //鼠标移出时隐藏本身
};

#endif // SOUNDWIDGET_H

重写内容如下:

soundwidget.cpp

cpp 复制代码
void soundwidget::leaveEvent(QEvent *event)
{
    this->hide();
    QWidget::leaveEvent(event);
    event->accept();
}

这段代码的作用是:当鼠标指针离开 soundWidget 时,首先隐藏这个 widget,然后调用基类 QWidgetleaveEvent 函数以执行其默认处理,最后通知事件系统这个事件已经被处理。通过这种方式,soundWidget 类不仅实现了自定义的事件处理逻辑(隐藏 widget),还保留了基类的默认行为。

实际上也就是说当我们点击调节音量条之后鼠标离开,soundwidget窗体和音量条会自动隐藏。

音量调节的实现

这个函数实现的功能是:

  • 当用户点击声音按钮时,计算并移动音量控制窗口soundwidget到音量按钮btn_volume正上方并水平居中。
  • 同步音量控制窗口中的音量滑块与当前播放器的音量。
  • 切换音量控制窗口的显示状态,如果它之前是隐藏的,则显示它;如果它之前是显示的,则隐藏它。
  • 获取按钮位置并计算soundW窗口的位置:

    • QPoint P=ui->btn_volume->mapTo(this,QPoint(0, 0)); 这一行代码获取了pushButton_2相对于其父窗口(MainWindow)的位置。mapTo(this, QPoint(0, 0))将按钮的左上角(坐标为(0, 0))映射到MainWindow的坐标系统。

    • QPoint globalP=mapToGlobal(P); 这一行代码将按钮的坐标转换为全局坐标(即屏幕坐标)。

    • int x=globalP.x()+ui->pushButton_2->width()/2-soundW->width()/2; 这一行计算soundW窗口应该放置的x坐标,使得soundW窗口的中心与btn_volume的中心对齐。

    • int y=globalP.y()-soundW->height(); 这一行计算soundW窗口应该放置的y坐标,使得soundW窗口位于pushButton_2的正上方。

    • soundW->move(x,y-15); 这一行代码移动soundW窗口到计算出的坐标位置,并且向下偏移15个像素,可能是为了在视觉上让窗口与按钮之间有间隔。

  • 设置音量:

    • float s=mainPlayer->audioOutput()->volume(); 这一行代码获取当前播放器(mainPlayer)的音量,音量值是一个0到1.0之间的浮点数。
    • soundW->slider->setValue(s*100); 这一行代码将音量滑块(slider)的值设置为当前音量的百分比形式(乘以100),以便用户可以通过滑块调整音量。
  • 显示或隐藏soundW窗口:

    • if(soundW->isHidden()) soundW->show(); 如果soundwidget窗口当前是隐藏的,这行代码将显示它。
    • else soundW->hide(); 如果soundW窗口当前是显示的,这行代码将隐藏它。

暂停歌曲的实现

我们之前定义了一个全局布尔变量(is_start),现在我们就可以用它来判断当前是否是歌曲播放状态,从而来确定点击播放按钮是播放音乐还是暂停播放。

cpp 复制代码
void MainWindow::on_pushButton_10_clicked()   //暂停按钮槽函数
{
    if(is_start){
        is_start=0;
        mainPlayer->pause();
        ui->pushButton_10->setIcon(QIcon(":/down/images/newPix/zanting.png"));
    }
    else{
        is_start=1;
        mainPlayer->play();
        ui->pushButton_10->setIcon(QIcon(":/down/images/newPix/kaishi.png"));
    }
}

播放模式切换的实现

我们通过单击btn_changeMode按钮来实现三种播放模式的切换:单曲,循环,随机。

我们在上文中定义了一个枚举类型变量playType, 三个参数为DANJI,XUNHUAN,SUIJI

如果说当前是单曲模式,那么经过单击之后就转换为循环播放模式,如果当前是循环模式那么就转换为随机播放模式,如果当前是随机播放模式,那么就转换为单曲播放模式。

cpp 复制代码
void MainWindow::on_pushButton_14_clicked()        //播放模式
{
    if(typePlay==DANQU){
        typePlay=XUNHUAN;
        ui->pushButton_14->setIcon(QIcon(":/cent/images/newPix/down/danqu1.png"));
        ui->pushButton_14->setStyleSheet(R"(
            QPushButton:hover{
                icon:url(:/cent/images/newPix/down/danqu2.png);
            }
        )");
    }
    else if(typePlay==XUNHUAN){
        typePlay=SUIJI;
        ui->pushButton_14->setIcon(QIcon(":/cent/images/newPix/down/suiji1.png"));
        ui->pushButton_14->setStyleSheet(R"(
            QPushButton:hover{
                icon:url(:/cent/images/newPix/down/suiji2.png);
            }
        )");
    }
    else if(typePlay==SUIJI){
        typePlay=DANQU;
        ui->pushButton_14->setIcon(QIcon(":/cent/images/newPix/down/xunhuan1.png"));
        ui->pushButton_14->setStyleSheet(R"(
            QPushButton:hover{
                icon:url(:/cent/images/newPix/down/xunhuan2.png);
            }
        )");
    }
}

​搜索歌曲

搜索模块的实现

  1. 读取网络响应数据:

    • 请求网络,并将信息存储到QByteArray中
  2. 解析JSON数据:

    • 尝试将响应数据解析为JSON文档。
  3. 清理并解析数据:

    • 如果songList不为空,则先清空它,为新的数据做准备。
  4. 将Json文档转换为Json对象

  5. 提取歌曲信息:

    • 检查JSON根对象中是否包含键名为"result"的项。
    • 将"result"对应的值转换为JSON对象。
    • 检查"result"对象中是否包含键名为"songs"的项,该键名下包含了歌曲信息数组。
    • 将"songs"对应的值转换为JSON数组。
  6. 遍历歌曲数组并保存数据:

    • 调整songList的大小以匹配歌曲数组的大小。
    • 遍历歌曲数组,并将每首歌曲的信息保存到songList中。
      • QJsonObject object = songArr[i].toObject(); 获取数组中的每个元素并将其转换为JSON对象。
      • searWid->songList[i].musicId = QString::number(object["id"].toVariant().toLongLong()); 提取音乐ID并将其转换为字符串。
      • searWid->songList[i].musicDuration = object["duration"].toInt(); 提取音乐时长。
      • searWid->songList[i].musicName = object["name"].toString(); 提取音乐名称。
      • 如果存在"artists"键,则提取歌手名称。
  7. 更新列表控件:

搜索歌曲的实现

我们通过在输入框le_in输入歌曲名称,单击按钮btn_search实现搜索歌曲功能。

首先我们用字符串存储我们输入框输入的歌曲名称,然后我们通过该歌曲名称调用网易NodejsAPI接口去获取歌曲,接着我们需要将获取到的歌曲刷新到我们的主窗口上。

上文中说了我们的主窗口是一个QStackWidget来存储的,

我们需要做两个操作:

一.如果list1之前有选中的行,这行代码将清除该选中状态。

下面三行代码实现这个功能:

cpp 复制代码
    list1->setCurrentRow(-1);
    list2->setCurrentRow(-1);
    stack->setCurrentIndex(12);

在用户在搜索框中按下回车键进行搜索时,清除两个列表控件的选中状态,并将主界面的堆栈窗口切换到特定的页面,为显示搜索结果做准备。

二.显示搜索歌曲内容:

cpp 复制代码
  searWid->getSongLab()->setText(ui->lineEdit->text());

链接信号和槽

窗口放大缩小退出恢复

定义一个封装各信号的函数InitConnect(),在构造的时候调用。

定义如下:

cpp 复制代码
void MainWindow::InitConnect()
{

    //右上方设置窗口槽函数
    connect(this->ui->exitButton,&QPushButton::clicked,this,&MainWindow::exitSlot);
    connect(this->ui->bigButton,&QPushButton::clicked,this,&MainWindow::bigSlot);
    connect(this->ui->smallButton,&QPushButton::clicked,this,&MainWindow::smallSlot);
    connect(this->ui->normalButton,&QPushButton::clicked,this,&MainWindow::normalSlot);

    //选中左侧导航栏切换主窗口
    connect(list1,&QListWidget::currentRowChanged,this,&MainWindow::curchangeSlot);
    connect(list2,&QListWidget::currentRowChanged,this,&MainWindow::curchangeSlot_2);
}

我喜欢的音乐界面

实现的效果为"我喜欢的音乐"界面显示本地存储的音乐