Qt编写网易云界面 (9) -----歌词界面、部分功能实现

发布时间 2023-09-30 20:11:48作者: 桂洛克船长

最近就是就是完成一些小功能,歌词界面,部分按钮的点击事件,歌词列表等;

效果如图:

视频页面

image-20230930195517822

歌单列表:

image-20230930200015308

歌词界面:

image-20230930195928753

点击歌曲详情页面:

image-20230930200110301

歌曲列表代码listform:

#ifndef LISTFORM_H
#define LISTFORM_H
#include <QVector>
#include <QWidget>
#include <QString>
namespace Ui {
class ListForm;
}

class ListForm : public QWidget
{
    Q_OBJECT

public:
    explicit ListForm(QWidget *parent = nullptr);
    ~ListForm();

    void setdata();
    QVector<QString> data;
    int pos=0;
signals:
    void mysig();

protected:
    void leaveEvent(QEvent *event);

private slots:
    void on_listWidget_currentRowChanged(int currentRow);

private:

    Ui::ListForm *ui;
};

#endif // LISTFORM_H

listform.cpp:

#include "listform.h"
#include "ui_listform.h"
#include <QDebug>
ListForm::ListForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ListForm)
{
    ui->setupUi(this);
}

void ListForm::setdata()
{
    QStringList s;
    for(int i=0;i<data.size();i++)
    {
        QString x=data[i];
        QStringList a=x.split(".");
        s.push_back(a[0]);
    }
    ui->listWidget->addItems(s);
}

ListForm::~ListForm()
{
    delete ui;
}
void ListForm::leaveEvent(QEvent *event)
{
    Q_UNUSED(event);
    this->close();
}

void ListForm::on_listWidget_currentRowChanged(int currentRow)
{
    pos = currentRow;
    emit mysig();
}

歌曲界面的代码参考某个大佬的实现:

lyricwidget.h:

#ifndef LYRICWIDGET_H
#define LYRICWIDGET_H

#include<QWidget>
#include<QLabel>
#include<QFile>
#include<vector>
#include <QMouseEvent>
#include <QMediaPlayer>
#include <QMediaPlaylist>
#include <QPainter>
using namespace std;

//表示一行歌词(一个时间点+对应的歌词文本)
class LyricLine
{
public:
    qint64 time;
    QString text;
    LyricLine(qint64 time, QString text):time(time), text(text){}
};

bool operator <(const LyricLine& A, const LyricLine& B);

namespace Ui {
class LyricWidget;
}

class LyricWidget : public QWidget
{
    Q_OBJECT

    //储存所有歌词
    vector<LyricLine> lines;
public:
    explicit LyricWidget(QWidget *parent = nullptr);
    ~LyricWidget();

    //将歌词文件的内容处理为歌词结构的QList
    bool process(QString filePath);
    //根据时间找到对应位置的歌词
    int getIndex(qint64 position);
    //显示当前播放进度的歌词
    void showcontent(qint64 position);
    //根据下标获得歌词内容
    QString getLyricText(int index);
    //清空歌词Label
    void clear();

    LyricWidget* l;
    QMediaPlaylist  *playlist;
    int pos;
    QVector<QString> music;

    void setlrc(LyricWidget* l,QMediaPlayer  *player,QMediaPlaylist  *playlist,int pos,QVector<QString> music);
    //更新歌词
    void updateSongLrc(qint64 position);
    void updateSongLyclist();


protected:
    //绘制背景函数
    void paintEvent(QPaintEvent* event);
    //鼠标按下
    void mousePressEvent(QMouseEvent* event);
    //鼠标移动
    void mouseMoveEvent(QMouseEvent* event);
    //鼠标释放
    void mouseReleaseEvent(QMouseEvent* event);

private:
    Ui::LyricWidget *ui;
    QPoint last;
};

#endif // LYRICWIDGET_H


lyricwidget.cpp:

#include "LyricWidget.h"
#include "ui_lyricwidget.h"

#include <algorithm>
#include <QTextCodec>
#include<QDebug>
#include <QPainter>
LyricWidget::LyricWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::LyricWidget)
{
    ui->setupUi(this);
    clear();//清空用于排版时的测试内容(.ui文件中)
    ui->label_i->setStyleSheet("color:LightSkyBlue");
    //首先去除自带的边框
    this->setWindowFlag(Qt::FramelessWindowHint);
    //背景透明
    setAttribute(Qt::WA_TranslucentBackground);
}

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

void LyricWidget::setlrc(LyricWidget* l,QMediaPlayer  *player,QMediaPlaylist  *playlist,int pos,QVector<QString> music)
{
    connect(player,&QMediaPlayer::positionChanged,this,&LyricWidget::updateSongLrc);//随时间变化歌词
    l->show();
    this->l=l;
    this->pos=pos;
    this->playlist=playlist;
    this->music=music;
    connect(playlist,&QMediaPlaylist::currentMediaChanged,this,&LyricWidget::updateSongLyclist);//歌曲变化就查找和提取歌词
}
void LyricWidget::updateSongLyclist()
{
    int index=playlist->currentIndex();//获取当前位置
    //歌名
    QString songname_daihouzui=music[index];//提取在当前位置的文件名
    qDebug() << songname_daihouzui;
    QStringList songnamelist = songname_daihouzui.split(".");//QString字符串分割函数
    QString songname=songnamelist[0];
    qDebug()<<"歌词歌曲名:"<<songname;
    QString lycpath="D:/mycode/qt_code/My_WYY/bin/music/" +songname+".lrc";
    l->process(lycpath);
}
void LyricWidget::updateSongLrc(qint64 position)//随时间变化而变化显示歌词
{
    l->showcontent(position);//更新歌词位置
}
void LyricWidget::mousePressEvent(QMouseEvent *event)
{
    //获取title widget 的高度,这里固定为60px
    int titleHeight = ui->layoutWidget->height();

    if(event->y() <titleHeight)
    {
        last = event->globalPos(); //获取到按压的位置
    }
}

