桌面 UI 的自动化现在已经变成的冷门技术,相比于移动与 Web 自动化来说,桌面应用的控件定位的有其历史复杂性,又有很多古老非标准控制或自定义控件,这些都为桌面程序的自动化增加了难度。
桌面的开源自动化测试框架一直不多,以前比较流行的 White Framework 在近几年也几乎停止了更新。
最近由于业务需要重拾了 White 框架,发现各种难用,定位与调试难度都比较大,有些控件在 White 中死活识别不能,或出现一些 BUG 导致识别失败,只好通过 UIAutomation 重写,非常麻烦。
一个偶然的机会,在 GIt 上看见了这个项目,Python-UIAutomation-for-Windows,基于 Python 封装的 UIAutomation 实现,试用了一下感觉还是不错的,作者更新也非常及时,这里推荐给大家。作者是个中国人哦。
它的核心库就是 automation.py,里面封装了基于 UIAutomation 的实现,理论上可以支持所有.Net 框架所编写的程序,作者声称可以支持 MFC, Windows Form, WPF, Modern UI(Metro UI), Qt and Firefox。
语法结构非常简单,这里贴一段示例吧,操作 QQ 登录界面的一段代码:
# -*- coding:utf-8 -*-from automation import *
time.sleep(2)
#查找qq窗口,searchDepth = 1,设置查找深度为1,查找Desktop的第一层子窗口就能很快找到QQqqWindow = WindowControl(searchDepth = 1, ClassName = 'TXGuiFoundation', Name = 'QQ')
if not qqWindow.Exists():
shellTray = Control(searchDepth = 1, ClassName = 'Shell_TrayWnd')
qqTray = ButtonControl(searchFromControl = shellTray, Name = 'QQ')
if qqTray.Exists():
qqTray.Click()
time.sleep(1)
if not qqWindow.Exists():
Logger.WriteLine(u'Can not find QQ window, please put it in front first!' % edit.CurrentValue(), ConsoleColor.Red)
#查找QQ帐号Edit,设置searchFromControl = qqWindow,从qqWindow开始查找子控件
#foundIndex = 1,表示查找第一个符合要求的控件,子窗口中的第一个Editedit = EditControl(searchFromControl = qqWindow, foundIndex = 1)
edit.Click()
Win32API.SendKeys('{Ctrl}A')
Logger.Write('Current QQ is ')
#获取edit内容Logger.WriteLine(u'%s' % edit.CurrentValue(), ConsoleColor.DarkGreen)
time.sleep(1)
#查找第二个Edit,即密码Editedit = EditControl(searchFromControl = qqWindow, foundIndex = 2)
edit.Click()
Logger.Write('Current QQ password is ')
#获取password内容Logger.WriteLine('%s' % edit.CurrentValue(), ConsoleColor.DarkGreen)
Logger.WriteLine('Only get stars. password can not be got', ConsoleColor.DarkGreen)
time.sleep(1)
Logger.WriteLine('Now let\'s show the buttons of QQ')
time.sleep(2)
#遍历QQ窗口内的所有ButtonbuttonIndex = 1
button = ButtonControl(searchFromControl = qqWindow, foundIndex = buttonIndex)
while button.Exists():
l, t, r, b = button.BoundingRectangle
Logger.WriteLine('button %d, position (%d,%d,%d,%d)' % (buttonIndex, l, t, r, b))
button.MoveCursorToMyCenter()
time.sleep(1)
buttonIndex += 1
button = ButtonControl(searchFromControl = qqWindow, foundIndex = buttonIndex)
Logger.WriteLine('\r\nLook, the last button\'s position are all 0, it may be invisible.', ConsoleColor.Yellow)
button = ButtonControl(searchFromControl = qqWindow, foundIndex = 4)
button.Click()
menu = Control(searchDepth = 1, ControlType = ControlType.MenuControl, Name = u'TXMenuWindow')
if (menu.Exists()):
menuItem = MenuItemControl(searchFromControl = menu, Name = u'隐身')
menuItem.Click()
time.sleep(1)
button = ButtonControl(searchFromControl = qqWindow, foundIndex = 8)
button.Click()
time.sleep(1)
button = ButtonControl(searchFromControl = qqWindow, Name = u'取消')
button.Click()
又比如自动化完成批量重命名的工作:
有兴趣的可以去试一下。