【pywinauto】简单的UI自动化入门(基于Windows) 第二篇

这个教程现在来看还是给自己写的,防止以后阿尔兹海默症以后忘了,

所以会不断的引用官方文档和别人的博客,

其实主要还是我怎么写过文档没什么头绪和逻辑,只能根据从上往下的顺序讲了;

其实我在想我这样写文档我干嘛不把中文丢到git上,反正git上太监这么久了

 


之前写了实例化的窗口怎么连接,连接上以后就可以看看程序了,

首先可以把主要经常用到的窗口定个名字:

from pywinauto import Application

app = Application().start('TestWinForm.exe')
dlg_test = app[r'测试窗体']

这里的写法 dlg_test = app[r‘测试窗体’],看看官方文档:

Once the application instance knows what application it is connected to a dialog to work on needs to be specified.

There are many different ways of doing this. The most common will be using item or attribute access to select a dialog based on it’s title. e.g

一旦应用程序实例知道它连接到哪个应用程序,就可以开始指定要处理的dialog。

有很多方法可以,最常见的还是直接用名字访问:

dlg = app.Notepad

or equivalently  这里上下是等效的

dlg = app['Notepad']

The next easiest method is to ask for the top_window() e.g. 

另一个简单方法就是请求 top_window(),

Note: This is currently fairly untested so I am not sure it will return the correct window. It will definitely be a top level window of the application - it just might not be the one highest in the Z-Order.

作者这里表示这个没怎么测试过但是给你返回的肯定是z-order最高的

If this is not enough control then you can use the same parameters as can be passed to findwindows.find_windows() e.g.

如果都不行建议还是用 findwindows.find_windows()


top_window()能够返回常见的最顶部窗口,但是不是一定的,下面这篇关于z-order结构的介绍文章,写的特别好:

https://blog.csdn.net/baidu_34331290/article/details/88053650

原文地址


下面就是最常用的用法,声明了这个窗口

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

Finally to have the most control you can use

dialogs = app.windows()

这里看返回的结果

<class 'pywinauto.application.WindowSpecification'>
<pywinauto.application.WindowSpecification object at 0x0550C5F0>

 

this will return a list of all the visible, enabled, top level windows of the application. You can then use some of the methods in handleprops module select the dialog you want. Once you have the handle you need then use

这里会返回一系列的值,还有程序当前顶层的窗体,如果你想对里面的控件/结构做什么,就需要用到handleprops,

只要你有了句柄你就可以为所欲为

这里其实作者讲了pywinauto的核心理念,就是对传递的抓取,这其中的原理我还不是很清楚,以后慢慢讲

下面就是依据句柄对某样东西进行直接绑定<-绑定这个词真好用,以后都这么说了

app.window(handle=win)

Note: If the title of the dialog is very long - then attribute access might be very long to type, in those cases it is usually easier to use

下方是一个使用的方法,通过title绑定,后面加上_re指使用正则规则,如果物件的名称过长,作者推荐是使用正则而不是全贴上去

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

其实这里捆绑物件的方法非常多,我举几个例子:

login_dlg['Edit2'].set_text('admin')
login_dlg.Edit2.set_text('admin')
login_dlg.child_window(handle=0x00010DE0).set_text('admin')
login_dlg.child_window(auto_id='txtUserName', control_type="Edit").set_text('admin')

从准确和方便的角度来看最后一种是比较好的,但是如果是表格,多输入框这种情况,第二种又比较好。

这里看原文:

如何准确的去控制一个物件(Dialog)

How to specify a control on a dialog

There are a number of ways to specify a control, the simplest are

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

The 2nd is better for non English OS’s where you need to pass unicode strings e.g.

下面这种情况要特别注意,在非英语环境下注意unicode的问题

 app[u'your dlg title'][u'your ctrl title']

那么如何知道一个程序的具体结构呢?

网上一般推荐使用SPYXX,Inspect,UISpy这几样,

我觉得比较好用的是SPYXX

但是最好用的一个方法还是这个↓:(这里很重要)

app.YourDialog.print_control_identifiers()

def print_control_identifiers(self, depth=None, filename=None):

 

这个代码帮助你查询了窗体的结构,而且帮你给每个控件构建了多个标识符:

 

分别是:

title
friendly class
title + friendly class

如果控件的title文本为空(删除非char字符后),则不使用这个样式。 而是会寻找控件上方和右侧最接近的标题文本。 并附上class name。 所以样式变成了:

friendly class
closest text + friendly class

作者在这里说一旦创建了所有控件的标识符,那么久会消除所有不确定性(我没看懂什么意思)

这个方法完整引用为:WindowSpecification.print_control_identifiers() 

e.g.举例:

app.YourDialog.print_control_identifiers()

Sample output

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'

This example has been taken from test_application.py

Note The identifiers printed by this method have been run through the process that makes the identifier unique. So if you have two edit boxes, they will both have “Edit” listed in their identifiers. In reality though the first one can be refered to as “Edit”, “Edit0”, “Edit1” and the 2nd should be refered to as “Edit2”

上面是一个print_control_identifiers() 方法的举例,经过了唯一化(这个唯一化我也想要拥有),输出出来是这种效果,

