QT项目 -仿QQ音乐的音乐播放器(第三节)

目录

自定义recBox:

RecBoxItem类中添加动画效果:

两个添加TEXT和imgStyle的函数:

图片随机函数:

qqmusic.cpp:

recbox的初始化函数:

QT的一些额外加餐:

关于如何设置按钮的功能

connect函数

[事件过滤器:实现监视目标对象事件 的作用](#事件过滤器:实现监视目标对象事件 的作用)

QPropertyAnimation的基本使用

QString

QJsonArray和QJsonObject

自定义recBox:

  1. RecBox界⾯布局
    ① 新添加设计师界⾯,命名为RecBox。geometry的宽⾼修改为:685*440。
    ② 添加三个Widget,objectName依次修改为leftPage、musicContent、rightPage;
    leftPage 和 rightPage的minimumSize和maximumSize修改宽为30,然后选中RecBox点击⽔平布
    局。将RecBox的 margin和Spacing修改为0
    ③ 在upPage和downPage中各拖⼀个按钮,upPage中按钮objectName修改为btUp,minimumSize
    的⾼度修改为 220;downPage中按钮objectName修改为btDown,minimumSize的⾼度修改为
    220;然后选中upPage和 downPage点击⽔平布局。将upPagedownPage和的margin和
    Spacing修改为0。
    ③ 在musicContent中拖两个Widget,objectName依次修改为recListUp和recListDown,然后选中
    musicContent点 击垂直布局,将musicContent的margin和Spacing修改为0。(为了看清楚效果可
    临时将recListUp背景⾊设置 为:background-color:green; 将recListDown背景⾊设置为:
    background-color:red;)
    ④ 在recListUp和recListDown中分别拖两个⽔平布局器,依次命名为recListUpHLayout和
    recListDownHLayout,选 中recListUp和recListDown点击⽔平布局,将margin和Spacing修改为0
    做好美化后将QQMusic主界⾯中recPage⻚⾯中的recMusicBox和supplyMusicBox提升为RecBox

RecBoxItem类中添加动画效果:

重写eventfilter函数

cpp 复制代码
bool RecBoxItem::eventFilter(QObject *watched, QEvent *event)
{
    if(watched == ui->musicImageBox)
    {
        if(QEvent::Enter == event->type())
        {
            // 添加图片上移动画
            QPropertyAnimation* animation = new QPropertyAnimation(ui->musicImageBox, "geometry");
            animation->setDuration(150);
            animation->setStartValue(QRect(9,9, ui->musicImageBox->width(), ui->musicImageBox->height()));
            animation->setEndValue(QRect(9, 0, ui->musicImageBox->width(), ui->musicImageBox->height()));
            animation->start();

            connect(animation, &QPropertyAnimation::finished, this, [=](){
                delete animation;
            });
        }
        else if(QEvent::Leave == event->type())
        {
            // 添加图标下移动画
            QPropertyAnimation* animation = new QPropertyAnimation(ui->musicImageBox, "geometry");
            animation->setDuration(150);
            animation->setStartValue(QRect(9,0, ui->musicImageBox->width(), ui->musicImageBox->height()));
            animation->setEndValue(QRect(9, 9, ui->musicImageBox->width(), ui->musicImageBox->height()));
            animation->start();

            connect(animation, &QPropertyAnimation::finished, this, [=](){
                delete animation;
            });
        }

        return true;
    }

    return QObject::eventFilter(watched, event);
}

两个添加TEXT和imgStyle的函数:

cpp 复制代码
void RecBoxItem::setText(const QString &text)
{
    ui->recBoxItemText->setText(text);
}

void RecBoxItem::setImage(const QString &Imagepath)
{
    QString imgStyle="border-image:url("+Imagepath+");";
    ui->recMusicImage->setStyleSheet(imgStyle);
}

图片随机函数:

在qqmusic.cpp中定义图片随机选取并且存成嵌套qjsonobject的qjsonarray格式返回,后面会在qqmusic调用initrecitem函数,里面randompicture的返回值会作为参数。

cpp 复制代码
QJsonArray QQMusic::RandomPicture()
{

        // 推荐⽂本 + 推荐图⽚路径
        QVector<QString> vecImageName;
        vecImageName<<"001.png"<<"003.png"<<"004.png"<<"005.png"<<"006.png"
                     <<"007.png"
                     <<"008.png"<<"009.png"<<"010.png"<<"011.png"<<"012.png"
                     <<"013.png"
                     <<"014.png"<<"015.png"<<"016.png"<<"017.png"<<"018.png"
                     <<"019.png"
                     <<"020.png"<<"021.png"<<"022.png"<<"023.png"<<"024.png"
                     <<"025.png"
                     <<"026.png"<<"027.png"<<"028.png"<<"029.png"<<"030.png"
                     <<"031.png"
                     <<"032.png"<<"033.png"<<"034.png"<<"035.png"<<"036.png"
                     <<"037.png"
                     <<"038.png"<<"039.png"<<"040.png";
        std::random_shuffle(vecImageName.begin(), vecImageName.end());
        // 001.png
        // path: ":/images/rec/"+vecImageName[i];
        // text: "推荐-001"
        QJsonArray objArray;
        for(int i = 0; i < vecImageName.size(); ++i)
        {
            QJsonObject obj;
            obj.insert("path", ":/images/rec/"+vecImageName[i]);
            // arg(i, 3, 10, QCchar('0'))
            // i:要放⼊%1位置的数据
            // 3: 三位数
            // 10:表⽰⼗进制数
            // QChar('0'):数字不够三位,前⾯⽤字符'0'填充
            QString strText = QString("推荐-%1").arg(i, 3, 10, QChar('0'));
            obj.insert("text", strText);
            objArray.append(obj);
        }
        return objArray;

}
cpp 复制代码
recbox.h:


#ifndef RECBOX_H
#define RECBOX_H

#include <QWidget>
#include <QJsonArray>

namespace Ui {
class RecBox;
}

class RecBox : public QWidget
{
    Q_OBJECT

public:
    explicit RecBox(QWidget *parent = nullptr);
    ~RecBox();
    void initRecBoxUi(QJsonArray data, int row);//初始化推荐界面
    void createRecBoxItem();

private:
    Ui::RecBox *ui;
    int row; // 记录当前RecBox实际总⾏数
    int col; // 记录当前RecBox实际每⾏有⼏个元素
    QJsonArray imageList; // 保存界⾯上的图⽚, ⾥⾯实际为key、value键值对

};

#endif // RECBOX_H

上面在recbox.h中定义了row和col作为行列

qqmusic.cpp:

cpp 复制代码
    srand(time(NULL));
    ui->recMusicBox->initRecBoxUi(RandomPicture(),1);
    ui->supplyMusicBox->initRecBoxUi(RandomPicture(),2);

在recbox的构造函数里初始化行列数值

cpp 复制代码
#include "ui_recbox.h"
#include<RecBoxItem.h>
#include <QJsonObject>

RecBox::RecBox(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::RecBox),
    row(1),
    col(4)
{
    ui->setupUi(this);
}

recbox的初始化函数:

cpp 复制代码
recbox.cpp

void RecBox::initRecBoxUi(QJsonArray data, int row)
{
    // 如果是两⾏,说明当前RecBox是主界⾯上的supplyMusicBox
    if(2 == row)
    {
        this->row = row;
        this->col = 8;
    }
    else
    {
        // 否则:只有⼀⾏,为主界⾯上recMusicBox
        ui->recListDown->hide();
    }
    // 图⽚保存起来
    imageList = data;
        // 往RecBox中添加图⽚
        createRecBoxItem();
}


void RecBox::createRecBoxItem()
{
    // 创建RecBoxItem对象,往RecBox中添加
    // col
    for(int i = 0; i < col; ++i)
    {
        RecBoxItem* item = new RecBoxItem();
        // 设置⾳乐图⽚与对应⽂本
        QJsonObject obj = imageList[i].toObject();
        item->setText(obj.value("text").toString());
        item->setImage(obj.value("path").toString());
        // recMusicBox:col为4,元素添加到ui->recListUpHLayout中
        // supplyMuscBox: col为8, ui->recListUpHLayout添加4个,ui->recListDownHLayout添加4个
            // 即supplyMuscBox上下两⾏都要添加
            // 如果是recMusicBox:row为1,只能执⾏else,所有4个RecBoxItem都添加到ui->recListUpHLayout中
                  // 如果是supplyMuscBox:row为2,col为8,col/2结果为4,i为0 1 2 3时,元素添加到ui->recListDownHLayout中
              // i为4 5 6 7时,元素添加到ui->recListUpHLayout中
              if(i >= col/2 && row == 2)
        {
             ui->recListDownHLayout->addWidget(item);
             }
         else
             {
             ui->recListUpHLayout->addWidget(item);
             }
         }
     }

总结下来就是在recboxitem类里重写eventfilter函数设置动画,然后在recbox函数里添加图片/写初始化图片函数,在qqmusic类里写随机图片函数(输出json格式的array数组),在qqmusic类里调用初始化图片函数。

最后的效果:

QT的一些额外加餐:

关于如何设置按钮的功能或者添加控件

1.手动添加槽函数

2.定义类然后在相应的Widget位置提升为新的类

3.直接在cpp中实例化类然后添加ui

cpp 复制代码
RecBoxItem* item = new RecBoxItem();
ui->recListUpHLayout->addWidget(item);

4.用connect函数连接
下面介绍connect函数

connect函数

信号槽机制,按钮是发送信号,窗口是接收信号,槽的本质就是对信号响应的函数,是一个回调函数

例1:

cpp 复制代码
void QQMusic::connectSignalAndSlot()
{
    connect(ui->Rec, &BtForm::click, this, &QQMusic::onBtFormClick);
    connect(ui->music, &BtForm::click, this, &QQMusic::onBtFormClick);
    connect(ui->audio, &BtForm::click, this, &QQMusic::onBtFormClick);
    connect(ui->like, &BtForm::click, this, &QQMusic::onBtFormClick);
    connect(ui->local, &BtForm::click, this, &QQMusic::onBtFormClick);
    connect(ui->recent, &BtForm::click, this, &QQMusic::onBtFormClick);
}  

这里连接了6个`BtForm`类型按钮(`ui->Rec`, `ui->music`, `ui->audio`, `ui->like`, `ui->local`, `ui->recent`)的`click`信号到当前对象(`this`,即`QQMusic`实例)的同一个槽函数`onBtFormClick`。- 这意味着当这些按钮中的任何一个被点击时,都会触发`QQMusic::onBtFormClick`槽函数。

这里的第一个参数是目标对象界面中的按钮组件 ),第二个参数是触发信号自定义按钮类 BtForm 的点击信号 ),第三个参数接收者当前 QQMusic 类实例 ),第四个参数槽函数处理按钮点击的槽函数
例2:

