给QT新手的练手项目——基于QT的GIF播放器

  本程序在ubuntu12.04、Qt5以及win7 64bit、Qt4均测试通过。

  最近闲来无事,想自己找几个Qt的小项目做做,于是就从Qt自带的演示程序着手。在Qt的自带example有一个movie的小程序,地址在xxx\QT\Examples\x.x\widgets\movie或/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

 

posted @ 2013-08-11 00:04  cityflickr  阅读(7673)  评论(4编辑  收藏  举报