Qt-鼠标长按式弹出菜单的一种实现方案
本文最后更新于:2022年10月26日 晚上
前情提要
菜单,所为何物?
餐馆里菜品的种类表嘛
核理 太河里了
今天的blog就写到这里,开饭了
end
在软件中,在大众印象里,菜单应该是右键单击后弹出来的那玩意儿:
横平竖直,死板而不富有生力
但是菜单项右侧的小箭头预示着 该项也是个菜单,可以开启二级菜单
同理可以开启多级菜单,aka 级联菜单
Qt
中,这玩意儿叫做QMenu
QMenu
中可以放入若干个菜单项:
- 可以是QAction
- 也可以是QMenu,构成级联树状结构
太好了,Qt
提供了QMenu
,太好用了,简直是救苦救难活菩萨
blog到这里又可以结束了 开饭了
欲求不满
太没追求了,太埋汰了,太丑了
他甚至不能自定义菜单项的位置,只能垂直排列
作为一个有追求的程序员,决不能就此罢休
让我们来康康Maya
的菜单:
我靠,什么叫Maya
,什么叫业内标杆,就一个字:newbility
哭闹
我也要我也要,不嘛不嘛,我就要,www
我不听我不听,别人有我也要
义眼盯针
论断:
这肯定是自绘菜单,肯定不是用的系统菜单(虽然QMenu也是Qt自绘的)
你问我怎么知道的?
我只能说:dddd,懂的都懂,不懂的也不必多说,这里面的水很深,不说也是为你好
正片(叠底
那么要如何实现这种效果呢?
大聪明
有人可能会说:一个menu不行,那就多个menu同时popup()
就可以了呀
聪明,太聪明了
但是经过实验发现,只有一个menu可以响应鼠标划过(hover),其他的均无反应
自绘
当然,自绘控件(窗体)肯定是最优解决方案,自定义程度最高,如:
这是一个图片查看器的右键菜单,长按右键弹出,并且可以实现花里胡哨的效果
但是要注意一个问题,一般的菜单都是可以实现级联的
如果要自绘实现级联,那代码量就可想而知了
所以这个方案好是好,但是我是懒狗,o(╥﹏╥)o
女娲补天
实际上,仔细观察Maya
的菜单,只有第一级菜单是持久显示的
只要解决了第一级菜单的显示和响应,后续菜单直接使用QMenu
即可
也就是只要实现第一级触发器即可
那么有什么能持久显示在窗体上呢
那么显然,除了QMenu
都可以…
好的,当然,经过层层筛选,发现QToolButton
是最合适的载体
因为这个控件自带菜单显示(setMenu方法)
补充一点:showMenu是阻塞方法(类似exec,内部循环),在QTimer
中用会阻断Timer
正片
根据高内聚低耦合精神,我们决定将菜单封装为一个单独的窗体
在窗体上放置QToolButton
作为菜单的载体,自然地解决了级联菜单问题
然后再将窗体设置为透明和鼠标穿透
Perfect
哦,我的上帝,太简单了
简直是原子核脑袋
还记得我们的标题吗:长按鼠标弹出菜单
参考:Apex长按中键弹出标记菜单,松开即可标记
所以这题真正的难点在于:按住鼠标按键,并响应菜单
一般人可能不觉得这有什么难的
像Apex和Maya自绘的话,也当然不是什么难题
但对于偷鸡取巧的我们来讲,这就差点被checkmate了
偷鸡,是要承担后果的
Windows窗口焦点系统
众所周知,只有焦点窗口才能接收鼠标和键盘输入
这其中,当属鼠标焦点最特殊
在一个窗口A内,按住鼠标任意按键,拖动鼠标至窗口B,Press & Move & Release消息都会发送给窗口A
导致作为菜单弹出的B无法正常响应鼠标(即便是转移焦点也不行)
也就是在窗口A中按住鼠标中键弹出菜单B的话,菜单B中的QToolButton
是无法正常响应鼠标的,也就无法自动弹出Menu
离奇的是弹出的QMenu
是可以响应鼠标hover的(怀疑是自行拦截了qApp事件)
所以我们的目标就是让QToolButton
响应鼠标并弹出Menu
唯一的方法是向qApp
安装事件过滤器,过滤所有mouseMove事件,控制菜单的弹出和关闭
当然细节很多,但是有了大思路,小细节只要耐心即可解决
具体可以参考:toolmenu.cpp · 蔡姬/Follower v2.0 - 码云 - 开源中国 (gitee.com)
Peace
Ref
qt - How to show the menu in QPushButton without blocking? - Stack Overflow