cpp 复制代码
connect(animation, &QPropertyAnimation::finished, this, [=](){
    delete animation;
});

这里连接了一个`QPropertyAnimation`对象(指针变量名为`animation`)的`finished`信号。- 当动画完成时,会触发一个Lambda表达式(作为槽函数)。- Lambda表达式以值捕获方式(`[=]`)捕获当前作用域的变量(注意:这里捕获的是`this`指针和`animation`指针,因为`animation`在外部定义)。- 在Lambda表达式内部,执行`delete animation;`,即删除动画对象,释放内存。

这里的第一个参数是针对musicImageBox创建的动画组件 ,第二个参数是结束触发信号 ,第三个参数是接收者this(recboxitem) ,第四个参数是槽函数(删除动画)

事件过滤器:实现监视目标对象事件 的作用

bool eventFilter(QObject *watched, QEvent *event);
eventFilter 的 第一个参数 obj 指向 的是 事件本应传递到的目标对象。
重写是类似这样的:

cpp 复制代码
 bool Widget::eventFilter(QObject *obj, QEvent *event)
    {
        if(obj == ui->label)
        {
            //鼠标进入的时候
            if (event->type() == QEvent::Enter)
            {
                ui->label->setText("我是红色");
                ui->label->setStyleSheet(redStyle);
                return true; //拦截事件,不再传递
            }
            else if(event->type() == QEvent::Leave) //鼠标离开
            {
                ui->label->setText("我是黑色");
                ui->label->setStyleSheet(blackStyle);
                return true;
            }
     
            return false;  //不拦截事件,允许继续传递,别的事件会传给label对象
        }
     
        // standard event processing
        return QWidget::eventFilter(obj, event);
    }

