QT之音乐播放器

和自甴很熟 提交于 2020-02-27 08:37:36

2020-2-26,项目耗时:2天

实现的功能

  • 依据歌曲作者对歌曲进行分类,并以树状目录的结构显示在界面上
  • 进度条实现
  • 播放、暂停、音量调节
  • 显示当前歌曲名

效果如下

模块划分

  • 歌曲目录树模块:用来将歌曲文件以目录树的形式展现在界面上,并向外界提供当前被选中歌曲的路径信息。
  • 控制面板模块:用来控制播放器的行为,以及反应播放器的状态。
  • 播放器模块:播放器引擎,用来执行音乐文件的播放。
  • 顶层模块:用来将以上模块组合在一起。

下面一一介绍每个模块的实现。

歌曲目录树模块

主要用到了一个QTreeWidget类,我从这个类派生出一个MusicTreeList类,关于QTreeWidget的详细说明见帮助文档。

music_tree_list.h

#ifndef MUSIC_TREE_LIST_H
#define MUSIC_TREE_LIST_H
#include "QTreeWidget"

class MusicTreeList : public QTreeWidget
{
    Q_OBJECT
public:
    MusicTreeList();
    ~MusicTreeList();

public slots:
    void musicChanged(QTreeWidgetItem *item, int column);

signals:
    void currentMusic(QString str);

};

#endif // MUSIC_TREE_LIST_H

music_tree_list.cpp

#include "music_tree_list.h"
#include "QRegExp"
#include "QtDebug"
#include "QDir"
MusicTreeList::MusicTreeList()
{
    //设置歌曲栏目,详细用法见QTreeWidget帮助文档
    this->setColumnCount(2);
    QStringList headers;
    headers << QObject::tr("歌名") << QObject::tr("发行时间");
    this->setHeaderLabels(headers);

    //设定音乐文件目录,使用过滤器保留歌曲作者文件夹,详细用法见QDir帮助文档
    QStringList folder;
    QString dirPath = "C:/Users/jiage/Desktop/folder/QT/qtreewidget/music";
    QDir dir(dirPath);
    dir.setFilter(QDir::Dirs);

    //foreach语句遍历dir中的文件夹名称列表,与此同时构建对应的目录树
    foreach(QFileInfo fullDir, dir.entryInfoList())
    {

        if(fullDir.fileName() == "." || fullDir.fileName() == "..") continue;

        folder.push_back(fullDir.fileName());
        QStringList singer_name;
        singer_name << fullDir.fileName();
        QTreeWidgetItem *temp_singer = new QTreeWidgetItem( this, singer_name );


        QDir temp_dir(fullDir.absoluteFilePath());
        QStringList nameFilters;
        nameFilters << "*.mp3";
        QStringList files = temp_dir.entryList(nameFilters, QDir::Files|QDir::Readable, QDir::Name);
        while( !files.isEmpty() ) {

                QStringList song_name;
                song_name << files[0];
                QTreeWidgetItem *temp_song = new QTreeWidgetItem(temp_singer, song_name);
                temp_singer->addChild(temp_song);

                files.pop_front();
        }

    }

    //当QTreeWidget中某个item被点击时,将这个被点击的item所对应的歌曲路径以字符串的形式发送出去
    connect(this, &QTreeWidget::itemClicked, this, &MusicTreeList::musicChanged);

}

MusicTreeList::~MusicTreeList()
{

}

void MusicTreeList::musicChanged(QTreeWidgetItem *item, int column)
{
    QString music_end_path;
    QString pattern(".*.mp3");
    QRegExp rx(pattern);
    bool match = rx.exactMatch(item->text(column));

    if ( !match )
    {
        return;
    }

    music_end_path = "/" + item->parent()->text(column) + "/" + item->text(column);
    //qDebug() << music_end_path;

    emit currentMusic( music_end_path  );
}

