开始
起因
一直挺烦那些繁琐又固定的操作,比如想打开某个文件夹、打开视频网站、打开文档手册等。这些工作都可以通过ahk脚本来完成,如打开文件夹和网页用run命令就可以。于是我便写了个整合脚本命令的ahk,可以方便的执行预定义的操作。
接下来,我会介绍它的使用,及出现的问题和改进,如果想看原理的话,在最后有简单的原理说明(很少)。
使用
长按鼠标中键即可弹出一串选项菜单,左键选择子项就可执行自定义行为了。
介绍
版本一
菜单的样式如下图。
它最多可以有19个菜单项,因为是使用Tooltip实现的,ahk规定最多只能同时存在20个。不过19也挺多了,如果没用太多需求的话……
但我使用了些许日子后,果然,19远远不够,还有了更大的问题——无分类显得杂乱。
于是我改进了第二版,增加了一个父级菜单用来分类,分类下是具体的子菜单。如此,分类可以有19个,而每个分类也有19个子项,足够我使用了!
版本二
菜单现在是这样的,父菜单:
子菜单:
我只做了些日需的分类,但仅仅使用了6个分类,还用13个分类可用喔。
代码
也有github地址喔,里面还有些ahk的小玩意:
https://github.com/refiz/ahk_vL_scripts.git
版本一
#SingleInstance, Force
CoordMode,ToolTip,Screen
CoordMode,Mouse,Screen
;========自定义
Global AllCharMenu:={ 1:"StartUp" ;在此按序号添加菜单项,需要自行添加及修改Label内容
,2:"Flypy.Com"
,3:"Ahk.Com"
,4:"Bili.Com"
,5:"AConvert.Com"
,6:"YouDao.Com"
,7:"Book"
,8:"Note"
,9:""
,10:""
,11:""
,12:""
,13:""
,14:""
,15:""
,16:""
,17:""
,18:""
,19:"TheMemo"}
Global SimpleCharMenu:={1:"笔记" ;此处为简字符菜单,适合圆形菜单形式
,2:"查形"
,3:"AHK"
,4:"哔哩"
,5:"转换"
,6:"翻译"
,7:"小说"
,8:"占十"
,9:"WEB"
,10:"占七"
,11:"占三"
,12:"虎牙"
,13:"占五"
,14:"占六"
,15:"JAPI"
,16:"UAPI"
,17:"菜鸟"
,18:"自启"
,19:"备忘"}
Global MenuStyle:="Circle" ;可选:长条形() 圆形(Circle)Circle
Global TipStyle:="SingleChar" ;可选:单字符(SingleChar) 全字符(AllChar)
Global Delay:=20 ;在此定义按住中键唤起菜单所需的时间
Global Radius:=50 ;在此定义圆形菜单的半径
Global MaxItems:=6 ;在此定义第一圈的最大项数,第二圈(最多)则为2*MaxItems项,总项数不超过19。关系为:(0-7]:[3-6],(0-9]:3,(9-13]:4,(13-16]:5,(16-19]:6
Global PI:=3.1415926
;========更换托盘图标相关,分激活与停用两种状态,双击托盘图标以停用脚本
Global IsOn:=True
Global IsStyle:=True
Global M_Open:="Open"
Global M_Close:="Close"
Menu,Tray,Icon,%A_Desktop%/Icon图集/MidMenu_Open.ico ;自备两个ICO
Menu,Tray,NoStandard ;去除默认菜单项
Menu,Tray,Add,Rect,M_MenuStyle
Menu,Tray,Add,%M_Close%,M_Toggle ;增加切换选项
Menu,Tray,Default,%M_Close% ;将切换选项设为默认
Menu,Tray,Click,2 ;设置双击响应
Menu,Tray,Add,&Exit,M_MenuExit ;添加退出按钮
#If IsOn
~MButton::
HowLong:=0 ;初始化
Loop{
HowLong++ ;计时
Sleep,10
If !(GetKeyState("MButton"))
Return
; ToolTip,% HowLong ;显示进度
} Until HowLong>=Delay ;达到预定时间
Send,{MButton} ;在浏览器中有用
OptimizeShowEffect() ;显示菜单
HotKey,~LButton,MenuClick ;设置单击热键,当单击时进行判断
If (GetKeyState("MButton"))
Return
HotKey,~LButton,On ;启用热键
Return
#If
MenuClick:
HotKey,~LButton,Off ;单击后关闭热键
Sleep,100 ;等待ToolTip被激活
Result:=CheckMenu() ;接受返回的数组
If Result[1]!=1 ;此次单击是否将菜单项激活
{
CleanMenu() ;未激活则清除菜单并退出
Return
}
OptimizeCloseEffect(Result[2]) ;优化菜单关闭效果
Gosub,% Result[2] ;追求效率将此条放在上条上面
Return
M_MenuStyle:
If(IsStyle)
{
Menu,Tray,Rename,Rect,Circle
MenuStyle:="Rect"
IsStyle:=False
}Else{
Menu,Tray,Rename,Circle,Rect
MenuStyle:="Circle"
IsStyle:=True
}
Return
M_Toggle:
If(IsOn) ;关闭
{
Menu,Tray,Rename,%M_Close%,%M_Open%
Menu,Tray,Icon,%A_Desktop%/Icon图集/MidMenu_Close.ico
IsOn:=False
}Else{ ;开启
Menu,Tray,Rename,%M_Open%,%M_Close%
Menu,Tray,Icon,%A_Desktop%/Icon图集/MidMenu_Open.ico
IsOn:=True
}
Return
M_MenuExit:
ExitApp
;=========================Function
CheckMenu() ;通过检测是否将ToolTip激活,以检查是否选择了菜单项
{
Loop,% AllCharMenu.Count() ;请确保菜单项与Label数量一致
{
If (WinActive(AllCharMenu[A_Index]) OR WinActive(SimpleCharMenu[A_Index]))
Return [True,A_Index] ;返回线性数组,下标一为选择了菜单项,下标二为选择菜单项的下标
Else
Continue ;检测下一条
}
}
CleanMenu() ;清除菜单
{
Loop, % SimpleCharMenu.Count()
{
If (SimpleCharMenu[A_Index]="")
Continue
ToolTip,,,,A_Index
Sleep,20 ;效果优化
}
}
OptimizeShowEffect() ;优化显示菜单效果
{
R:=Radius
MouseGetPos,PosX,PosY ;获取坐标,用于计算Tip的坐标
If (MenuStyle="Circle") ;以下为不同的菜单形式
{
PosX-=18,PosY-=10 ;稍微调整坐标,让光标置于中心
}
Loop,% SimpleCharMenu.Count()
{
If GetKeyState("RButton")
Return
If (SimpleCharMenu[A_Index]="")
Continue
If % (AllCharMenu.Count()<=MaxItems) ;总数未超过设定数
{
Radians:=(PI/180)*Round(360/AllCharMenu.Count()) ;不变
}Else ;第二圈
{
If (A_Index>MaxItems) ;第二圈时更改半径及等分点
{
R:=2*Radius ;在此调节第二圈的半径
Radians:=(PI/180)*Round(360/(2*MaxItems))
}
Else ;第一圈时
Radians:=(PI/180)*Round(360/MaxItems)
}
If (MenuStyle="Circle") ;以下为不同的菜单形式
{
X:=PosX+R*Cos(Radians*A_Index)
Y:=PosY+R*Sin(Radians*A_Index)
}Else If (MenuStyle="Rect")
{
If (A_Index>1)
PosY+=22
X:=PosX
Y:=PosY
}
If (TipStyle="SingleChar")
{
If % (A_Index=SimpleCharMenu.Count())
{
Sleep,30
PopUpStr(SimpleCharMenu[SimpleCharMenu.Count()],PosX,PosY,SimpleCharMenu.Count())
}
Else
PopUpStr(SimpleCharMenu[A_Index],X,Y,A_Index,15) ;在此调节弹出的速度
}Else
{
If % (A_Index=AllCharMenu.Count())
PopUpStr(AllCharMenu[AllCharMenu.Count()],PosX,PosY,AllCharMenu.Count())
Else
PopUpStr(AllCharMenu[A_Index],X,Y,A_Index)
}
}
}
OptimizeCloseEffect(SelectIndex) ;优化选取效果
{
Loop, % AllCharMenu.Count()
{
If % A_Index=SelectIndex ;跳过选中的菜单项
Continue
ToolTip,,,,A_Index
Sleep,20
}
Sleep,% 25*AllCharMenu.Count() ;保留所选取的菜单项到最后
ToolTip,,,,%SelectIndex%
}
PopUpStr(String,PosX,PosY,Weight,Speed:=0) ;建议保存此函数,在许多脚本中可使用
{
ClipLenth:=2
Loop,% StrLen(StrReplace(String, A_Space, "")) ;去除空格并获取长度
{
SplitStr:=SubStr(String,1,ClipLenth)
ToolTip,%SplitStr%,%PosX%,%PosY%,%Weight%
ClipLenth+=1
Sleep,8
}
Sleep,%Speed%
Return
}
;==============================CustomizeLabel
1:
Run,G:\笔记
Return
2:
Run,http://react.xhup.club/search
Return
3:
Run,https://wyagd001.github.io/zh-cn/docs/AutoHotkey.htm
Return
4:
Run,https://www.bilibili.com/
Return
5:
Run,https://www.aconvert.com/cn/icon/jpg-to-ico/
Return
6:
Run,https://fanyi.youdao.com/
Return
7:
Run,D:\小说
Return
8:
Return
9:
Run,https://developer.mozilla.org/zh-CN/
Return
10:
Return
11:
Return
12:
Run,https://www.huya.com/786724
Return
13:
Return
14:
Return
15:
Run,https://www.runoob.com/manual/jdk11api/index.html
Return
16:
Run,https://docs.unity.cn/cn/2020.3/Manual/UnityManual.html
Return
17:
Run,https://www.runoob.com/
Return
18:
Run,%A_Startup%
Return
19:
Run,%A_Desktop%\备忘录.txt
Return
版本二
;拥有更多菜单了!
#SingleInstance, Force
CoordMode,ToolTip,Screen
CoordMode,Mouse,Screen
;此脚本菜单样式为圆形
Global Delay:=20 ;在此定义按住中键唤起菜单所需的时间
Global BaseRadius:=60 ;在此定义父级圆形菜单的半径(子菜单需要在方法调用中修改)
Global MaxItems:=6 ;在此定义第一圈的最大项数,第二圈(最多)则为2*MaxItems项,总项数不超过19。关系为:(0-7]:[3-6],(0-9]:3,(9-13]:4,(13-16]:5,(16-19]:6
Global PI:=3.1415926
Global PosX,PosY ;记录菜单弹出的坐标,使子菜单与父菜单在同一位置激活
;========更换托盘图标相关,分激活与停用两种状态,双击托盘图标以停用脚本
Global IsOn:=True
Global IsStyle:=True
Global M_Open:="Open"
Global M_Close:="Close"
;=============基础菜单
;!同样,键只能使用数字!
;帮助:多字可使用`n换行,此时将半径适当调大可更好选中。父级建议都是四字,子级随意。
Global BaseMenu:={ 1:"API`n手册"
,2:"工具`n网站"
,3:"视频`n网站"
,4:"本地`n文件"
,5:"杂项`n网站"
,6:"占位`n占位"
,7:"学习`n网站"}
;=============对应的展开菜单
Global ExMenu_1:={ 1:"JAVA`n文档"
,2:"UNIT`n手册"
,3:"MDN`n文档"
,4:"W3C`n文档"
,5:"AHK`n文档"
,6:"占位"
,7:"占位"}
Global ExMenu_2:={ 1:"正则`n在线"
,2:"图标`n转换"
,3:"字符`n百科"
,4:"Json`n在线"
,5:"颜色`n进制"
,6:"正则`n菜鸟"
,7:"小鹤`n查形"
,8:"有道`n翻译"}
Global ExMenu_3:={ 1:"BILI`nBILI"
,2:"虎牙`n直播"
,3:"斗鱼`n直播"
,4:"占位"
,5:"占位"
,6:"占位"
,7:"占位"}
Global ExMenu_4:={ 1:"笔记`nFD"
,2:"小说`n合集"
,3:"自启`nFD"
,4:"文档`nFD"
,5:"CHM`nFD"
,6:"PDF`nFD"
,7:"备忘`n文件"
,8:"AHK`n合集"}
Global ExMenu_5:={ 1:"QQ`n邮箱"
,2:"学校`n官网"
,3:"占位"
,4:"占位"
,5:"占位"
,6:"占位"
,7:"占位"}
Global ExMenu_7:={ 1:"MDN`n网站"
,2:"菜鸟`n网站"
,3:"占位"
,4:"占位"
,5:"占位"
,6:"占位"
,7:"占位"}
; ,8:"占位"
; ,9:"占位"
; ,10:"占位"
; ,11:"占位"
; ,12:"占位"
; ,13:"占位"
; ,14:"占位"
; ,15:"占位"
; ,16:"占位"
; ,17:"占位"
; ,18:"占位"
; ,19:"占位"}
Menu,Tray,Icon,%A_Desktop%/Icon图集/MidMenu_Open.ico ;自备两个ICO
Menu,Tray,NoStandard ;去除默认菜单项
Menu,Tray,Add,%M_Close%,M_Toggle ;增加切换选项
Menu,Tray,Default,%M_Close% ;将切换选项设为默认
Menu,Tray,Click,2 ;设置双击响应
Menu,Tray,Add,&Exit,M_MenuExit ;添加退出按钮
#If IsOn
~MButton::
HowLong:=0 ;初始化
Loop{
HowLong++ ;计时
Sleep,10
If !(GetKeyState("MButton"))
Return
;ToolTip,% HowLong ;显示进度,可删除
} Until HowLong>=Delay ;达到预定时间
Send,{MButton} ;在浏览器中可去除中键的副作用
MouseGetPos,PosX,PosY ;获取坐标,用于计算Tip的坐标
PosX-=18,PosY-=20 ;稍微调整坐标,让光标置于中心,可能需要自己调整为合适位置
PopUpMenu(BaseMenu,BaseRadius) ;显示基础菜单
HotKey,~LButton,MenuClick ;设置单击热键,当单击时进行判断(这是一种方式,另一种见下方)
HotKey,~LButton,On ;启用热键
Return
#If
;使用标签的方式处理父级菜单
MenuClick:
HotKey,~LButton,Off ;单击后关闭热键
Sleep,100 ;等待ToolTip被激活
Result:=CheckMenu(BaseMenu) ;接受返回的数组
If Result[1]!=1 ;此次单击是否将菜单项激活
{
CleanMenu(BaseMenu) ;未激活则清除菜单并退出
Return
}
OptimizeCloseEffect(Result[2],BaseMenu) ;优化菜单关闭效果
; Gosub, % "B_"Result[2] ;追求效率将此条放在上条上面
; 讨论在此使用标签还是函数
; SelectedSubMenu:="ExMenu_" . Result[2]
; ExtendsMenu(SelectedSubMenu) ;真正传入的是字符串
Switch Result[2] ;在此增加子菜单的入口
{
Case 1:ExtendsMenu(ExMenu_1,1,60) ;在此可定义每个子菜单的半径
Case 2:ExtendsMenu(ExMenu_2,2,60) ;注意正确填写对应的参数
Case 3:ExtendsMenu(ExMenu_3,3,60)
Case 4:ExtendsMenu(ExMenu_4,4,60)
Case 5:ExtendsMenu(ExMenu_5,5,60)
Case 7:ExtendsMenu(ExMenu_7,7,60)
}
Return
;弹出菜单
PopUpMenu(Arrays,Radius:=60){
R:=Radius
Loop,% Arrays.Count()
{
If (Arrays[A_Index]="")
Continue
If % (Arrays.Count()<=MaxItems) ;总数未超过设定数
{
Radians:=(PI/180)*Round(360/(Arrays.Count()-1))
}Else ;第二圈
{
If (A_Index>MaxItems) ;第二圈时更改半径及等分点
{
R:=2*Radius ;在此调节第二圈的半径
Radians:=(PI/180)*Round(360/(2*MaxItems))
}
Else ;第一圈时
Radians:=(PI/180)*Round(360/MaxItems)
}
X:=PosX+R*Cos(Radians*A_Index)
Y:=PosY+R*Sin(Radians*A_Index)
If % (A_Index=Arrays.Count())
{
Sleep,30
PopUpStr(Arrays[Arrays.Count()],PosX,PosY,Arrays.Count())
}
Else
PopUpStr(Arrays[A_Index],X,Y,A_Index,15) ;在此调节弹出的速度
}
}
;优化选取效果
OptimizeCloseEffect(SelectIndex,Arrays,SleepTime:=0)
{
Loop, % Arrays.Count()
{
If % A_Index=SelectIndex ;跳过选中的菜单项
Continue
ToolTip,,,,A_Index
Sleep,% SleepTime
}
Sleep,% 25*SleepTime ;保留所选取的菜单项到最后
ToolTip,,,,%SelectIndex%
}
;弹出字符串,可调节速度
;参数:一、传入字符串,二三、弹出坐标,四、ToolTip的权重(1-20),五、两个ToolTip间隔的速度
PopUpStr(String,PosX,PosY,Weight,Speed:=0)
{
ClipLenth:=2
Loop,% StrLen(StrReplace(String, A_Space, "")) ;去除空格并获取长度
{
SplitStr:=SubStr(String,1,ClipLenth)
ToolTip,%SplitStr%,%PosX%,%PosY%,%Weight%
ClipLenth+=1
; Sleep,5 ;在此调节弹出速度
}
Sleep,%Speed%
Return
}
;通过检测是否将ToolTip激活,以检查是否选择了菜单项
CheckMenu(Arrays)
{
Loop,% Arrays.Count() ;请确保菜单项与Label数量一致
{
If (WinActive(Arrays[A_Index]))
Return [True,A_Index] ;返回线性数组,下标一为选择了菜单项,下标二为选择菜单项的下标
Else
Continue ;检测下一条
}
}
;清除菜单
CleanMenu(Arrays)
{
Loop, % Arrays.Count()
{
If (Arrays[A_Index]="")
Continue
ToolTip,,,,A_Index
Sleep,20 ;效果优化
}
}
;使用函数的方式处理子菜单的弹出与选中
;参数一传入子菜单名,二为子菜单在父级中的序号,三为子菜单的半径
ExtendsMenu(Arrays,Which,R)
{
If (Arrays.Count()=0)
Return
PopUpMenu(Arrays,R)
Sleep,200
KeyWait, LButton,D ;另一中检测选中的方式,不使用热键,而是中止程序等待左键键击
Result:=CheckMenu(Arrays)
If Result[1]!=1 ;此次单击是否将菜单项激活
{
CleanMenu(Arrays) ;未激活则清除菜单并退出
Return
}
OptimizeCloseEffect(Result[2],Arrays,20)
; msgbox,% "Ex_"Which "_"Result[2] ;使用时改为GoSub,并自行添加相应标签,命名
GoSub,% "Ex_"Which "_"Result[2]
Return
}
M_Toggle:
If(IsOn) ;关闭
{
Menu,Tray,Rename,%M_Close%,%M_Open%
Menu,Tray,Icon,%A_Desktop%/Icon图集/MidMenu_Close.ico
IsOn:=False
}Else{ ;开启
Menu,Tray,Rename,%M_Open%,%M_Close%
Menu,Tray,Icon,%A_Desktop%/Icon图集/MidMenu_Open.ico
IsOn:=True
}
Return
M_MenuExit:
ExitApp
;=====================api手册
Ex_1_1:
Run,https://www.runoob.com/manual/jdk11api/index.html
Return
Ex_1_2:
Run,https://docs.unity.cn/cn/2020.3/Manual/UnityManual.html
Return
Ex_1_3:
Run,https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element
Return
Ex_1_4:
Run,https://www.w3school.com.cn/tags/index.asp
Return
Ex_1_5:
Run,https://wyagd001.github.io/zh-cn/docs/
Return
;====================工具网站
Ex_2_1:
Run,https://regexr-cn.com/
Return
Ex_2_2:
Run,https://www.aconvert.com/cn/icon/jpg-to-ico/
Return
Ex_2_3:
Run,https://unicode-table.com/cn/
Return
Ex_2_4:
Return
Ex_2_5:
Run,https://c.runoob.com/front-end/55/
Return
Ex_2_6:
Run,https://c.runoob.com/front-end/854/
Return
Ex_2_7:
Run,http://react.xhup.club/search
Return
Ex_2_8:
Run,https://fanyi.youdao.com/
Return
;====================视频网站
Ex_3_1:
Run,https://www.bilibili.com/
Return
Ex_3_2:
Run,https://www.huya.com/786724
Return
Ex_3_3:
Run,https://www.douyu.com/
Return
;====================本地文件
Ex_4_1:
Run,G:\笔记
Return
Ex_4_2:
Run,D:\小说
Return
Ex_4_3:
Run,%A_Startup%
Return
Ex_4_4:
Run,%A_MyDocuments%
Return
Ex_4_5:
Run,G:\CHM
Return
Ex_4_6:
Run,G:\PDF
Return
Ex_4_7:
Run,%A_Desktop%\备忘录.txt
Return
Ex_4_8:
Run,G:\AHK\AHK脚本合集
Return
;====================杂项网站
Ex_5_1:
Run,https://mail.qq.com/cgi-bin/frame_html?sid=dsj_Hrro6ARguRh7&r=97a7af1edd5c52db38404bf341e107a9&lang=zh
Return
Ex_5_2:
Run,http://www.gnust.edu.cn/
Return
;====================学习网站
Ex_7_1:
Run,https://developer.mozilla.org/zh-CN/docs/Web
Return
Ex_7_2:
Run,https://www.runoob.com/
Return
原理
我太懒,嫌麻烦,就不讲代码了,只说些思路,共两步:
1、根据三角函数确定tooltip生成的坐标,可看下面的示例脚本。
#SingleInstance, Force
CoordMode,ToolTip,Screen
String:=["a","b","c","d","e","f","g","h","i","j","k","l"]
PI:=3.1415926
Radius:=100
Radians:=(PI/180)*Round(360/String.Length())
~LButton::
MouseGetPos,PosX,PosY
Loop,% String.Length()
{
Y:=PosY+Radius*Cos(radians*A_Index)
X:=PosX+Radius*Sin(radians*A_Index)
PopUpStr(String[A_Index],X,Y,A_Index)
}
Return
PopUpStr(String,PosX,PosY,Weight)
{
ClipLenth:=2
Loop,% StrLen(StrReplace(String, A_Space, "")) ;去除空格并获取长度
{
SplitStr:=SubStr(String,1,ClipLenth)
ToolTip,%SplitStr%,%PosX%,%PosY%,%Weight%
ClipLenth+=1
Sleep,10
}
Sleep,150
Return
}
2、tooltip属于ahk的gui部分,如果没有给tooltip显式使用v选项赋值,则其name默认为tooltip的内容(所以菜单项不能重名)。那么便可使用if winactive判断鼠标点击时具体激活哪个tooltip,再执行对应操作即可。
其他
- 版本一还定义了另一种菜单样式--矩形:
实际可能需要调整每个tooltip的y坐标差值。 - 因为我使用的是100%缩放,所以tooltip很小,不占空间。缩放125%可能显的过大了,所以不一定适合所有人……
- 如果直接copy是一定会报找不到icon的错误的,需要注释掉menu命令中关于tray的,或在对应路径上准备两个icon。
- 另外,脚本还有许多细节我没有说,具体的自己尝试吧~