鼠标进入过滤->鼠标离开过滤

QPropertyAnimation的基本使用

常用接口函数

  • setTargetObject:设置仿真对象
  • setPropertyName:设置仿真属性的名称,
  • setDuration:设置仿真持续的时间
  • setStartValue:设置初始值
  • setEndValue:设置结束值
  • start:开始仿真
  • currentValue:返回当前值
  • setKeyValueAt:设置关键点的值
  • valueChanged:只要仿真追踪的值发生变化,就发送该信号

QString

QString 是 Qt 中的一个类,用于存储字符串,QString 没有父类。QString 存储的是一串字符,每个字符是一个 QChar 类型的数据。QChar 使用的是 UTF-16 编码,一个字符包含 2 字节数据。 对于超过 65535 的 Unicode 编码,QString 使用两个连续的 QChar 字符表示。UTF-16 是一种 Unicode 编码,能表示汉字,在 QString 字符串中一个汉字是一个字符。

QString 类定义了大量的接口函数用于字符串操作。QString在 Qt 类库中应用非常广泛,很多函数的参数是 QString 类型。

例如:

cpp 复制代码
QString str = "Hello Qt";

QString str1= "洋洋", str2= "得意"; 
QString str3= str1 + str2; //str3 ="洋洋得意" 
str1= str2 + str1; //str1 ="得意洋洋" 

QString str1= "卖", str2= "拐"; 
QString str3= str1; 
str1.append(str2); //str1 ="卖拐" 
str3.prepend(str2); //str3 ="拐卖" 

//等等还有好多函数

RecBox添加RecBoxItem
random_shuffle是打乱列表顺序的函数

QJsonArray和QJsonObject

这两个类都是用于处理JSON数据
在Qt中,QJsonArray和QJsonObject是Qt JSON模块的一部分,用于表示JSON数据

  1. QJsonArray: - 功能:QJsonArray类用于封装一个JSON数组。 - 一个JSON数组是一个值的有序列表,这些值可以是不同的类型(包括字符串、数字、布尔值、对象、数组,以及null)。 - 在QJsonArray中,元素是通过索引(从0开始)来访问的。 - 常用操作: * 添加值:使用`append`、`push_back`或`insert`方法。 * 访问值:使用`at`方法或`operator[]`(注意:operator[]返回的是非const的引用,而at返回的是const引用)。 * 删除值:使用`removeAt`方法。 * 获取数组大小:使用`size`方法。 * 遍历:可以使用迭代器或简单的for循环(基于索引)。
  2. QJsonObject: - 功能:QJsonObject类用于封装一个JSON对象。 - 一个JSON对象是一个无序的键值对集合,其中键是字符串,值可以是各种JSON类型(包括数组、对象、基本类型等)。 - 在QJsonObject中,通过键(字符串)来访问对应的值。 - 常用操作: * 插入键值对:使用`insert`方法。 * 访问值:使用`value`方法或`operator[]`(注意:operator[]如果键不存在则会插入一个null值,而value方法不会修改对象)。 * 删除键值对:使用`remove`方法。 * 检查键是否存在:使用`contains`方法。 * 获取所有键:使用`keys`方法。 * 获取键值对数量:使用`size`方法。 * 遍历:可以使用迭代器或遍历键列表。
    QJsonArray:表示JSON数组,有序列表,通过索引访问。
    QJsonObject:表示JSON对象,键值对映射,通过键访问。
    例如:
cpp 复制代码
QJsonArray:

QJsonArray arr;
arr.append(42);          // 添加整数
arr.append("Hello");     // 添加字符串
arr.append(QJsonObject{ {"key", "value"} }); // 添加嵌套对象

int num = arr[0].toInt();     // 获取索引 0 的值 → 42
QString str = arr[1].toString(); // → "Hello"

arr.replace(0, 100);     // 替换索引 0 的值
arr.removeAt(1);         // 删除索引 1 的元素

int size = arr.size();   // 获取元素数量
bool isEmpty = arr.empty(); // 判空
cpp 复制代码
QJsonObject:

QJsonObject obj;
obj.insert("id", 1001);
obj["name"] = "Alice";       // 使用运算符[]插入
obj["scores"] = QJsonArray{90, 85, 95}; // 嵌套数组

int id = obj["id"].toInt();          // → 1001
QString name = obj.value("name").toString(); // → "Alice"

obj["id"] = 2002;          // 修改值
obj.remove("name");        // 删除键

bool hasKey = obj.contains("scores"); // 检查键是否存在
QStringList keys = obj.keys();      // 获取所有键 → ["id", "scores"]
int size = obj.size();              // 键值对数量