如何使用pywinauto

原文地址:https://github.com/pywinauto/pywinauto/blob/master/docs/HowTo.txt

pywinauto是个非常不错的windows自动化程序,也就是完成类似案件精灵功能

下面我就翻译一下怎么用吧

========

如何使用

========


如何指定一个应用程序实例

---------------------------------------------

Application()实例是你想要自动执行程序的重点,所以 Application需要连接到一个进程上。

有两种方法来生成 Application:


  ::

    start(self, cmd_line, timeout=app_start_timeout)  # instance method:

或者

  ::

    connect(self, **kwargs)  # instance method:

 ``start()`` 主要用在程序还未启动,用程序自己打开,下面是实例:

  ::


   app = Application().start(r"c:\path\to\your\application -a -n -y --arguments")

 timeout 参数是可选的,如果程序程序启动需要很长时间,那么有可能会用到它。


``connect()`` 用在程序已经启动的情况。连接接到已经启动的程序,下面是实例:


:process:程序的id(此处应该是指PID),比如

  ::

              app = Application().connect(process=2341)


:handle:程序的窗口句柄,比如

  ::

              app = Application().connect(handle=0x010f0c)


:path:可执行程序的路径(`GetModuleFileNameEx``常常用来寻找进程的路径和比对传入的参数),比如


  ::

          app = Application().connect(path=r"c:\windows\system32\notepad.exe")


使用组合参数指定一个窗口,这些参数都会直接传递到pywinauto.findwindows.find_elements()程序,比如

  ::


          app = Application().connect(title_re=".*Notepad", class_name="Notepad")



提示:即将要启动的程序,可以使用connect*(),但不会提示超时和重连。

所以不是使用pywinauto模块启动的程序,通过sleep或一个循环等待来保证程序已经彻底启动是非常有必要。 





如何指定程序的对话框

------------------------------------------

如果连接的应用实例是一个对话框,需要被定义才能使用。



指定对话框有很多方法可以实现。最常见的方法是把对话框的标题作为组件(item)或属性(attribute)来使用,比如:
  ::


   dlg = app.Notepad


或等价于

  ::


   dlg = app['Notepad']

最简单的方法是调用top_window()`函数,比如

  ::


    dlg = app.top_window()

这个方法可以返回程序Z顺序(Z-order)中最高的最上层的窗口。


注意:目前我还不能够确认一定能够返回你需要的窗口。返回一定会程序最上层的窗口,但也许不一定是Z-order里最高的。


如果上述方法部分满足你的需求,那么可以使用findwindows.find_windows()函数,参数传递与上面相同。

  ::


    dlg = app.window(title_re="Page Setup", class_name="#32770")


最后介绍最灵活的控制方法

  ::


    dialogs = app.windows()


这种方法将以列表(list)的形式返回程序中所有可见(visible)、允许(enabled)、上层(top level )窗口。

  ::


    app.window(handle=win)


注意:如果对话框的标题(title )非常长,通过属性(attribute )来连接输入很麻烦,可以使用以下方法来使用更简单

  ::


    app.window(title_re=".*Part of Title.*")




如何指定对话框中的控件

------------------------------------


有多种方法,最简单的是

  ::


   app.dlg.control
   app['dlg']['control']



上面的的第二种方法更适合在非英语的操作系统,你可以使用unicode字符,比如app[u'your dlg title'][u'your ctrl title']

每个控件可以使用以下方面来建立标识符:

 - title标题

 - friendly class友好类名
 - title + friendly class标题+友好类名


如果控件的text是空的(不包括非显示字符)或没有使用text。

替代方案是我们可以找控件离该控件最右上的控件,并且加上友好类名,列表可以表示为:


 - friendly class
 - closest text + friendly class



对话框中所有控件一旦被创建,可以用一个集合(set)来表示,我可以使用它来消除歧义。 

使用`WindowSpecification.print_control_identifiers()`方法来实现,比如

  ::


   app.YourDialog.print_control_identifiers()


实例输出的结果:

  ::


    Button - Paper   (L1075, T394, R1411, B485)
            'PaperGroupBox' 'Paper' 'GroupBox'
    Static - Si&ze:   (L1087, T420, R1141, B433)
            'SizeStatic' 'Static' 'Size'
    ComboBox -    (L1159, T418, R1399, B439)
            'ComboBox' 'SizeComboBox'
    Static - &Source:   (L1087, T454, R1141, B467)
            'Source' 'Static' 'SourceStatic'
    ComboBox -    (L1159, T449, R1399, B470)
            'ComboBox' 'SourceComboBox'
    Button - Orientation   (L1075, T493, R1171, B584)
            'GroupBox' 'Orientation' 'OrientationGroupBox'
    Button - P&ortrait   (L1087, T514, R1165, B534)
            'Portrait' 'RadioButton' 'PortraitRadioButton'
    Button - L&andscape   (L1087, T548, R1165, B568)
            'RadioButton' 'LandscapeRadioButton' 'Landscape'
    Button - Margins (inches)   (L1183, T493, R1411, B584)
            'Marginsinches' 'MarginsinchesGroupBox' 'GroupBox'
    Static - &Left:   (L1195, T519, R1243, B532)
            'LeftStatic' 'Static' 'Left'
    Edit -    (L1243, T514, R1285, B534)
            'Edit' 'LeftEdit'
    Static - &Right:   (L1309, T519, R1357, B532)
            'Right' 'Static' 'RightStatic'
    Edit -    (L1357, T514, R1399, B534)
            'Edit' 'RightEdit'
    Static - &Top:   (L1195, T550, R1243, B563)
            'Top' 'Static' 'TopStatic'
    Edit -    (L1243, T548, R1285, B568)
            'Edit' 'TopEdit'
    Static - &Bottom:   (L1309, T550, R1357, B563)
            'BottomStatic' 'Static' 'Bottom'
    Edit -    (L1357, T548, R1399, B568)
            'Edit' 'BottomEdit'
    Static - &Header:   (L1075, T600, R1119, B613)
            'Header' 'Static' 'HeaderStatic'
    Edit -    (L1147, T599, R1408, B619)
            'Edit' 'TopEdit'
    Static - &Footer:   (L1075, T631, R1119, B644)
            'FooterStatic' 'Static' 'Footer'
    Edit -    (L1147, T630, R1408, B650)
            'Edit' 'FooterEdit'
    Button - OK   (L1348, T664, R1423, B687)
            'Button' 'OK' 'OKButton'
    Button - Cancel   (L1429, T664, R1504, B687)
            'Cancel' 'Button' 'CancelButton'
    Button - &Printer...   (L1510, T664, R1585, B687)
            'Button' 'Printer' 'PrinterButton'
    Button - Preview   (L1423, T394, R1585, B651)
            'Preview' 'GroupBox' 'PreviewGroupBox'
    Static -    (L1458, T456, R1549, B586)
            'PreviewStatic' 'Static'
    Static -    (L1549, T464, R1557, B594)
            'PreviewStatic' 'Static'
    Static -    (L1466, T586, R1557, B594)
            'Static' 'BottomStatic'


这个例子来自test_application.py文件

注意:使用该方法打印出的标识在整个进程中能够保证独一无二。

当有两个输入框(edit boxes),他们标识列表中都有"Edit",第一个可以使用"Edit", "Edit0"来指定,

但"Edit1"指的是第二个



注意:不一定非得精确指定!比如下面的一个实例

  ::


    Button - Margins (inches)   (L1183, T493, R1411, B584)
            'Marginsinches' 'MarginsinchesGroupBox' 'GroupBox'


你一定不喜欢以下指定的方法

  - ``GroupBox``-名字太普通,能代表任何group box

 - ``Marginsinches`` and ``MarginsinchesGroupBox`` -这两个看上去不错,

如果把'inches' 省略就更好了。



这样!代码最好能够通过使用正确的表达式来匹配到对话框中所需控件。


举个例子,如果你通过调试器下断点就能看出用不同标识的区别了。



  ::


    (Pdb) print app.PageSetup.Margins.window_text()
    Margins (inches)
    (Pdb) print app.PageSetup.MarginsGroupBox.window_text()
    Margins (inches)



这样将输出错误信息。如果你想操作的对话框中一个控件和另一个很相似,比如有两个标识很相似,不小心的错误编码就会出错。




如何在非英语程序中使用pywinauto

------------------------------------------------------------------

你无法使用属性(attribute )去连接一个控件,因为python代码不支持unicode标识符,

所以要么使用组件(item ),要么要么显示调用window()


所以下面的情况
  ::


   app.dialog_ident.control_ident.click()


你可以这样写

  ::


    app['dialog_ident']['control_ident'].click()


或显示调用window()函数

  ::


    app.window(title_re="NonAsciiCharacters").window(title="MoreNonAsciiCharacters").click()


实例可以查看``examples\misc_examples.py get_info()``





如何处理与不按常理响应的控件(比如OwnerDraw控件)

------------------------------------------------------------------------------------

有些控件(特别是指OwnerDraw控件)不按常理响应事件。

比如你打开HLP(help)文件,在Index Tab上(单击搜索按钮)将会显示一个listbox(列表)。

使用Spy或Winspector工具搜索这个,它确实是一个list box,但它是自绘(ownerdrawn)的。

这意味着软件的开发者已经告诉windows那些控件的显示将被重载,行为自定义。

这种情况下,就会造成那些字符串无法被返回。:-(.



那么这种情况会出现什么问题呢?

  ::


   app.HelpTopics.ListBox.texts()                # 1
   app.HelpTopics.ListBox.select("ItemInList")   # 2




1、会返回一个空字符串,这意味着pywinauto 将不能获取listbox中的字符串。


2、会返回IndexError错误,因为ListBox控件的select(string) 方法要获取项目(item )中的文字,组件的索引是能被选择的。



下面变通的方法可以用在这个控件

  ::


   app.HelpTopics.ListBox.select(1)


这样将选择listbox中第二个项目(item ),由于不使用字符串所以运行正常。



不幸的是即使这样能够运行。软件的开发者也可以让控件不响应标准的事件(events )比如Select。

这种情况下,你只能通过键盘模拟TypeKeys()来在listbox 选择项目(item )。


这样就可以可以给控件发送键盘按键。比如选第三个项目(item )

  ::


   app.Helptopics.ListBox1.type_keys("{HOME}{DOWN 2}{ENTER}")




- ``{HOME}`` will make sure that the first item is highlighted.

- ``{HOME}`` 保证第一个项目(item)

- ``{DOWN 2}`` will then move the highlight down two items

- ``{DOWN 2}`` 向下标记两个项目(item)

- ``{ENTER}`` will select the highlighted item

- ``{ENTER}`` 选择被标记的项目

如果你的程序使用大量相同种类的控件,从ListBox继承一个新类会事半功倍,这些你编写的特殊程序将涉及一些额外知识。
比如在WinHelp例子中,在list view中每个被标记项目(item)的文本都会显示在Edit control 上面,所以可以从这个项目获取文本
  ::

   # 打印list box中选择的项目

   # (只要你没有在Edit control输入内容!)

   print app.HelpTopics.Edit.texts()[1]




如何连接系统托盘(也被称作SysTray或通知区域)(译者注:win系统左下角的区域)

------------------------------------------------------------------------------------

在系统时钟区域有些图标是正在运行的程序。此区域通常被称为“系统托盘”。

事实上,这个区域有很多不同的windows控件。包含图标的控件一般是toolbar。

它是Pager 控件的子类,在含有TrayNotifyWnd类的窗口内部。

他们是位于其他窗口中,包含了Shell_TrayWnd的,这些窗口其实是Explorer 实例的一部分。

幸运的是,我们不需要明白也能操作。:-).


最重要的事情是寻找"Explorer.exe"程序中包含 "Shell_TrayWnd"类的窗口,这些窗口中Toolbar控件的标题(title)是"Notification Area".。


下面是一种操作操作方法。

  ::


   import pywinauto.application
   app = pywinauto.application.Application().connect(path="explorer")
   systray_icons = app.ShellTrayWnd.NotificationAreaToolbar


taskbar模块提供了连接到系统托盘的初步方法。

定义的变量如下:

:explorer_app:  定义了连接到explorer进程的.Application()对象。我们不一定要直接用它。


:TaskBar:  任务条(task bar)的句柄 (bar包含开始按钮、快捷启动图标、正在运行的任务等等)
           the QuickLaunch icons, running tasks, etc

:StartButton:  "开始点我" :-)我觉得你应该知道它!

