QT之音樂播放器

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() << "上一首";
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章