前言
就是像QQ宠物一样,在电脑屏幕上放一个可交互的GIF……提前准备一批透明背景的GIF图和分解出来的透明背景的PNG图
无边框&透明背景窗口
建一个集成QMainWindow或者QWidget的QT项目。UI编辑里删除状态栏、工具栏、菜单栏。放入一个QLablel,我们就是用QLabel的setMovie方法来显示图片。把label设置为右键设置为栅格布局,并且记得右下角的布局属性设置为0
接着设置窗口透明、无边框、始终置于前面。这样运行会得到一个透明窗口,因为是透明的所以看不到,需要在任务栏关闭。
//widget.cpp
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//无边框//保证窗口一直在前面
setWindowFlags(Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
//透明背景
setAttribute(Qt::WA_TranslucentBackground);
}
实现窗口可拖动
首先需要填充一下背景,只有被填充的窗口部分才能接受到鼠标点击的信号——到后面我们播放透明背景的GIF图就明白了,透明的部分的窗口是可以穿过的,只有不透明的地方才能交互。
//widget.cpp
void Widget::paintEvent(QPaintEvent *event){
QPainter p(this);
//颜色填充
p.fillRect(6,6,width()-12,height()-12,QColor(232, 236, 247));
//图片填充
//位图填充
//QPixmap backBmp("../window_try_2/Background.bmp");
// p.drawPixmap(6,6,width()-12,height()-12,backBmp,0,0,backBmp.width(),backBmp.height());
}
可以重写一下mouseMoveEvent输出鼠标坐标,来看看是否接受到了鼠标消息。
接受到了鼠标消息,拖动就是靠这个消息来手动移动窗口。
//widget.h
protected:
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
bool flag_clicked; //左鼠标点击实现移动
QPoint currentPos; //屏幕上点击的坐标点
//widget.cpp
void Widget::mouseMoveEvent(QMouseEvent *event){
//移动窗口
if(flag_clicked){
QPoint tempPos = event->globalPos() - currentPos;
move(pos() + tempPos);
currentPos = event->globalPos();
}
}
void Widget::mousePressEvent(QMouseEvent *event){
if(event->buttons() == Qt::LeftButton){
//qDebug()<<"flag_clicked=true";
flag_clicked = true;
currentPos = event->globalPos();
}
}
void Widget::mouseReleaseEvent(QMouseEvent *event){
//qDebug()<<"flag_clicked=false";
flag_clicked = false;
}
播放GIF
文件太大了,只能放截图了。官方公布的两个GIF图,找不到透明背景的,所以只有自己处理了(这个之后讲)
先播放试试效果。使用QMovie类来加载GIF图,再使用QLabel的setMovie方法来显示。
//widget.cpp
//这个添加到构造函数里
//播放GIF
movie=new QMovie("../window_try_4/1.gif");
movie->setScaledSize(QSize(200,200));//设置GIF大小
//movie->setSpeed(100);//默认100%原始动画速度
ui->label->setMovie(movie);
movie->start();
保存为JPG
为了更好的操作,必须要PNG图。用在线的GIF转JPG没用,因为文件太大,转换出来的都坏了,所以还是自己来转换。
通过获取QMovie对象的当前图像,可以保存为JPG图片。另外每次QMovie对象的帧变化时会发射frameChanged信号,所以我们需要自定义一个槽函数on_saveAsJPG来关联一下。
通过这种方法,可以发现第一个GIF图有111帧,第二个GIF图有158帧……
//widget.cpp
void Widget::on_saveAsJPG(){
static int i=0;
if(i<movie->frameCount()){ //避免保存太多
qDebug()<<"save>"<<i;
QImage p = movie->currentImage();
p.save(QString::asprintf("E:/干员1_JPG/1_%d.jpg",i++),"JPG",100);//100表示未压缩
}
}
抠图变成PNG
无论你是想直接播放几个GIF(下面那步)还是想逐帧播放(下下面那步),都需要抠图(如果有现成的该多好)……
用PS打开一张JPG图片,用多边形套索选择出来。新建一个图层,把选择的内容复制到新的图层中,再把旧的图层删除,保存到PNG文件夹。
[抠图中]
合成几个GIF
拿第一个GIF分开的PNG图做成两个GIF,一个普通播放的,一个特殊交互的。
在鼠标点击后,设置特殊GIF播放标记,关掉普通GIF播放。在特殊GIF播放完前,再次点击不会再触发播放特殊的。通过记录帧数来判断是否播放完,播放完特殊的后,再次初始化标记,并且关掉特殊GIF,开启普通GIF。
——这里还需要关联一下frameChanged信号与paintEvent,因为这个函数并不是每次都会被执行。
//widget.cpp
void Widget::mousePressEvent(QMouseEvent *event){
if(event->buttons() == Qt::LeftButton){
//qDebug()<<"flag_clicked=true";
flag_clicked = true;
currentPos = event->globalPos();
if(!flag_special){ //如果之前播放的是普通GIF
qDebug()<<"special gif";
flag_special=true;
movie->stop();
delete movie;
movie=new QMovie("../window_try_4/yifulite2.gif");
movie->setScaledSize(QSize(200,200));//设置GIF大小
ui->label->setMovie(movie);
movie->start();
}
}
}
void Widget::paintEvent(QPaintEvent *event){
static int i=0;
if(flag_special&&i++>=movie->frameCount()){//在播放特殊GIF,并且放完了
qDebug()<<"normal gif";
flag_special=false;
movie->stop();
delete movie;
movie=new QMovie("../window_try_4/yifulite1.gif");
movie->setScaledSize(QSize(200,200));//设置GIF大小
ui->label->setMovie(movie);
movie->start();
i=0;
}
}
有点难受。。。抠了一天,抠了快12个小时,做的效果并不好。。。
逐帧绘制以增加交互感
参考: