花了兩個禮拜的時間在qt平臺上做了一箇中等的音樂播放器,以下是一些.cpp和.h文件僅供參考,這個播放器還有一些功能需要完善,比如說顯示歌詞,上網搜索歌曲等等。在整個播放器的製作過程當中,首先需要了解一下一個音樂播放器需要用到哪些模塊,比如說資源管理器,音樂信息對象,物理連接設備等等,分清楚這些模塊它們各自的功能,然後再開始做,這樣要稍微容易一些。有一個問題需要注意一下,雖然說這是一個小小的問題,但也花了我幾天的時間,問題就是在新建的pro裏面沒有加phonon,因爲音樂播放器在播放歌曲的時候需要phonon對歌曲進行解析,纔可以播放,所以值得注意。
主窗口加列表截圖如下:
如下是widget.cpp文件
#include "widget.h"
#include "ui_widget.h"
#include "musiclist.h"
#include <QTime>
#include <QUrl>
#include <QtGlobal>
#include <QtGui>
int num = 0;
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this); // 建立主窗口
// 一個音樂文件列表建立
list= new musiclist(this);
QString str = tr("播放列表"); // 設置窗口的名稱
list->setWindowTitle(str); // 如果是這條代碼(list->setWindowTitle("音樂播放器")),則會顯示亂碼
list->setFixedSize(285, 300); // 設置窗口的長和寬
list->installEventFilter(this); // object 事件過濾器,比如說是鼠標點擊,移動等等
list->ui->tableWidget->installEventFilter(this);
list->ui->tableWidget->setColumnWidth(1, 100); // 調節第1列的寬度
list->ui->tableWidget->setColumnHidden(0, 100); // 調節第0列的寬度
readSettings(); // 讀取主窗口和列表寬的位置
// 主窗口模塊
QString str2 = tr("音樂播放器 (陳偉東製作)");
this->setWindowTitle(str2);
this->setFixedSize(280, 150);
this->ui->listWidget->setStyleSheet("background-image:url(images/3.jpg)"); // 添加背景圖片,具體參考QT收藏資料,也可以使用另外一種方法
// 建立媒體管理
musicInformationMediaObject = new Phonon::MediaObject(this); // 音樂信息對象
mediaObject = new Phonon::MediaObject(this); // 分配一個資源管理器
audioOutput = new Phonon::AudioOutput(); // 有些時候是 audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory),它的作用是將源文件映射到物理設備
Phonon::createPath(mediaObject, audioOutput); // 用creatPath函數將資源管理器和audioOutput連接起來
creation(); // 初始化歌曲器件,以及托盤menu的一些菜單
// 聲音控制模塊
volumeSlider = new Phonon::VolumeSlider(this); // 在主窗口裏申請一個volumSlider
volumeSlider->setAudioOutput(audioOutput); // 使得音量控制器和音樂文件連接起來
volumeSlider->move(175, 130);
volumeSlider->resize(20, 20);
volumeSlider->setFixedWidth(90); //固定音量條寬度的大小
// 進度條控制及設置模塊
seekSlider = new Phonon::SeekSlider(this); // 在主窗口中分配seekSlider 函數中的this必需得加上去,因爲this代表主窗口的意思
seekSlider->setMediaObject(mediaObject); // 使得進度條和歌曲進度一致
seekSlider->move(2, 2); // 設置進度條在主窗口的哪個位置 做上角爲(0,0)
seekSlider->resize(265, 20); // 設置進度條寬度
// 媒體狀態發生變化時,有如下信號與槽的關係
connect(mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(mediaObjectStateChanged(Phonon::State,Phonon::State)));
// 雙擊音樂音樂列表裏面的音樂時使得點到的歌曲播放
connect(list->ui->tableWidget, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(tableWidget_cellDoubleClicked(int,int)));
// 音樂列表和主窗口裏(列表)按鈕的連接
connect(list, SIGNAL(listClose()), this->ui->toolButton, SLOT(toggle()));
// 在當前的歌曲快要播放完時
connect(mediaObject, SIGNAL(aboutToFinish()), this, SLOT(musicAboutToFinish()));
connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)), this, SLOT(currentMusicSourceChanged(Phonon::MediaSource)));
// 當musicInformationMediaObject信息對象發生變化時,建立如下槽和信號的連接
connect(musicInformationMediaObject,SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(musicInformationStateChanged(Phonon::State,Phonon::State)));
// 音樂文件的時間顯示與顯示建立信號與槽的聯繫
connect(mediaObject,SIGNAL(tick(qint64)),this,SLOT(showTime(qint64)));
connect(this,SIGNAL(mySignal()),this,SLOT(close()),Qt::QueuedConnection);
}
Widget::~Widget()
{
delete ui;
}
void Widget::tableWidget_cellDoubleClicked(int row, int column) // 雙擊一首歌曲
{
mediaObject->stop();
mediaObject->clearQueue(); // clear the quenue of the media object
if (row > sourceList.size())
{
return;
}
mediaObject->setCurrentSource(sourceList.at(row)); // 設置mediaobject當前的歌曲
mediaObject->play();
}
void Widget::currentMusicSourceChanged(Phonon::MediaSource source) // 當自動播放下一曲音樂時,在tableWidget中讓它爲選中狀態
{
list->ui->tableWidget->selectRow(sourceList.indexOf(source));
mediaObject->pause(); // 在此改變一下狀態觸發mediaObjectStateChanged槽函數
}
// 音樂信息變化時,比如說增加一些歌曲,或者是清空列表等等
void Widget::musicInformationStateChanged(Phonon::State newState,Phonon::State)
{
if (newState != Phonon::StoppedState && newState != Phonon::PausedState)
return ;
if (musicInformationMediaObject->currentSource().type() == Phonon::MediaSource::Invalid)
return ;
/*
剛開始,媒體對象會進入Phonon::LoadingState狀態,此時後端進行資源的載入。
當Phonon::LoadingState完成後就會進入Phonon::StoppedState 狀態,
這時才能添加新的資源。
*/
QMap<QString, QString> metaData = musicInformationMediaObject->metaData();
int rowNum = list->ui->tableWidget->rowCount();
list->ui->tableWidget->insertRow(rowNum); // 插入一行空行
QString title = metaData.value("TITL"); // 歌曲名稱
if (title == "")
{
QString fileName = musicInformationMediaObject->currentSource().fileName();
QString mp3Name = fileName.right(fileName.length() - fileName.lastIndexOf('/') - 1);
title= mp3Name; // 花括號裏面的語句作用是求出音樂文件的name
}
QTableWidgetItem *titleItem = new QTableWidgetItem(title);
list->ui->tableWidget->setItem(rowNum,0,titleItem);
QTableWidgetItem *artistItem = new QTableWidgetItem(metaData.value("ARTIST"));
// 歌手名稱
list->ui->tableWidget->setItem(rowNum,1,artistItem);
// 讓列表顯示時間還沒有解決
qint64tTime = mediaObject->totalTime();
QTime tolTime(0,(tTime / 60000) % 60,(tTime / 1000) % 60);
QString time = tolTime.toString("mm:ss");
QTableWidgetItem *totalTime = new QTableWidgetItem(time); //專輯
list->ui->tableWidget->setItem(rowNum,2,totalTime);
list->ui->tableWidget->close();
list->ui->tableWidget->show(); // 列表信息發生變化,先關掉,然後再顯示
if (list->ui->tableWidget->selectedItems().isEmpty())
{ //如果沒有選擇行
list->ui->tableWidget->selectRow(0); //選擇第一行
mediaObject->setCurrentSource(musicInformationMediaObject->currentSource());
}
int index = sourceList.indexOf(musicInformationMediaObject->currentSource()) + 1;
if (sourceList.size() > index)
{
musicInformationMediaObject->setCurrentSource(sourceList.at(index));
}
}
void Widget::mediaObjectStateChanged(Phonon::State, Phonon::State)
{
switch(mediaObject->state())
{
case Phonon::PlayingState:
{
QString fileName = mediaObject->currentSource().fileName();
QString title = fileName.right(fileName.length() - fileName.lastIndexOf('/') - 1);
this->ui->label->setText(title);
trayicon->setToolTip(title);
break;
}
case Phonon::StoppedState:
trayicon->setToolTip("停止" );
break;
case Phonon::PausedState:
trayicon->setToolTip("暫停");
break;
case Phonon::LoadingState:
trayicon->setToolTip("加載");
break;
case Phonon::ErrorState:
trayicon->setToolTip("錯誤" );
break;
case Phonon::BufferingState:
trayicon->setToolTip("緩衝");
break;
default:
break;
}
}
void Widget::creation()
{
/* QIcon iconremove("images/remove.png"); // 爲按鍵分配圖標
remove = new QAction(iconremove,tr("清空播放列表"),this);
QIcon iconsound("images/sound.png");
QIcon iconstop("images/gtk-media-stop.png");
QIcon iconopen("images/gtk-open.png");
QIcon iconexit("images/exit.png"); */
remove= new QAction(tr("清空"),this);
connect(remove,SIGNAL(triggered()),this,SLOT(removelist()));
sound= new QAction( tr("靜音"), this);
sound->setCheckable(true);
connect(sound,SIGNAL(triggered(bool)),audioOutput ,SLOT(setMuted(bool)));
stop= new QAction(tr("停止"),this);
connect(stop,SIGNAL(triggered()), this, SLOT(stopSlot()));
open= new QAction( tr("打開"),this);
connect(open, SIGNAL(triggered()), this, SLOT(on_openFileBtn_clicked()));
exit= new QAction(tr("退出"),this);
connect(exit,SIGNAL(triggered()),this,SLOT(exitSlot()));
QIcon ico("images/VMP2.png");
trayicon = new QSystemTrayIcon(this);
trayicon->setIcon(ico);
trayicon->setToolTip(tr("正在載入"));
menu= new QMenu(this);
menu->addAction(remove);
menu->addAction(open);
menu->addAction(sound);
menu->addSeparator();
menu->addAction(stop);
menu->addAction(exit);
trayicon->setContextMenu(menu);
trayicon->show();
trayicon->showMessage(tr("音樂播放器"),tr("歡迎你使用本軟件"),QSystemTrayIcon::Information,10000);
connect(trayicon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this,SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));//圖標被激活時
}
void Widget::exitSlot()
{
this->hide();
this->close();
}
void Widget::stopSlot()
{
mediaObject->stop();
}
void Widget::removelist()
{
mediaObject->stop();
int row = list->ui->tableWidget->rowCount();
for (int i = 0; i < row; i++)
list->ui->tableWidget->removeRow(0);
list->ui->tableWidget->close();
list->ui->tableWidget->show();
mediaObject->clear();
sourceList.clear();
}
void Widget::iconActivated(QSystemTrayIcon::ActivationReason reason)
{
switch(reason)
{
case QSystemTrayIcon::Trigger://單擊
this->setWindowState(Qt::WindowActive);//恢復窗口顯示
this->show();
break;
case QSystemTrayIcon::DoubleClick://雙擊
this->hide();
list->hide();
break;
case QSystemTrayIcon::MiddleClick:
trayicon->showMessage(tr("音樂播放器"),tr("歡迎使用本軟件"),QSystemTrayIcon::Information,10000);
break;
default:
;
}
}
void Widget::on_openFileBtn_clicked()
{
QStringList files = QFileDialog::getOpenFileNames(this, tr("Selec Files to play"), QDesktopServices::storageLocation(QDesktopServices::MusicLocation)); // 從系統當中添加音樂文件
QString file;
int index = sourceList.size(); // 計算音樂列表當中有多少個文件
foreach(file, files) // 添加音樂file
{
Phonon::MediaSource source(file);
sourceList.append(source); // 使得添加進來的file按循序排列
}
if (!sourceList.isEmpty()) // 將音樂信息對象的當前文件指定爲還沒有加文件前的位置
musicInformationMediaObject->setCurrentSource(sourceList.at(index));
}
void Widget::readSettings() // 讀取主窗口的位置信息
{
QSettings settings("ecjtu", "Phonon Example");
QPoint pos = settings.value("pos", QPoint(400, 200)).toPoint();
move(pos);
//QSize size = settings.value("size", QSize(298,412)).toSize();
//resize(size);
}
void Widget::writeSettings() // 記錄主窗口的位置信息
{
QSettings settings("ecjtu","Phonon Example");
settings.setValue("pos", pos());
//settings.setValue("size", size());
}
void Widget::on_playOrpause_clicked() // 音樂文件在音樂列表當中的排列是:0 1 2 3 4 5 6
{
if(sourceList.isEmpty()) // 判斷音樂列表是否爲空
{
QMessageBox::information(this, tr("警告"), tr("no files to play")); // 當沒有音樂時, 顯示警告信息
return ;
}
if (mediaObject->state() == Phonon::PlayingState) // 如果按下暫停按鈕
mediaObject->pause(); // 暫停播放歌曲
else
mediaObject->play();; // 按音樂列表播放歌曲
}
void Widget::on_previousMusic_clicked()
{
int index = sourceList.indexOf(mediaObject->currentSource())-1; // 找到當前歌曲的序列號
if (index >= 0)
{
mediaObject->stop();
mediaObject->setCurrentSource(sourceList.at(index)); // 設置當前播放歌曲下一首的序列號
mediaObject->play();
}
else
QMessageBox::information(this, tr("警告"), tr("沒有上一首歌曲"));
}
void Widget::on_nextMusic_clicked()
{
int index = sourceList.indexOf(mediaObject->currentSource()) + 1;// 獲取當前播放類表歌曲的位置然後加一表示下一首的歌曲
if (index < sourceList.size()) // 利用sourceList.size()來得到列表當中的音樂文件個數
{
mediaObject->stop();
mediaObject->setCurrentSource(sourceList.at(index)); // 播放指定index的歌曲
mediaObject->play();
}
else
QMessageBox::information(this, tr("警告"), tr("沒有下一首歌曲"));
}
void Widget::musicAboutToFinish() // 本首音樂快結束時
{
int index = sourceList.indexOf(mediaObject->currentSource()) + 1;
mediaObject->setCurrentSource(sourceList.at(index)); // 注意:不會產生狀態的改變state
mediaObject->play();
}
void Widget::showTime(qint64 time) // 顯示歌曲的時間進度
{
qint64temp = mediaObject->totalTime();
QTime totalTime(0,(temp / 60000) % 60,(temp / 1000) % 60,time %1000);
QTime curTime(0,(time / 60000) % 60,(time / 1000) % 60,time %1000);
ui->showTime->setText(tr("%1 / %2")
.arg(curTime.toString("mm:ss"))
.arg(totalTime.toString("mm:ss")));
ui->showTime->update();
}
void Widget::on_toolButton_clicked(bool checked)
{
num++;
if (num % 2 == 1)
{
list->listReadSettings();
list->show();
}
else
{
list->listWriteSettings();
list->hide();
}
}
void Widget::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void Widget::closeEvent(QCloseEvent *e)
{
writeSettings();
list->listWriteSettings();
if (this->isVisible())
{
this->hide();
list->hide();
trayicon->showMessage(tr("MusicPlay"), tr("最小化"),QSystemTrayIcon::Information,15000);
e->ignore();
}
}
bool Widget::eventFilter(QObject *object, QEvent *event)
{
qDebug() << object;
if(object == list->ui->tableWidget) // 過濾表格的事件
{
qDebug() << "event" << event;
if(event->type() == QEvent::ContextMenu)
{
QContextMenuEvent *contextevent = static_cast<QContextMenuEvent *>(event);
QMenu menu(this);
menu.addAction(remove);
menu.exec(contextevent->globalPos());
return true;
}
else if(event->type() == QEvent::DragEnter)
{
QDragEnterEvent *dragEnterevent = static_cast<QDragEnterEvent *>(event);
if(dragEnterevent->mimeData()->hasFormat("text/uri-list"))
dragEnterevent->acceptProposedAction();
return true;
}
else if(event->type() == QEvent::Drop)
{
QDropEvent *dropevent = static_cast<QDropEvent *>(event);
dropEvent(dropevent);
return true;
}
else return false;
}
return QWidget::eventFilter(object,event);
}
void Widget::dragEnterEvent(QDragEnterEvent *e)
{
if(e->mimeData()->hasFormat("text/uri-list"))
e->acceptProposedAction();
}
void Widget::dropEvent(QDropEvent *e)
{
QList<QUrl> urls= e->mimeData()->urls();
QString fileName = urls.first().toLocalFile();
int index = sourceList.size(); //獲取當前列表的長度
for(int i=0; i<urls.length(); i++)
{
QString string = urls.at(i).toString();
Phonon::MediaSource source(string);
sourceList.append(source);//在當前列表後加入一條記錄
}
if(!sourceList.isEmpty())
musicInformationMediaObject->setCurrentSource(Phonon::MediaSource(sourceList.at(index)));
}
void Widget::moveEvent(QMoveEvent *e)
{
if(isTouched)
{
QPoint p = e->pos() - e->oldPos();
list->move(list->pos() + p);
}
else
{
isTouched = false;
if(qAbs(list->y()- this->y() - this->frameGeometry().height()) < 20)
{
isTouched = true;
this->move(this->x(),list->y()+this->frameGeometry().height());
}
if(qAbs(this->x() - list->x() - list->frameGeometry().width()) < 20)
{
isTouched = true;
this->move(list->x()+list->frameGeometry().width(),this->y());
}
if(qAbs(this->x() + this->frameGeometry().width() - list->x()) < 20)
{
isTouched = true;
this->move(list->x()-this->frameGeometry().width(),this->y());
}
}
QWidget::moveEvent(e);
}
如下是musiclist.h
#ifndef MUSICLIST_H
#define MUSICLIST_H
#include <QWidget>
namespace Ui {
class musiclist;
}
class musiclist : public QWidget
{
Q_OBJECT
public:
explicit musiclist(QWidget *parent = 0);
~musiclist();
Ui::musiclist *ui;
void listReadSettings();
void listWriteSettings();
protected:
void changeEvent(QEvent *e);
void moveEvent(QMoveEvent *);
signals:
void listClose();
};
#endif // MUSICLIST_H
如下是widget.h文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <phonon>
#include "musiclist.h"
#include "ui_musiclist.h"
#include <QTimer>
#include <QSystemTrayIcon>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void creation();
protected:
void changeEvent(QEvent *e);
void closeEvent(QCloseEvent *e);
void dragEnterEvent(QDragEnterEvent *);
bool eventFilter(QObject *, QEvent *);
void dropEvent(QDropEvent *);
void moveEvent(QMoveEvent *);
private:
Ui::Widget *ui;
musiclist *list;
Phonon::MediaObject *mediaObject;
Phonon::MediaObject *musicInformationMediaObject;
Phonon::AudioOutput *audioOutput;
Phonon::SeekSlider *seekSlider;
Phonon::VolumeSlider *volumeSlider;
QList<Phonon::MediaSource> sourceList;//播放列表
void readSettings();
void writeSettings();
QAction *remove;
QAction *open;
QAction *exit;
QAction *sound;
QAction *stop;
QMenu *menu;
QSystemTrayIcon *trayicon;
bool isTouched;
private slots:
void removelist();
void tableWidget_cellDoubleClicked(int row, int column);
void musicInformationStateChanged(Phonon::State newState,Phonon::State);
void on_toolButton_clicked(bool checked);
void on_nextMusic_clicked();
void on_previousMusic_clicked();
void on_playOrpause_clicked();
void on_openFileBtn_clicked();
void showTime(qint64);
void musicAboutToFinish();
void currentMusicSourceChanged(Phonon::MediaSource source);
void iconActivated(QSystemTrayIcon::ActivationReason reason);
void mediaObjectStateChanged(Phonon::State, Phonon::State);
void exitSlot();
void stopSlot();
signals:
void mySignal();
};
#endif // WIDGET_H
如下是musiclist.cpp文件
#include "musiclist.h"
#include "ui_musiclist.h"
#include <QSettings>
musiclist::musiclist(QWidget *parent) :
QWidget(parent),
ui(new Ui::musiclist)
{
ui->setupUi(this);
listReadSettings();
ui->tableWidget->setAcceptDrops(true);
this->setWindowFlags(Qt::Tool);
}
musiclist::~musiclist()
{
delete ui;
}
void musiclist::listReadSettings() // 讀取application位置及大小信息
{
QSettings settings("ecjtu", "Phonon LIST");
QPoint pos = settings.value("pos", QPoint(this->x(), this->y())).toPoint();
QSize size = settings.value("size", QSize(300, 320)).toSize();
move(pos);
resize(size);
}
void musiclist::listWriteSettings() // 記錄application的位置及大小信息
{
QSettings settings("ecjtu", "Phonon LIST");
settings.setValue("pos", pos());
settings.setValue("size", size());
}
void musiclist::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void musiclist::moveEvent(QMoveEvent *e)
{
if(qAbs((this->y()- this->parentWidget()->y() - this->parentWidget()->frameGeometry().height())) < 20)
{
this->move(this->x(),this->parentWidget()->y() + this->parentWidget()->frameGeometry().height());
}
if(qAbs(this->x()+this->frameGeometry().width() - this->parentWidget()->x()) < 20)
{
this->move(this->parentWidget()->x()-this->frameGeometry().width(),this->y());
}
if(qAbs(this->x()-this->parentWidget()->frameGeometry().width()-this->parentWidget()->x()) < 20)
{
this->move(this->parentWidget()->x()+this->parentWidget()->frameGeometry().width(),this->y());
}
QWidget::moveEvent(e);
}
如下是main.cpp文件
#include <QtGui/QApplication>
#include <QTextCodec>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
Widget w;
w.show();
return a.exec();
}