void LyricWidget::mouseMoveEvent(QMouseEvent *event)
{
    int titleHeight = ui->layoutWidget->height();
    if(event->y() <titleHeight)
    {
        int dx = event->globalX() - last.x();
        int dy = event->globalY() - last.y();
        last = event->globalPos();
        this->move(this->x()+dx,this->y()+dy);
    }
}

void LyricWidget::mouseReleaseEvent(QMouseEvent *event)
{
    int titleHeight = ui->layoutWidget->height();
    if(event->y() <titleHeight)
    {
        int dx = event->globalX() - last.x();
        int dy = event->globalY() - last.y();
        this->move(this->x()+dx,this->y()+dy);
    }
}

void LyricWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.fillRect(rect(), QColor(0, 0, 0, 0));  // 使用透明色填充背景
    // 其他绘制操作...
}

//重载比较(歌词按时间排序)
bool operator <(const LyricLine& A, const LyricLine& B)
{
    return A.time<B.time;
}

bool LyricWidget::process(QString filePath)
{
    QFile lyricFile(filePath);
    //qDebug() << filePath<<"111";
    lyricFile.open(QFile::ReadOnly);
    //QString content(QString::fromLocal8Bit(lyricFile.readAll()));
    QString content(lyricFile.readAll());
    qDebug()<<"歌词\n"<<content;
    lyricFile.close();

    //先清空歌词
    lines.clear();

    const QRegExp rx("\\[(\\d+):(\\d+(\\.\\d+)?)\\]"); //用来查找时间标签的正则表达式

    // 步骤1
    int pos = rx.indexIn(content);//返回第一个匹配项的位置,如果没有匹配项则返回-1。
    if (pos == -1) {
        return false;
    }
    else {
        int lastPos;//
        QList<int> timeLabels;//时间标签
        do {
            // 步骤2
            timeLabels << (rx.cap(1).toInt() * 60 + rx.cap(2).toDouble()) * 1000;
            lastPos = pos + rx.matchedLength();//返回最后一个匹配字符串的长度,如果没有匹配则返回-1
            pos = rx.indexIn(content, lastPos);
            if (pos == -1) {
                QString text = content.mid(lastPos).trimmed();
                foreach (const int& time, timeLabels)
                    lines.push_back(LyricLine(time, text));
                break;
            }
            // 步骤3
            QString text = content.mid(lastPos, pos - lastPos);
            if (!text.isEmpty()) {
                foreach (const int& time, timeLabels)
                    lines.push_back(LyricLine(time, text.trimmed()));
                timeLabels.clear();
            }
        }
        while (true);
        // 步骤4
        stable_sort(lines.begin(), lines.end());//排序
    }
    if (lines.size()) {
        return true;
    }
    return false;
}

int LyricWidget::getIndex(qint64 position)//获取歌词位置
{
    if(!lines.size()){
        return -1;//如果没歌词
    }
    else{
        if(lines[0].time>=position){
            return 0;
        }
    }
    int i=1;
    for(i=1;i<lines.size();i++){
        if(lines[i-1].time<position && lines[i].time>=position){
            return i-1;
        }
    }
    return lines.size()-1;
}

void LyricWidget::showcontent(qint64 position)//显示当前播放进度的歌词
{
    int index=getIndex(position);
    if(index==-1){
        //ui->label_3i->setText("");
        //ui->label_2i->setText("");
        ui->label_1i->setText("");
        ui->label_i->setText(u8"当前歌曲无歌词");
        ui->label_i1->setText("");
        //ui->label_i2->setText("");
        //ui->label_i3->setText("");
    }else{
        //ui->label_3i->setText(getLyricText(index-3));
        //ui->label_2i->setText(getLyricText(index-2));
        ui->label_1i->setText(getLyricText(index-1));
        ui->label_i->setText(getLyricText(index));
        ui->label_i1->setText(getLyricText(index+1));
        //ui->label_i2->setText(getLyricText(index+2));
        //ui->label_i3->setText(getLyricText(index+3));
    }
}

QString LyricWidget::getLyricText(int index)//根据下标获得歌词内容
{
    if(index<0 || index>=lines.size()){
        return "";
    }else{
        //qDebug()<<"输出歌词:"<<lines[index].text;
        return lines[index].text;
    }
}

void LyricWidget::clear()
{
    //ui->label_3i->setText("");
    //ui->label_2i->setText("");
    ui->label_1i->setText("");
    ui->label_i->setText("");
    ui->label_i1->setText("");
    //ui->label_i2->setText("");
    //ui->label_i3->setText("");
}

mv界面就是简单实现了一下,详细界面也是简单实现点击事件,后续慢慢实现功能及美化。