总的来说,这个类的作用就是根据用户点击的歌曲,以字符串的形式输出该歌曲的路径。

控制面板模块

ControlPanel类继承自QWidget。

control_panel.h

#ifndef CONTRO_PANEL_H
#define CONTRO_PANEL_H

#include <QWidget>
class QPushButton;
class QSlider;
class QLabel;

class ControlPanel : public QWidget
{
    Q_OBJECT
public:
    explicit ControlPanel(QWidget *parent = nullptr);
    ~ControlPanel();
    QPushButton * stop_btn;//停止
    QPushButton * last_btn;//上一首
    QPushButton * next_btn;//下一首
    QPushButton * play_btn;//播放或者暂停
    QPushButton * volume_btn;//音量启停
    QPushButton * orderplay_btn;//顺序播放
    QSlider * music_progress;//音乐进度滑杆
    QSlider * volume_slider;//音量滑杆
    QLabel * time_label;//时间标签
    QString  total_time;//总时间
    QString current_time;//当前时间


public slots:
    void changeTotalTime(qint64 duration);//歌曲切换时发生
    void updateProgress(int64_t milli_sec);//歌曲进度
    void volumeSliderChanged(int value);//音量改变时发生

    void causePlayBtnClicked();//播放按钮按下,调用
    void causeStopBtnClicked();//停止按钮按下,调用
    void causeNextBtnClicked();//下一首按钮按下,调用
    void causeLastBtnClicked();//上一首按钮按下,调用

signals:
    void volumeChanged(int value);//发出音量改变信号
    void playBtnClicked();//发出播放信号
    void stopBtnClicked();//发出停止信号
    void nextBtnClicked();//发出下一首信号
    void lastBtnClicked();//发出上一首信号

private:
    int64_t total_millisec;//当前歌曲总时间


};

#endif // CONTRO_PANEL_H

control_panel.cpp

#include "contro_panel.h"
#include "QSlider"
#include "QLabel"
#include "QPushButton"
#include "QHBoxLayout"
#include "QVBoxLayout"
#include "QGridLayout"
#include "QStyle"
ControlPanel::ControlPanel(QWidget *parent) : QWidget(parent)
{
    total_time = "/00:00";
    current_time = "00:00";
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);

    stop_btn = new QPushButton();
    stop_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaStop));
    last_btn = new QPushButton();
    last_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaSkipBackward));
    play_btn = new QPushButton();
    play_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaPlay));
    next_btn = new QPushButton();
    next_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaSkipForward));
    orderplay_btn = new QPushButton();
    orderplay_btn->setIcon(this->style()->standardIcon(QStyle::SP_ArrowDown));
    volume_btn = new QPushButton();
    volume_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaVolume));
    music_progress = new QSlider();
    music_progress->setOrientation(Qt::Horizontal);
    time_label = new QLabel(current_time + total_time);
    time_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

    QHBoxLayout * layout1 = new QHBoxLayout();
    layout1->addWidget(music_progress);
    layout1->addWidget(time_label);


    volume_slider = new QSlider();
    volume_slider->setOrientation(Qt::Horizontal);

    QHBoxLayout * layout2 = new QHBoxLayout();
    layout2->addStretch();
    //layout2->addWidget(orderplay_btn);
    layout2->addWidget(stop_btn);
    layout2->addWidget(last_btn);
    layout2->addWidget(play_btn);
    layout2->addWidget(next_btn);
    layout2->addWidget(volume_btn);
    layout2->addWidget(volume_slider);
    layout2->addStretch();

    QVBoxLayout * layout = new QVBoxLayout();
    layout->addLayout(layout1);
    layout->addLayout(layout2);

    this->setLayout(layout);


    connect(volume_slider, &QSlider::valueChanged, this, &ControlPanel::volumeSliderChanged);
    connect(play_btn, &QPushButton::clicked, this, &ControlPanel::causePlayBtnClicked);
    connect(stop_btn, &QPushButton::clicked, this, &ControlPanel::causeStopBtnClicked);
    connect(last_btn, &QPushButton::clicked, this, &ControlPanel::causeLastBtnClicked);
    connect(next_btn, &QPushButton::clicked, this, &ControlPanel::causeNextBtnClicked);

}

void ControlPanel::updateProgress(int64_t milli_sec)
{
    int64_t total_seconds;
    int seconds;
    int minutes;
    QString sec;
    QString min;
    total_seconds = static_cast<int64_t>(milli_sec / 1000);
    minutes = static_cast<int>(total_seconds / 60);
    seconds = static_cast<int>(total_seconds % 60);
    sec = QString::number(seconds);
    min = QString::number(minutes);
    current_time = min + ":" + sec;
    time_label->setText(current_time + total_time);

    int bar_percentage;
    float percentage;
    if(total_millisec == 0)
    {
        return;
    } else {
        percentage = static_cast<float>( milli_sec )/ total_millisec;
        bar_percentage = static_cast<int>(percentage * 100);
    }

   music_progress->setValue(bar_percentage);
}

//设定当前歌曲总时长
void ControlPanel::changeTotalTime(qint64 duration)
{
    total_millisec = duration;
    int64_t total_seconds;
    int seconds;
    int minutes;
    QString sec;
    QString min;
    total_seconds = static_cast<int64_t>(duration / 1000);
    minutes = static_cast<int>(total_seconds / 60);
    seconds = static_cast<int>(total_seconds % 60);
    sec = QString::number(seconds);
    min = QString::number(minutes);
    total_time = "/" + min + ":" + sec;
    time_label->setText(current_time + total_time);
}

void ControlPanel::causeNextBtnClicked()
{
    emit nextBtnClicked();
}

void ControlPanel::causeLastBtnClicked()
{
    emit lastBtnClicked();

}

void ControlPanel::volumeSliderChanged(int value)
{
    emit volumeChanged(value);
}

void ControlPanel::causePlayBtnClicked()
{
    emit playBtnClicked();
}

void ControlPanel::causeStopBtnClicked()
{
    emit stopBtnClicked();
}

ControlPanel::~ControlPanel()
{

}

这个模块的作用总结来说是:发出五个信号,接受两个信息。五个信号指的是播放、停止、下一首、上一首、以及音量改变。两个信息指的是当前歌曲总时长、当前歌曲已经播放时长。

播放器模块

这里要谈的是QMediaPlayer的用法,这个类可以用来播放音频、视频、网络媒体。以本地音频上的具体用法为例,如下所示:

player = new QMediaPlayer;
//新建一个媒体播放器类
connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
//当这个媒体播放器的进度改变时会发出一个信号指示当前进度,我们要写一个槽函数用来处理这个信号
player->setMedia(QUrl::fromLocalFile("/Users/me/Music/coolsong.mp3"));
//设置要播放的媒体
player->setVolume(50);
//设置音量
player->play();
//播放

还有更多用法以及更详细的说明,见官方帮助文档。

顶层模块

用来将各个子模块连接为一个整体

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
class QMediaPlayer;
class QMediaPlaylist;
class ControlPanel;
class QSplitter;
class MusicWord;
class QMediaContent;
class QLabel;
class MusicTreeList;

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

    enum PlayState {
        STOP = 1,
        PLAYING = 2,
        PAUSE = 3
    };


public slots:
    void playMusic();//用来接受控制面板发出的播放信号
    void setMusic(QString music_name);//用来接受音乐树目录发来的歌曲信息,用来设置当前的媒体源
    void setVolume(int i);//用来接受控制面板发出的改变音量信号
    void stopPlayer();//用来接受控制面板发出的停止信号
    void playNext();//接受控制面板发出的下一首信号
    void playLast();//接受控制面板发出的上一首信号

