QT之桌面精灵实现(一) --- 透明窗体和图片绘制

时间:2020-12-01 20:39:18

关于QT种种,之前已经吐槽过了,打算写几篇记录一下桌面精灵的制作过程,不过由于是过了挺长时间才想要写,所以之前的一些东西都忘得差不多了,总之想起多少写多少。


桌面精灵的第一步,就是要把一个精灵渲染到桌面,具体使用WINAPI做过一些,都不完美,也不想深挖WINAPI,因为我的目的只是想要把精灵渲染的桌面,而QT 很容易就能实现。


另外说一下渲染精灵到桌面其实有两种途径,一种是直接获取桌面窗口句柄(windows里桌面其实也只是一个窗口)然后直接在上面绘制或者渲染,某个把3D渲染到桌面就是这么做的,不过据说是利用了桌面其实有三层的原理,具体不是很清楚也没有深究。另一种就是想办法把窗口变透明,这种方法细分来目前知道三种,第一种是剪裁窗口,让窗口的形状符合你要画的形状,第二种是让窗口某种颜色变透明,第三种其实我并不怎么明白,貌似是直接使用了Alpha混合。第一种坑爹程度可以想象,第一种和第二种都不支持半透明,虽然桌面精灵很多时候不需要半透明,但是比如影子这类东西你不用半透明就是坑爹。


具体实现半透明其实后来知道FLASH也可以做到,但是要依靠AIR或者第三方软件,AIR比较坑爹,当你完成一个作品,发给别人想得瑟一下的时候别人告诉你运行不了,然后你告诉他你装个AIR就好了,于是别人兴致大减,如果丢在网上,估计很多人都懒得去装个AIR,因为有点经验的人知道电脑装的东西越多就越慢,而事实上AIR这种强行启动的软件也会导致开机变慢。而FLASH依靠第三方软件也可以实现直接打包成一个EXE就可以运行的,不过写惯了C++的人去写AS感觉就像是折磨,虽然实现速度来说其实AS还是会比较快的,但我还是希望能用C++,于是使用QT就顺理成章。


以上,都是废话,下面正式开始

1、透明窗口的实现

窗口标签+透明背景

DeskTopSprite(QWidget *parent = 0, Qt::WFlags flags =Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint|Qt::Tool);//构造函数
this->setAttribute(Qt::WA_TranslucentBackground);//设置背景透明
核心就上面两句,设置完以后随便绘制一张图片就是想要的效果。


2、图片绘制

没有这个就什么都没有了。做过窗口的都知道,绘制要在Paint之类的函数里,当然做AS应该不知道这个东西因为被封装掉了。QT里的绘制函数不需要什么消息映射,那种东西既麻烦又丑陋。QT只需要重写父类函数就可以,其实具体是不是重写我也不是很清楚,因为如果QT是用了名字识别的方法来调用这个函数那就不能叫重写了,因为我知道QT可以根据函数名自动绑定槽,所以实现这个机制并不难,如果是我的话我估计会考虑那样实现的,因为空的虚函数也是有那么点开销的。

具体实现:

//重写绘图事件
void DeskTopSprite::paintEvent(QPaintEvent *)
{
float nowTime = timer.elapsed()/1000.f;
deltaTime = nowTime - lastTime;
FrameUpdate();
QPainter painter(this);

for (size_t i = 0;i< renderQueue.size();i++)
{
RenderData& data = renderQueue[i];
QImage img = data.pic->toImage();
painter.drawImage(data.x,data.y,img.mirrored(data.bMirror, false));.
}

renderQueue.clear();

lastTime = nowTime;
}

头文件的声明想都知道该怎么写所以不贴了。绘制图片主要是用drawImage,img.mirrored(data.bMirror, false)是让图片是否翻转,返回的就是一张图片,具体可以查文档。

这里设计成了一个队列绘制,每次队列有新的数据才绘制,个人感觉这样比较好控制,具体喜欢怎么做自己选择吧,目的无法是想在需要画什么的时候就可以画什么。

顺便贴一下图片加载

QPixmap* DeskTopSprite::LoadPic( const QString& fn )
{
QPixmap *p = new QPixmap;
if(!p->load(fn))
{
QMessageBox::warning(this, tr("错误!"), tr("加载图片失败:")+fn);
Q_ASSERT(0);
return NULL;
}
picList.push_back(p);
return p;
}

加载进来的时候是QPixmap


3、拖拽

桌面精灵一个重要的功能就是拖拽,否则完全没有互动性了。原理和绘制是一样的,实现函数就能监听到事件。

void DeskTopSprite::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
oldPos = event->globalPos();
press = true;
LuaFunctionVoid fun = gLua->GetGlobal("OnHit");
fun(event->globalX () , event->globalY (), event->x(), event->y());
}
}

void DeskTopSprite::mouseMoveEvent(QMouseEvent *event)
{
if (press)
{
oldPos = event->globalPos();
LuaFunctionVoid fun = gLua->GetGlobal("OnMove");
fun(event->globalX () , event->globalY ());
}
}

void DeskTopSprite::mouseReleaseEvent(QMouseEvent *event)
{
press = false;
LuaFunctionVoid fun = gLua->GetGlobal("OnMouseRelease");
fun();
}

一般拖拽的实现都是靠这三个函数,具体细节自己去实现。这里我把函数映射到脚本里,在脚本里控制

function OnHit(x, y, localx, localy)
hitx,hity = x,y
ActorOnHit(actor)
end


function OnMove(x, y)
if not bDrag then
local len = (Vec2.new(x,y) - Vec2.new(hitx,hity)):len()
if len > startDragLen then
bDrag = true
end
end
if bDrag then
ActorOnDrag(actor, x, y)
end

end

function OnMouseRelease()
bDrag = false
OnActorRelease(actor)
end


剩下的事情就是把必要函数注册到脚本里,然后在脚本中对动画和位置进行控制,具体没有什么精心的设计,大多都是追求快速实现的代码。


我不怎么会写教程,具体想看源代码就去这里下http://pan.baidu.com/share/link?shareid=136176&uk=3087344852