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