实际上,如果您有两个编辑框,则它们的标识符中都会列出“Edit”。实际上,虽然第一个可以被称为“Edit”,“Edit0”,“Edit1”,

第二个应该被称为“Edit2”

事实确实是如此

实际上当时我自己摸索的时候情况是这样的↓

对我自己的测试窗口使用这个方法:

输出为:

WindowsForms10.Window.8.app.0.141b42a_r9_ad1 - '测试窗体'    (L33, T33, R843, B433)
['测试窗体', 'WindowsForms10.Window.8.app.0.141b42a_r9_ad1', '测试窗体WindowsForms10.Window.8.app.0.141b42a_r9_ad1']
child_window(title="测试窗体", auto_id="Form1", control_type="TestWinForm.Form1")
   | 
   | Edit - ''    (L383, T239, R478, B265)
   | ['中文按钮1Edit', 'Edit', 'Edit0', 'Edit1', '中文按钮1Edit0', '中文按钮1Edit1']
   | child_window(auto_id="textBox22", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L715, T209, R810, B235)
   | ['测试窗体Edit', 'Edit2', '测试窗体Edit0', '测试窗体Edit1']
   | child_window(auto_id="textBox21", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L606, T209, R701, B235)
   | ['中文按钮1Edit2', 'Edit3']
   | child_window(auto_id="textBox20", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L495, T209, R590, B235)
   | ['中文按钮1Edit3', 'Edit4']
   | child_window(auto_id="textBox19", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L383, T209, R478, B235)
   | ['中文按钮1Edit4', 'Edit5']
   | child_window(auto_id="textBox18", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L715, T178, R810, B204)
   | ['测试窗体Edit2', 'Edit6']
   | child_window(auto_id="textBox17", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L606, T178, R701, B204)
   | ['中文按钮1Edit5', 'Edit7']
   | child_window(auto_id="textBox16", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L495, T178, R590, B204)
   | ['中文按钮1Edit6', 'Edit8']
   | child_window(auto_id="textBox15", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L383, T178, R478, B204)
   | ['中文按钮1Edit7', 'Edit9']
   | child_window(auto_id="textBox14", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L715, T146, R810, B172)
   | ['测试窗体Edit3', 'Edit10']
   | child_window(auto_id="textBox13", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L606, T146, R701, B172)
   | ['中文按钮1Edit8', 'Edit11']
   | child_window(auto_id="textBox12", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L495, T146, R590, B172)
   | ['中文按钮1Edit9', 'Edit12']
   | child_window(auto_id="textBox11", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L383, T146, R478, B172)
   | ['中文按钮1Edit10', 'Edit13']
   | child_window(auto_id="textBox10", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L715, T115, R810, B141)
   | ['测试窗体Edit4', 'Edit14']
   | child_window(auto_id="textBox9", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L606, T115, R701, B141)
   | ['中文按钮1Edit11', 'Edit15']
   | child_window(auto_id="textBox8", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L495, T115, R590, B141)
   | ['中文按钮1Edit12', 'Edit16']
   | child_window(auto_id="textBox7", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L383, T115, R478, B141)
   | ['中文按钮1Edit13', 'Edit17']
   | child_window(auto_id="textBox6", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L715, T84, R810, B110)
   | ['测试窗体Edit5', 'Edit18']
   | child_window(auto_id="textBox5", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L606, T84, R701, B110)
   | ['测试窗体Edit6', 'Edit19']
   | child_window(auto_id="textBox4", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L495, T84, R590, B110)
   | ['测试窗体Edit7', 'Edit20']
   | child_window(auto_id="textBox3", control_type="System.Windows.Forms.TextBox")
   | 
   | Edit - ''    (L383, T84, R478, B110)
   | ['测试窗体Edit8', 'Edit21']
   | child_window(auto_id="textBox2", control_type="System.Windows.Forms.TextBox")
   | 
   | ListView - ''    (L225, T153, R359, B383)
   | ['ListView', '中文按钮1ListView']
   | child_window(auto_id="listView1", control_type="System.Windows.Forms.ListView")
   | 
   | Edit - ''    (L54, T84, R359, B110)
   | ['测试窗体Edit9', 'Edit22']
   | child_window(auto_id="textBox1", control_type="System.Windows.Forms.TextBox")
   | 
   | ListBox - ''    (L54, T153, R188, B383)
   | ['button1ListBox', 'ListBox']
   | child_window(auto_id="listBox1", control_type="System.Windows.Forms.ListBox")
   | 
   | Button - '中文按钮1'    (L225, T115, R358, B146)
   | ['中文按钮1', 'Button', '中文按钮1Button', 'Button0', 'Button1']
   | child_window(title="中文按钮1", auto_id="button2", control_type="System.Windows.Forms.Button")
   | 
   | Button - 'button1'    (L54, T115, R187, B146)
   | ['button1', 'Button2', 'button1Button']
   | child_window(title="button1", auto_id="button1", control_type="System.Windows.Forms.Button")

如果你的程序层级比较多,建议使用

print_control_identifiers(self, depth=None, filename=None)中的depth进行层数的限制,不然很容易一查就几万行,filename可以直接将结果保存为文件。

今天先写到这里,如果说print_control_identifiers的输出结果非常奇怪或是少,那么很可能就是

backend的问题,关于win32和uia的问题下次再讲

先干活了

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值