ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Qt6 实现 主窗体外另开一个浮窗,并让它动起来

Qt6 实现 主窗体外另开一个浮窗,并让它动起来 Qt6 实现 主窗体外另开一个浮窗并让它动起来flyfish下载地址在文末主窗口里点一下屏幕别的地方冒出一张卡片、一条通知、一个悬浮工具条。它不是主窗口里嵌着的子控件而是飘在外面的独立窗口——能跨过主窗口的边界能被别的窗口盖住能被拖到屏幕任意位置。第一反应是把一个 QWidget 塞进主窗口再用 move / raise 想把它挪出去。但子控件永远被父窗口的矩形裁切出不了主窗口的边框。要让它真正出去得让它成为一个属于自己的顶层窗口。QWidget 什么时候是子控件什么时候是窗口Qt 里一个 QWidget 到底是不是独立窗口看的是 window flags不是有没有传父对象。// 普通用法parent 是主窗口 → 嵌在主窗口里的子控件auto*labelnewQLabel(parent);// 想让它成为独立窗口auto*wnewQWidget(parent);w-setWindowFlags(Qt::Window);// 关键给自己一个 Window 标志w-show();只要带了Qt::Window不管有没有传 parent它都会向系统要一个自己的原生窗口Windows 上是 HWNDmacOS 上是 NSWindow有自己的 z 序、能跑到父窗口外面。parent 在这个场景下只负责一件事主窗口关掉时它跟着被析构不用手动管生命周期。所以在主窗体外建窗体这句落到代码上就一句话给 QWidget 加Qt::Window标志它就是一个独立窗口了。做成无边框的浮窗带系统标题栏的窗口不适合做卡片、通知这类东西。去掉边框用Qt::FramelessWindowHintw-setWindowFlags(Qt::FramelessWindowHint|Qt::Window);w-setAttribute(Qt::WA_TranslucentBackground,true);// 背景透明圆角才干净WA_TranslucentBackground不能省。不透明窗口你画了圆角四个角会是方形的黑补丁背景透明之后paintEvent 里只画卡片本体其余区域天然透掉圆角和阴影才成立。阴影我直接挂一个QGraphicsDropShadowEffect它按你画出来的内容轮廓投影透明的地方不投影auto*effnewQGraphicsDropShadowEffect(w);eff-setBlurRadius(22);eff-setOffset(0,8);eff-setColor(QColor(0,0,0,130));w-setGraphicsEffect(eff);窗口本身要比卡片大一圈我四周各留 14px那圈是给阴影的呼吸空间不画任何东西。窗体动画顶层窗口动不了整体变换窗口独立了接下来的问题是怎么让它动。子控件可以用setTransform整体缩放但顶层窗口是系统级的Qt 不会替你做几何变换。能直接动的只有两样geometry()位置和尺寸和windowOpacity()透明度。我用一个弹簧去驱动它的中心坐标、缩放、透明度每帧把这四个值合成成真实的窗口矩形voidCardWindow::applyGeometry(){constdoublescm_scale.value;constintwqMax(1,qRound((m_fullW28)*sc));constinthqMax(1,qRound((m_fullH28)*sc));constintxqRound(m_cx.value-w/2.0);// 绕中心缩放constintyqRound(m_cy.value-h/2.0);setGeometry(x,y,w,h);setWindowOpacity(qBound(0.0,m_opacity.value,1.0));}位置我用的是中心坐标而不是左上角因为缩放是绕中心发生的。你要是拿左上角去乘缩放窗口会朝右下方被拽大看着像被拉出来的不是从中间长出来的。这一个坐标选择基本决定了动画真不真。弹簧本身不复杂五个字段当前值、目标值、速度、阻尼比、响应。每帧朝目标推一截。想弹出就把目标设到终点想中途打断就直接改目标当前值和速度原地接着用不会跳变。比起写死duration300ms的固定时长动画它天然就能被打断、能接住松手时的惯性。一个定时器驱动所有浮窗主窗口持有这些卡片的指针列表起一个约 60fps 的定时器每帧把每张卡片的弹簧都 step 一遍voidMainWindow::tickAll(){constqint64 nowm_clock.nsecsElapsed();boolmovingfalse;for(auto*c:m_cards)if(c-tick(now))movingtrue;if(!moving)m_timer-stop();// 都停了就停表不空转耗电}都在主窗体外在这里是实打实的含义这些卡片的geometry用的是屏幕全局坐标跟主窗口的矩形没有隶属关系。你拖主窗口卡片不动卡片飘在屏幕任意角落都行。它们只是逻辑上挂在主窗口底下方便统一调度和回收。收起完成的卡片要在定时器循环之外再删不然边遍历m_cards边delete会踩野指针// closed 信号只往待删列表里塞循环结束后再统一 deleteLaterfor(auto*c:m_toDelete){m_cards.removeAll(c);c-deleteLater();}下载地址
返回列表