private:
    QMediaPlayer * music_player;
    QMediaPlaylist * play_list;
    ControlPanel * control_panel;
    MusicTreeList * music_tree_list;
    QLabel * music_title;//用来显示当前歌曲
    QString path;//歌曲路径
    QString project_path;//我的工程路径

    int player_state;//播放器状态

};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "contro_panel.h"
#include "music_word.h"
#include <QMediaPlayer>
#include <QPushButton>
#include "QFileDialog"
#include "QFileInfo"
#include "QHBoxLayout"
#include "QVBoxLayout"
#include "QGridLayout"
#include "QMediaPlaylist"
#include "QSplitter"
#include "QMediaContent"
#include "QLabel"
#include "QFont"
#include "music_tree_list.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    player_state = Widget::STOP;
    project_path = "C:/Users/jiage/Desktop/folder/QT/music_player/music";
    this->setWindowIcon( QIcon(":/picture/music.png") );
    control_panel = new ControlPanel();
    music_tree_list = new MusicTreeList;
    music_title = new QLabel();
    music_title->setAlignment(Qt::AlignHCenter);
    music_title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
    QFont ff;
    ff.setPointSize(20);
    music_title->setFont(ff);


    QSplitter * main_splitter = new QSplitter();
    main_splitter->addWidget(music_tree_list);
    main_splitter->addWidget(music_title);
    main_splitter->setSizes(QList<int>{200, 600});


    //播放器引擎对象
    music_player = new QMediaPlayer();
    music_player->setVolume(10);
    control_panel->volume_slider->setValue(10);

    play_list = new QMediaPlaylist;

    QVBoxLayout *layout = new QVBoxLayout();

    layout->addWidget(main_splitter);
    layout->addWidget(control_panel);

    this->setLayout(layout);
    this->resize(550, 350);


    connect(music_tree_list, &MusicTreeList::currentMusic, this, &Widget::setMusic);
    connect(control_panel, &ControlPanel::playBtnClicked, this, &Widget::playMusic);
    connect(control_panel, &ControlPanel::stopBtnClicked, this, &Widget::stopPlayer);
    connect(control_panel, &ControlPanel::nextBtnClicked, this, &Widget::playNext);
    connect(control_panel, &ControlPanel::lastBtnClicked, this, &Widget::playLast);
    connect(control_panel, &ControlPanel::volumeChanged, this, &Widget::setVolume);
    connect(music_player, &QMediaPlayer::durationChanged, control_panel, &ControlPanel::changeTotalTime);
    connect(music_player, &QMediaPlayer::positionChanged, control_panel, &ControlPanel::updateProgress);


}

Widget::~Widget()
{

}

//播放音乐
void Widget::playMusic()
{
    if( player_state == Widget::STOP || player_state == Widget::PAUSE ) {
        music_player->play();
        player_state = Widget::PLAYING;
        control_panel->play_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaPause));
    } else {
        music_player->pause();
        player_state = Widget::PAUSE;
        control_panel->play_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaPlay));
    }
}

//设定当前音乐
void Widget::setMusic(QString music_name)
{
    control_panel->play_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaPlay));
    player_state = Widget::STOP;
    path = project_path + music_name;
    music_player->setMedia(QUrl::fromLocalFile(path));
    music_name.replace(QRegExp("(/(.*)/)"), "");
    music_name.replace(QRegExp(".mp3"), "");
    music_title->setText(music_name);
}

//设置音量
void Widget::setVolume(int i){
    music_player->setVolume(i);
}

//停止播放
void Widget::stopPlayer()
{
    player_state = Widget::STOP;
    control_panel->play_btn->setIcon(this->style()->standardIcon(QStyle::SP_MediaPlay));
    music_player->stop();
}


//播放下一首
void Widget::playNext()
{
    //play_list->next();
    qDebug() << "下一首";
}


//播放上一首
void Widget::playLast()
{
    //play_list->previous();
    qDebug() << "上一首";
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!