如何利用Python和win32编程避免重复性体力劳动(二)——菜单操作:GetMenu,GetSubMenu,GetMenuItemID以及wParam的HIWORD&LOWORD
Part 2:菜单操作
有了句柄,我们就可以操作FaceGen了!嗯,要先打开文件,File→Open,然后再File→Save Image(很悲剧,Save Image没有快捷键,所以不得不进行菜单操作)。现在我们有了FindWindow和FindWindowEx,要怎么操作菜单呢?
哦,抱歉,靠他俩还做不到。
窗口的菜单就像窗口的标题栏一样,是窗口自身的一部分,不是其他窗体控件,也就没有办法用FindWindow和FindWindowEx返回句柄。所以要对菜单进行操作的话,我们需要新的函数,也就是GetMenu,GetSubMenu和GetMenuItemID,它们也都属于win32gui模块。怎么用呢,结合下图来说:
- GetMenu(hwnd)
- 描述:获取窗口的菜单句柄。
- 参数:
- hwnd:整型,需要获取菜单的窗口的句柄。
- 说明:获取的是插图中黄色的部分。
- GetSubMenu(hMenu, nPos)
- 描述:获取菜单的下拉菜单或者子菜单。
- 参数:
- hMenu:整型,菜单的句柄,从GetMenu获得。
- nPos:整型,下拉菜单或子菜单的的索引,从0算起。
- 说明:这个可以获取插图中蓝色的部分z;如描述所述,这个不仅可以获取本例中的下拉菜单,还可以获取子菜单。
- GetMenuItemID(hMenu, nPos)
- 描述:获取菜单中特定项目的标识符。
- 参数:
- hMenu:整型,包含所需菜单项的菜单句柄,从GetSubMenu获得。
- nPos:整型,菜单项的索引,从0算起。
- 说明:这个获取的就是红色区域中的项目啦,注意,分隔符是被编入索引的,所以Open的索引是2而非1,而Exit的索引是9而非6。
找到这个菜单项的标识符有什么用呢?找到以后我们就可以告诉应用程序:我们要执行这个菜单项的命令!怎么告诉呢?这就要涉及消息机制了,我们要给应用程序发个消息,让它执行所需菜单项的命令。假设之前获取的Open的标识符是open_ID,那么只需要这样:
- win32gui.PostMessage(self.Mhandle, win32con.WM_COMMAND, open_ID, 0)
就会有一个打开文件的对话框出现啦。
解释一下:
- PostMessage(hWnd, Msg, wParam, lParam)
- 描述:在消息队列中加入为指定的窗体加入一条消息,并马上返回,不等待线程对消息的处理。
- 参数:
- hWnd:整型,接收消息的窗体句柄
- Msg:整型,要发送的消息,这些消息都是windows预先定义好的,可以参见系统定义消息(System-Defined Messages))
- wParam:整型,消息的wParam参数
- lParam:整型,消息的lParam参数
- 说明:简单说,就是给指定程序发一个消息,这些消息都用整型编好号,作为windows的常量可以查询的。在这里,我们用的就是win32con这个库里定义的WM_COMMAND这个消息,具体的wParam和lParam是根据消息的不同而不同的。具体请根据MSDN查阅。
关于wParam的low word和high word:
查阅MSDN的消息时,会发现有的wParam定义了low word和high word,这是什么呢?wParam的定义是32位整型,high word就是他的31至16位,low word是它的15至0位,如图。当参数超过两个,wParam和lParam不够用时,可以将wParam就给拆成两个int16来使用。这种时候在python里记得用把HIWORD的常数向左移16位,再加LOWORD,即wParam = HIWORD<<16+LOWORD。
更新一下我们定义的类,把菜单添加进去:
- class FaceGenWindow(object):
- def __init__(self, fgFilePath=None):
- self.Mhandle = win32gui.FindWindow("FaceGenMainWinClass", None)
- self.menu = win32gui.GetMenu(self.Mhandle)
- self.menu = win32gui.GetSubMenu(self.menu, 0)
- print "FaceGen initialization compeleted"
- # 然后定义一个菜单操作的方法:
- def menu_command(self, command):
- """
- 菜单操作
- 返回弹出的打开或保存的对话框的句柄 dig_handle
- 返回确定按钮的句柄 confBTN_handle
- """
- command_dict = { # [目录的编号, 打开的窗口名]
- "open": [2, u"打开"],
- "save_to_image": [5, u"另存为"],
- }
- cmd_ID = win32gui.GetMenuItemID(self.menu, command_dict[command][0])
- win32gui.PostMessage(self.Mhandle, win32con.WM_COMMAND, cmd_ID, 0)
- for i in range(10):
- if win32gui.FindWindow(None, command_dict[command][1]):
- break # 如果找到了打开或者另存为的对话框,就跳出循环
- else:
- win32api.Sleep(200) # 利用这个函数等待200ms,就不需要再额外导入time模块了
- dig_handle = win32gui.FindWindow(None, command_dict[command][1])
- confBTN_handle = win32gui.FindWindowEx(dig_handle, 0, "Button", None)
- return dig_handle, confBTN_handle
在这里顺便返回了弹出来的对话框的句柄和确定按钮的句柄,后面操作会用到。