QT平臺上製作的音樂播放器

花了兩個禮拜的時間在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();
}
 

 

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