:QuickLaunch: 包含快捷启动图标的Toolbar 。

:SystemTray: 包含时钟和系统托盘图标的窗口。

:Clock: 时钟

:SystemTrayIcons:  指向系统托盘图标的toolbar。

:RunningApplications:   指向正在运行的任务的toolbar。


这里提供两种点击系统托盘图标的方法:



:``ClickSystemTrayIcon(button)``: 可以使用此方法左键单击系统托盘内的可见图标。

我这里说可见图标是因为还有很多不可见图标是不能被点击的。Button参数可以是任意整数。

比如指定3那么就会找到第三个可见按钮并点击。

(错误的点击大部分情况不会提示,但这个方法在未来很可能会被转移或重命名)


:``RightClickSystemTrayIcon(button)``: 和`ClickSytemTrayIcon``相似但是是右键单击。



一般情况下,左/右键单击图标,将会显示一个弹出式菜单。需要注意到是这个弹出式菜单是被自动化运行程序的,

而不是explorer的一部分。


比如:

  ::


    # 连接到outlook
    outlook = Application.connect(path='outlook.exe')


    # 单击outlook图标
    taskbar.ClickSystemTrayIcon("Microsoft Outlook")


    # 选择弹出式菜单的一个项目
    outlook.PopupMenu.Menu().get_menu_path("Cancel Server Request")[0].click()
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值