天天看點

給QT新手的練手項目——基于QT的GIF播放器

  本程式在ubuntu12.04、Qt5以及win7 64bit、Qt4均測試通過。

  最近閑來無事,想自己找幾個Qt的小項目做做,于是就從Qt自帶的示範程式着手。在Qt的自帶example有一個movie的小程式,位址在xxxQTExamplesx.xwidgetsmovie或/opt/Qt/5.1.0/gcc/examples/widgets/widgets中。這是一個用來播放gif檔案的程式,在ubuntu運作界面如下:

  其基本功能有載入檔案、暫停、停止、退出、快進,顯示播放速度以及根據視窗挑戰檔案大小。

  這個播放器已經實作了大多數的功能,但是我在調試過程中發現它的暫停功能并不能實作,快進快退隻能使用QSpinBox很不友善,最好內建到button中去。視窗調整的功能有點費,因為一般我們都希望視窗适應檔案的大小而不是檔案根據視窗大小來調整,而且使用這樣的調整會使得圖像失真,不好看。最後内置的movie鍵不是很好看,可以換一下。以下是最後改進後的界面:

  OK,現在就開始。首先建立一個Qt Gui程式命名為movie。選擇QWidget并命名為movieplayer,ui可選可不選,我們這不選:

  建立檔案後進入movieplayer.h,首先聲明一下要用到的庫檔案,這邊一次性把接下來所有要用到的都聲明一下:

#include<QWidget>
#include<QMovie>
#include<QLabel>
#include<QSpinBox>
#include<QToolButton>
#include<QSlider>
#include<QHBoxLayout>
#include<QGridLayout>
#include<QVBoxLayout>
#include<QIcon>
#include<QPixmap>
#include<QPushButton>
#include<QFileInfo>
#include<QFileDialog>      

  接着構造需要用到的控件,這裡需要使用者顯示的frameLabel和另外兩個QLabel,一個用于顯示播放速度的speedSpinBox,一個顯示進度的frameSlider以及另外幾個button控件。這裡需要強調一下,QToolButton一般用于菜單欄和QToolBar一起使用比較多,而菜單欄button一般是要替換掉圖示的,這也使我剛開始認為隻能使用這個,後來發現使用QPushButton其實也一樣,大家有時間可以實踐一下~還有我這裡隐藏了一個pause鍵,我的想法是,play和pause在一個位置放置,因為這兩個永遠是交替顯示的,是以放在一起可以節省空間,也更符合當下的主流設定。另外還有可以對檔案進行基本操作的QMovie。currentMovieDirectory是用來辨別檔案路徑的,以下是這些檔案的聲明:

QString currentMovieDirectory;      
QLabel *movieLabel;
QMovie *movie;
QToolButton *openButton;
QToolButton *playButton;
QToolButton *pauseButton;
QToolButton *stopButton;
QToolButton *quitButton;
QToolButton *speedPlusButton;
QToolButton *speedMinusButton;
QSpinBox *speedSpinBox;
QSlider *frameSlider;
QLabel *frameLabel;
QLabel *speedLabel;      

  QLayout的布置我的想法frameLabel單獨一個,用一個QGridLayout列出進度和速度空間命名為controlsLayout,用QGridLayout列出快進播放等空間命名為buttonsLayout,這一行加上open鍵命名為buttonsLayoutAll,最後整個布局用mainLayout。

QGridLayout *controlsLayout;
QHBoxLayout *buttonsLayoutAll;
QGridLayout *buttonsLayout;
QVBoxLayout *mainLayout;      

  下面進入movieplayer.cpp檔案,首先設定視窗屬性,這裡将視窗的大小設為500*500:

setWindowTitle(tr("Movie"));
resize(500,500);      

  設定movie的緩存模式,這裡我們要求當檔案播放一遍後傳回重新播放,是以使用CacheAll模式:

movie = new QMovie(this);
movie->setCacheMode(QMovie::CacheAll);      

  設定movieLabel,這裡我們設定縮放的方式為Expanding,這樣可以按照原來檔案的尺寸做出較好的調整,不至于被截取部分:

movieLabel=new QLabel(tr("NO movie loaded"));
movieLabel->setAlignment(Qt::AlignCenter);
movieLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
movieLabel->setBackgroundRole(QPalette::Dark);
movieLabel->setAutoFillBackground(true);      

  以下是兩個函數:

currentMovieDirectory="movies";

createControls();
createButtons();      

  在createControls中,注意幾個控件的布局要協調:

void movieplayer::createControls()
{
    frameLabel=new QLabel(tr("Current frame:"));
    frameLabel->setAlignment(Qt::AlignVCenter|Qt::AlignRight);

    frameSlider=new QSlider(Qt::Horizontal);
    frameSlider->setTickInterval(1);

    speedLabel=new QLabel(tr("Speed:"));
    speedLabel->setAlignment(Qt::AlignVCenter|Qt::AlignRight);

    speedSpinBox=new QSpinBox;
    speedSpinBox->setRange(1,500);
    speedSpinBox->setValue(movie->speed());
    speedSpinBox->setSuffix(tr("%"));

    controlsLayout=new QGridLayout;
    controlsLayout->addWidget(frameLabel,1,0);
    controlsLayout->addWidget(frameSlider,1,1,1,3);
    controlsLayout->addWidget(speedLabel,1,4);
    controlsLayout->addWidget(speedSpinBox,1,5);
}      

  在cteateButtons之前要把圖示檔案xxx.png添加到生成的.pro根目錄中去,然後右擊movie,添加新檔案,Qt資源檔案,定位到根檔案夾,命名為button,系統會自動生成button.qrc檔案。點選button.qrc檔案,在最下角字首輸入框将“/new/prefix1”改成“/”,然後點選添加,添加檔案,将幾個png檔案選擇添加,此時再點選button.qrc就會看到png檔案已經添加進來。

  然後就是button控件的建立。這裡調用QPixmap将圖檔添加到button中,設定控件固定大小為25*25,setAutoRaise可以将button的外邊框去除在滑鼠滑過該button時會有浮出效果顯示邊框,這個效果比較好看。setToolTip函數可以使滑鼠放在控件上時提示該控件可以執行的操作,然後調用信号槽做響應的Open(),Start()等操作。

  值得注意的是,play和pause控件将重疊在同一位置。

void movieplayer::createButtons()
{
    QSize iconSize(25,25);

    QPixmap icon1(":/use_001.png");
    openButton=new QToolButton;
    openButton->setIcon(icon1);
    openButton->setAutoRaise(true);
    openButton->setIconSize(iconSize);
    openButton->setToolTip(tr("Open a file"));
    connect(openButton,SIGNAL(clicked()),this,SLOT(Open()));

    QPixmap icon2(":/use_007.png");
    playButton=new QToolButton;
    playButton->setIcon(icon2);
    playButton->setIconSize(iconSize);
    playButton->setAutoRaise(true);
    playButton->setToolTip(tr("Play"));
    connect(playButton,SIGNAL(clicked()),this,SLOT(Start()));

    QPixmap icon3(":/use_008.png");
    pauseButton=new QToolButton;
    pauseButton->setIcon(icon3);
    pauseButton->setAutoRaise(true);
    pauseButton->setIconSize(iconSize);
    pauseButton->setToolTip(tr("Pause"));
    pauseButton->setVisible(false);
    connect(pauseButton,SIGNAL(clicked()),this,SLOT(Pause()));

    QPixmap icon4(":/use_002.png");
    stopButton=new QToolButton;
    stopButton->setIcon(icon4);
    stopButton->setAutoRaise(true);
    stopButton->setIconSize(iconSize);
    stopButton->setToolTip(tr("Stop"));
    connect(stopButton,SIGNAL(clicked()),this,SLOT(Stop()));

    speedMinusButton=new QToolButton;
    QPixmap icon5(":/use_005.png");
    speedMinusButton->setIcon(icon5);
    speedMinusButton->setAutoRaise(true);
    speedMinusButton->setIconSize(iconSize);
    speedMinusButton->setToolTip(tr("Speed up"));
    connect(speedMinusButton,SIGNAL(clicked()),this,SLOT(SpeedDown()));

    speedPlusButton=new QToolButton;
    QPixmap icon6(":/use_011.png");
    speedPlusButton->setIcon(icon6);
    speedPlusButton->setAutoRaise(true);
    speedPlusButton->setIconSize(iconSize);
    speedPlusButton->setToolTip(tr("Speed down"));
    connect(speedPlusButton,SIGNAL(clicked()),this,SLOT(SpeedUp()));

    buttonsLayout=new QGridLayout;
    buttonsLayout->addWidget(speedMinusButton,0,0);
    buttonsLayout->addWidget(playButton,0,1);
    buttonsLayout->addWidget(pauseButton,0,1);
    buttonsLayout->addWidget(speedPlusButton,0,2);
    buttonsLayout->addWidget(stopButton,0,3);

    buttonsLayoutAll=new QHBoxLayout;
    buttonsLayoutAll->addWidget(openButton);
    buttonsLayoutAll->addStretch();
    buttonsLayoutAll->addLayout(buttonsLayout);
    buttonsLayoutAll->addStretch();
}      

  接下來利用layout完成整個布局,以下是完整的構造函數:

movieplayer::movieplayer(QWidget *parent) :
    QWidget(parent)
{
    setWindowTitle(tr("Movie"));
    resize(500,500);

    movie = new QMovie(this);
    movie->setCacheMode(QMovie::CacheAll);

    movieLabel=new QLabel(tr("NO movie loaded"));
    movieLabel->setAlignment(Qt::AlignCenter);
    movieLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
    movieLabel->setBackgroundRole(QPalette::Dark);
    movieLabel->setAutoFillBackground(true);

    currentMovieDirectory="movies";

    createControls();
    createButtons();

    connect(movie,SIGNAL(frameChanged(int)),this,SLOT(updateFrameSlider()));
    connect(movie,SIGNAL(stateChanged(QMovie::MovieState)),this,SLOT(updateButtons()));
    connect(frameSlider,SIGNAL(valueChanged(int)),this,SLOT(goToFrame(int)));
    connect(speedSpinBox,SIGNAL(valueChanged(int)),movie,SLOT(setSpeed(int)));

    mainLayout=new QVBoxLayout;
    mainLayout->addWidget(movieLabel);
    mainLayout->addLayout(controlsLayout);
    mainLayout->addLayout(buttonsLayoutAll);
    setLayout(mainLayout);

    updateFrameSlider();
    updateButtons();
}      

  中間的信号槽想必不難了解,最後講調用兩個update函數更新控件狀态。

  接下來先對button控件的槽函數做一下定義,首先是Open(),首先得打開的路徑,QFileInfo(fileName).path()将記錄第一次打開檔案的路徑,當打開檔案後,movie就開始自動播放:

void movieplayer::openFile(const QString &fileName)
{
    currentMovieDirectory=QFileInfo(fileName).path();

    movie->stop();
    movieLabel->setMovie(movie);
    movie->setFileName(fileName);
    movie->start();
}      
void movieplayer::Open()
{
    QString fileName=QFileDialog::getOpenFileName(this,tr("Open a flie"),
                                                  currentMovieDirectory);
    if(!fileName.isEmpty())
    {
        openFile(fileName);

        pauseButton->setVisible(true);
        playButton->setVisible(false);
    }
}      

  Pause():

void movieplayer::Pause()
{
    movie->setPaused(true);
    playButton->setEnabled(true);
    pauseButton->setEnabled(false);
    pauseButton->setVisible(false);
    playButton->setVisible(true);
}      

  Start():

void movieplayer::Start()
{
    movie->start();
    playButton->setEnabled(false);
    playButton->setVisible(false);
    pauseButton->setEnabled(true);
    pauseButton->setVisible(true);
}      

  Stop():

void movieplayer::Stop()
{
    movie->stop();
    playButton->setEnabled(true);
    pauseButton->setEnabled(false);
    pauseButton->setVisible(false);
    playButton->setVisible(true);
}      

  SpeedUp()&SpeedDown(),這裡設定Speed的範圍是1—500,每按下一次控件增減的量為20:

void movieplayer::SpeedUp()
{
    qint32 currentSpeed=movie->speed();
    if(currentSpeed<=480)
    {
        movie->setSpeed(currentSpeed+20);
        speedSpinBox->setValue(currentSpeed+20);
    }
    else
    {
        movie->setSpeed(500);
        speedSpinBox->setValue(500);
    }
}

void movieplayer::SpeedDown()
{
    qint32 currentSpeed=movie->speed();
    if(currentSpeed>20)
    {
        movie->setSpeed(currentSpeed-20);
        speedSpinBox->setValue(currentSpeed-20);
    }
    else
    {
        movie->setSpeed(1);
        speedSpinBox->setValue(1);
    }
}      

  這裡還有一點要說明,當接受者為movie時,槽函數将調用系統函數,這也是QMovie的功能所在。其實這裡的Speed()函數時同樣可以調用movie->setSpeed(),隻是因為關聯控件的考慮才沒有用。

  下面是槽函數goToFrame,用以關聯frameSlider和movie:

void movieplayer::goToFrame(int frame)
{
    movie->jumpToFrame(frame);
}      

  最後是update函數,在updateFrameSlider中,首先設定frameSlider的最大值,如果打開的movie存在,将激活除pause外的所有控件:

void movieplayer::updateFrameSlider()
{
    bool hasFrame=(movie->currentFrameNumber()>=0);

    if(hasFrame)
    {
        if(movie->frameCount()>0)
            frameSlider->setMaximum(movie->frameCount()-1);
        else
        {
            if(movie->currentFrameNumber()>frameSlider->maximum())
                frameSlider->setMaximum(movie->currentFrameNumber());
        }
        frameSlider->setValue(movie->currentFrameNumber());
    }
    else
        frameSlider->setMaximum(0);

    frameLabel->setEnabled(hasFrame);
    frameSlider->setEnabled(hasFrame);
    speedMinusButton->setEnabled(hasFrame);
    speedPlusButton->setEnabled(hasFrame);
    stopButton->setEnabled(hasFrame);
}      

  updateButtons的設定是原來程式裡的,這裡沒有做修改0v0:

void movieplayer::updateButtons()
{
    playButton->setEnabled(movie->isValid()&&movie->frameCount()!=1
            &&movie->state()==QMovie::NotRunning);
    pauseButton->setEnabled(movie->state()!=QMovie::NotRunning);
    pauseButton->setCheckable(movie->state()==QMovie::NotRunning);
    stopButton->setEnabled(movie->state()!=QMovie::NotRunning);
}      

  經過上述步驟,就可以得到開頭所示的gif播放器了,加載了檔案如下:

  如果要生成可執行檔案,就要将預設的debug改為release,就可以生成了:

  在windows中往往需要将安裝目錄下/bin中的libgcc_s_dw2-1.dll、mingwm10.dll、QtCore4.dll、QtGui4.dll一并拷入exe所在的目錄,這樣就可以點選exe直接執行啦!!

  最後,如果你嫌預設的exe圖示不夠好看,你也可以将其替換成你喜歡的圖形。這裡需要強調,在之前的button中,圖像檔案可以是png、ico、jpeg等格式的,但是在這裡,系統隻接受ico格式的!

  将可執行檔案替換成自己想要的圖示需要以下幾個步驟:

  1、将你的ico檔案放入根目錄,我用的是i.icon;

  2、右擊movie添加新檔案,概要,文本檔案,選擇路徑并命名為app.rc;

  3、打開app.rc,隻添加一條指令為

IDI_ICON1 ICON DISCARDABLE "i.ico"      

  4、建構檔案

  5、打開pro檔案,在最尾處添加

RC_FILE=
    app.rc      

  6、重新建構釋出,就可以得到新的exe圖示:

       

  這個簡單的gif播放器來源于Qt自帶的example中,通過從頭到尾自己的建立,對于熟練掌握Qt的一些基本操作還是很有好處的,最後附上生成檔案,也算是個小軟體了吧,希望大家可以一起學習,共同進步!

  百度雲:http://pan.baidu.com/share/link?shareid=4263268927&uk=3641520234

  Eggif 1.0:http://pan.baidu.com/share/link?shareid=3500459208&uk=3641520234