前言
就是像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大小
movie->setSpeed(200);//2倍速
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大小
movie->setSpeed(200);
ui->label->setMovie(movie);
movie->start();
i=0;
}
}
有點難受。。。摳了一天,摳了快12個小時,做的效果並不好。。。
由於是一張張的託過來做成GIF,所以伊芙利特的位置就不定。比起播放GIF,還是一幀幀的播放比較靠譜
(手很痛,以後再搞)
與任務欄交互
讓伊芙利特落在桌面底下,踩在任務欄上,根據任務欄的高低而起伏。
這是與其他窗口交互的第一步,先與任務欄交互。需要調用windows api,獲取窗口信息。
使用Microsoft spy++ 工具來查找窗口,爲了得到一個窗口信息,首先需要知道該窗口的類名和標題。任務欄的類名是Shell_TrayWnd,標題沒有。
比如下面是使用這個工具來查找伊芙利特窗口(設置窗口名:this->setWindowTitle("伊芙利特");
)
在pro文件中添加LIBS += user32.lib
添加頭文件#include <Windows.h>
//widget.cpp
//添加到paintEvent裏
if(!flag_clicked){//鼠標鬆手
RECT rect;
::GetWindowRect(::FindWindow(TEXT("Shell_TrayWnd"), NULL), &rect);
if(rect.top>500)//保證任務欄在桌面底下,不在其他位置
move(QPoint(pos().x(),rect.top-225));
}
與其他窗口碰撞
雙擊後可放縮、關閉
添加其他功能
參考: