python自动化工具之pywinauto

最近在看一个学习视频,视频是用屏幕录像大师录的,保存成exe格式。虽然也能播放,但是比较麻烦,只能在window平台播放,自带的播放器不能进行快进变速等。于是想把exe转成mp4等主流的视频格式。幸运的是,屏幕录像专家自带转换的功能。具体步骤可参考百度经验 EXE视频如何直接转换为MP4.
由于是一系列视频,每个视频都得这么导一次,很是麻烦,而且是重复操作。想到看能不能使用python脚本来模拟这一系列操作。

win32*模块

因为之前使用win32* 模块模拟人工操作,见之前写的两篇文章python使用win32*模块模拟人工操作——城通网盘下载器(零) , python使用win32*模块模拟人工操作——城通网盘下载器(一).
所以首先想到的是使用win32*模块进行模拟操作。
但是,事情不会总是一帆风顺。首先第一步(从主程序的菜单栏中调出EXE转MP4插件)就遇到了问题。如下图所示。
这里写图片描述
使用Spy++查看了一下,如图所示,发现菜单栏并不属于主窗体底下的子窗体,而状态栏、工具栏都属于主窗体的子窗体。
这里写图片描述

窗口的菜单就像窗口的标题栏一样,是窗口自身的一部分,不是其他窗体控件,也就没有办法用FindWindow和FindWindowEx返回句柄。所以要对菜单进行操作的话,我们需要新的函数,也就是GetMenu,GetSubMenu和GetMenuItemID,它们也都属于win32gui模块。
这些函数的具体用法可以参考 橘子一方的博客
如何利用Python和win32编程避免重复性体力劳动(二)——菜单操作:GetMenu,GetSubMenu,GetMenuItemID以及wParam的HIWORD&LOWORD
使用这些函数试了一下,发现效果并不好,可能是对window本身运行机制理解的不够深;另一方面可能是这方面的文档不多;还有就是这个模块已经停止更新好久了,坑没人填。

pywinauto

答案总比问题多。无意中搜到了这个人的网页Python AHK – Python automation package – Automation made simple
文中提到:

win32gui
I use win32gui for simple window handling such as moving and resizing. I personally prefer win32gui for short, simple tasks. There is one problem with win32gui, there is no real help file. Fortunately, there are plenty of examples out there. Google is your friend.

pywinauto
I use pywinauto for more complex window tasks. An example would be if I had to access a menu within a program (like File-New). For the most part it is enough to simply use win32gui. Please refer to the pywinauto documentation for help. If you are starting out with python automation, I recommend you to just use pywinauto.

mouse
Mouse is a small module to control the mouse. This is the most robust way I have found so far. The version I use is an extension of a module I found at stackoverflow – ctypes mouse_event. You can find more info on mouse in my blog post Mouse.py – Control your mouse in python.

为什么是pywinauto

作者在写pywinauto的时候,市面上已经有许多自动化脚本了。
这里写图片描述
关于pywinauto的特点,作者总结了几点:
– 提供面向对象的方法
– 使用python使调用更简洁
– 很方便的本地化(非英语语言的主要需求),这个之后会说到。

pywinauto安装

pywinauto正中我的痛点。pywinauto现在放在github上托管,关键是一直在更新。。地址: https://github.com/pywinauto/pywinauto ,官方文档https://pywinauto.readthedocs.io/en/latest/getting_started.html
README中的例子使用pywinauto操作notepad简单的无法想象。于是有了试一试的冲动。
打开anaconda prompt终端,使用pip安装pywinauto模块。

pip install -U pywinauto

安装完后,在终端中输入语句:

from pywinauto.application import Application

若没有出现错误,则说明该模块可以正常使用了。

pywinauto使用

首先,导入这个模块

from pywinauto.application import Application

(一) 判断程序的backend

注意,首先需要判断你要进行的程序是用什么语言写的,在实例化的时候会有区别,主要是判断backend是什么。
这里写图片描述

那么如何判断程序的backend是’win32’还是’uia’呢?官方文档中推荐使用spy++和inspect来检查。有人专门整理了一下,放在github上了https://github.com/blackrosezy/gui-inspect-tool
spy++咱们之前讲过。接下来将将如何使用inspect来判断backend的类别。

Switch Inspect.exe into UIA mode (using MS UI Automation). If it can show more controls and their properties than Spy++, probably the "uia" backend is your choice.

将inspect左上角的下拉列表中切换到“UI Automation”,然后鼠标点一下你需要测试的程序窗体,inspect就会显示相关信息。
下图为点击window文件夹的结果,inspect中显示了相关的信息,如下图所示。说明backend为uia。
这里写图片描述

再来看点击 屏幕录像专家程序的结果
这里写图片描述
inspect中显示拒绝访问,说明屏幕录像专家程序的backend应该是win32。

(二)确定自动化入口点

这里主要是限制自动化控制进程的范围。如一个程序有多个实例,自动化控制一个实例,而保证其他实例(进程)不受影响。
主要有两种对象可以建立这种入口点——Application() , Desktop().
Application的作用范围是一个进程,如一般的桌面应用程序都为此类。
Desktop的作用范围可以跨进程。主要用于像win10的计算器这样包含多个进程的程序。这种目前比较少见。使用方法见entry-points-for-automation

(三)连接到进程

建立好入口后,我们需要连接到进程中。这里有两种方法:
– 重新开启一个进程。使用Application对象的start()方法

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

这里的cmd_line参数就是你使用命令行启动程序的命令语句。所以可以很方便的实现带输入参数的程序启动。
启动屏幕录像专家,可以使用如下代码:

app = Application().start(r"D:\Program Files (x86)\tlxsoft\屏幕录像专家 共享版 V2017\屏录专家.exe")
  • 连接到已有的进程。使用Application对象的connect()方法。这个方法对已有进程的绑定非常灵活。
    1.使用进程ID (PID)进行绑定。
app = Application().connect(process=19188)

进程的PID可以在任务管理器中查看。
这里写图片描述

2.使用窗口句柄绑定

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

窗口句柄可以在Spy++中查看
这里写图片描述

3.使用程序路径绑定

app = Application().connect(path=r"D:\Program Files (x86)\tlxsoft\屏幕录像专家 共享版 V2017\屏录专家.exe")

4.使用标题、类型等匹配

app = Application().connect(title_re="屏幕录像专家.*", class_name="TMainForm")

第1、2种方法通用性不强,每次运行ID和窗口句柄都可能不一样。第3种方法最直接简单,而第4种方法灵活性最强。

(四) 选择菜单项

个人感觉pywinauto最方便的地方之一就是能够很轻易的操作菜单,而且对于多级菜单毫无压力。
这里写图片描述

下面介绍两种方法来操作它:
– 使用menu_select()函数进行选择
要从屏幕录像专家程序的菜单选中转换工具->EXE/LXE转成MP4。只需要在python中加入如下语句:

dlg_spec = app.window(title='屏幕录像专家 V2017')
dlg_spec.menu_select(r"转换工具->EXE/LXE转成MP4")
  • 使用快捷键进行选择
    可以看到,菜单的每个选项都对应着快捷键,可以使用组合的快捷键直接访问我们需要的选项。
    使用type_keys()函数,这里需要的快捷键是Alt+T+P:
dlg_spec = app.window(title='屏幕录像专家 V2017')
dlg_spec.type_keys('%TP')

对于一些特殊符号的快捷键,对应的码表如下:

SHIFT                            +      
CTRL                             ^      
ALT                               %
空格键                            {SPACE}

BACKSPACE                        {BACKSPACE}{BS}   or   {BKSP}      
BREAK                            {BREAK}      
CAPS   LOCK                      {CAPSLOCK}      
DEL   or   DELETE                {DELETE}   or   {DEL}      
DOWN   ARROW                     {DOWN}      
END                              {END}      
ENTER                            {ENTER}   or   ~      
ESC                              {ESC}      
HELP                             {HELP}      
HOME                             {HOME}      
INS   or   INSERT                {INSERT}   or   {INS}      
LEFT   ARROW                     {LEFT}      
NUM   LOCK                       {NUMLOCK}      
PAGE   DOWN                      {PGDN}      
PAGE   UP                        {PGUP}      
PRINT   SCREEN                   {PRTSC}      
RIGHT   ARROW                    {RIGHT}      
SCROLL   LOCK                    {SCROLLLOCK}      
TAB                              {TAB}      
UP   ARROW                       {UP}     
+                                {ADD}      
-                                {SUBTRACT}      
*                                {MULTIPLY}      
/                                {DIVIDE}

上述操作结束后,EXE转MP4的插件窗口就弹出来了。

pywinauto的使用——各控件的操作

对于常见的窗口程序,需要点点填填的控件有输入框(Edit)、按钮(Button)、复选框(CheckBox)、单选框(RadioButton)、下拉列表(ComboBox).
关于各个控件的函数方法,可以查阅官网 Methods available to each different control type

下面结合程序讲一下各个控件的用法。
这里写图片描述
要进行文件格式转换,一般需要三步:
1. 选择源文件
2. 转换参数配置
3. 选择输出文件位置
在这里,就是现在文件名后面的编辑框中填入源文件的位置,然后勾选自动扩帧复选框,然后点击转换按钮,最后会弹出一个另存为的文件对话框来选择目标文件位置。
目的明确了,接下来就是如何干的问题了。分两步:
1. 找到控件
2. 操作控件
如何让程序找到控件呢?

如何匹配控件

最简单的方法就是通过空间特征进行匹配。窗体也可以看成是一个大控件。匹配窗口的方法除了前面提到的window()方法,还可以通过中括号加窗口名。如:

dlg_spec = app.window(title=r'EXE/EXE 转 MP4')   # 1
dlg_spec = app[r'EXE/EXE 转 MP4']    # 2

除了title,还可以使用class或者title+class或者相近的text和类来匹配控件。

另外一种方法就是我们知道了这个程序的层次结构,然后类似寻到DOM元素一样一层一层的匹配。
那么如何找到这个层次结构呢。pywinauto提供了print_control_identifiers()函数来显示该窗体下所有控件的结构。

dlg_spec = app[r'EXE/EXE 转 MP4']
dlg_spec.print_control_identifiers()

输出如下:

Control Identifiers:

TEXE2FLVForm - 'EXE/EXE 转 MP4'    (L202, T55, R496, B562)
['EXE/EXE 转 MP4TEXE2FLVForm', 'TEXE2FLVForm', 'EXE/EXE 转 MP4']
child_window(title="EXE/EXE 转 MP4", class_name="TEXE2FLVForm")
   | 
   | TTrackBar - ''    (L273, T189, R470, B204)
   | ['TTrackBar', '下载安装相关视频编码器TTrackBar']
   | child_window(class_name="TTrackBar")
   | 
   | TGroupBox - '图像缩小输出时处理方法'    (L212, T460, R483, B522)
   | ['图像缩小输出时处理方法', 'TGroupBox', '图像缩小输出时处理方法TGroupBox', 'TGroupBox0', 'TGroupBox1']
   | child_window(title="图像缩小输出时处理方法", class_name="TGroupBox")
   |    | 
   |    | RadioButton - '部分画面鼠标自动跟随(动态显示光标附近图像)'    (L232, T501, R470, B515)
   |    | ['RadioButton', '部分画面鼠标自动跟随(动态显示光标附近图像)RadioButton', 'RadioButton1', '部分画面鼠标自动跟随(动态显示光标附近图像)', 'RadioButton0']
   |    | child_window(title="部分画面鼠标自动跟随(动态显示光标附近图像)", class_name="TRadioButton")
   |    | 
   |    | RadioButton - '全画面缩放(文字可能会变不清晰)'    (L232, T480, R409, B495)
   |    | ['全画面缩放(文字可能会变不清晰)', 'RadioButton2', '全画面缩放(文字可能会变不清晰)RadioButton']
   |    | child_window(title="全画面缩放(文字可能会变不清晰)", class_name="TRadioButton")
   | 
   | RadioButton - '部分画面鼠标自动跟随(动态显示光标附近图像)'    (L232, T501, R470, B515)
   | ['RadioButton', '部分画面鼠标自动跟随(动态显示光标附近图像)RadioButton', 'RadioButton1', '部分画面鼠标自动跟随(动态显示光标附近图像)', 'RadioButton0']
   | child_window(title="部分画面鼠标自动跟随(动态显示光标附近图像)", class_name="TRadioButton")
   | 
   | RadioButton - '全画面缩放(文字可能会变不清晰)'    (L232, T480, R409, B495)
   | ['全画面缩放(文字可能会变不清晰)', 'RadioButton2', '全画面缩放(文字可能会变不清晰)RadioButton']
   | child_window(title="全画面缩放(文字可能会变不清晰)", class_name="TRadioButton")
   | 
   | Edit - '扩帧的作用是让鼠标移动更加平滑,文件也会变大一些\r\n如要扩帧建议扩帧后不要超过15帧/秒\r\n'    (L225, T298, R477, B319)
   | ['自动扩帧Edit0', '自动扩帧Edit', '自动扩帧Edit1', 'Edit', 'Edit1', 'Edit0']
   | child_window(title="扩帧的作用是让鼠标移动更加平滑,文件也会变大一些\r\n如要扩帧建议扩帧后不要超过15帧/秒\r\n", class_name="TMemo")
   | 
   | Edit - '除非对FLV的编码器非常熟悉,完全知道自己想要什\r\n么,否则请不要修改默认的编码器设置\r\n'    (L214, T115, R483, B143)
   | ['Edit2', '浏览Edit']
   | child_window(title="除非对FLV的编码器非常熟悉,完全知道自己想要什\r\n么,否则请不要修改默认的编码器设置\r\n", class_name="TMemo")
   | 
   | Button - '转换(&Z)'    (L300, T529, R362, B551)
   | ['Button', '转换(&Z)', 'Button0', 'Button1', '转换(&Z)Button']
   | child_window(title="转换(&Z)", class_name="TButton")
   | 
   | Button - '关闭(&C)'    (L370, T529, R432, B551)
   | ['关闭(&C)', '关闭(&C)Button', 'Button2']
   | child_window(title="关闭(&C)", class_name="TButton")
   | 
   | Button - '浏览'    (L434, T88, R483, B106)
   | ['Button3', '浏览', '浏览Button']
   | child_window(title="浏览", class_name="TButton")
   | 
   | Edit - ''    (L259, T88, R429, B107)
   | ['EXE/EXE 转 MP4Edit', 'Edit3']
   | child_window(class_name="TEdit")
   | 
   | Edit - '5'    (L286, T273, R307, B292)
   | ['自动扩帧Edit2', 'Edit4']
   | child_window(title="5", class_name="TEdit")
   | 
   | CheckBox - '自动扩帧'    (L219, T277, R280, B292)
   | ['CheckBox1', '自动扩帧', '自动扩帧CheckBox', 'CheckBox', 'CheckBox0']
   | child_window(title="自动扩帧", class_name="TCheckBox")
   | 
   | Button - '下载安装相关视频编码器'    (L273, T166, R409, B184)
   | ['下载安装相关视频编码器Button', '下载安装相关视频编码器', 'Button4']
   | child_window(title="下载安装相关视频编码器", class_name="TButton")
   | 
   | ComboBox - 'AAC(推荐)'    (L273, T221, R409, B240)
   | ['ComboBox1', 'ComboBox0', '下载安装相关视频编码器ComboBox', 'ComboBox']
   | child_window(title="AAC(推荐)", class_name="TComboBox")
   | 
   | ComboBox - 'H264(AVC X264) 推荐'    (L273, T146, R409, B165)
   | ['ComboBox2', 'EXE/EXE 转 MP4ComboBox']
   | child_window(title="H264(AVC X264) 推荐", class_name="TComboBox")
   | 
   | TGroupBox - '输出图像尺寸'    (L212, T331, R483, B454)
   | ['输出图像尺寸TGroupBox', 'TGroupBox2', '输出图像尺寸']
   | child_window(title="输出图像尺寸", class_name="TGroupBox")
   |    | 
   |    | CheckBox - '输出时画面保持原长宽比例'    (L286, T433, R436, B447)
   |    | ['CheckBox2', '输出时画面保持原长宽比例CheckBox', '输出时画面保持原长宽比例']
   |    | child_window(title="输出时画面保持原长宽比例", class_name="TCheckBox")
   |    | 
   |    | ComboBox - ''    (L314, T410, R457, B429)
   |    | ['设置为 宽度:ComboBox', 'ComboBox3']
   |    | child_window(class_name="TComboBox")
   |    | 
   |    | ComboBox - '1/2'    (L314, T369, R396, B388)
   |    | ['ComboBox4', '按倍数缩小为ComboBox']
   |    | child_window(title="1/2", class_name="TComboBox")
   |    | 
   |    | Edit - '300'    (L402, T388, R429, B407)
   |    | ['设置为 宽度:Edit0', '设置为 宽度:Edit', 'Edit5', '设置为 宽度:Edit1']
   |    | child_window(title="300", class_name="TEdit")
   |    | 
   |    | Edit - '400'    (L314, T388, R341, B407)
   |    | ['设置为 宽度:Edit2', 'Edit6']
   |    | child_window(title="400", class_name="TEdit")
   |    | 
   |    | RadioButton - '设置为 宽度:'    (L232, T392, R308, B406)
   |    | ['设置为 宽度:', '设置为 宽度:RadioButton', 'RadioButton3']
   |    | child_window(title="设置为 宽度:", class_name="TRadioButton")
   |    | 
   |    | RadioButton - '按倍数缩小为'    (L232, T372, R314, B386)
   |    | ['RadioButton4', '按倍数缩小为RadioButton', '按倍数缩小为']
   |    | child_window(title="按倍数缩小为", class_name="TRadioButton")
   |    | 
   |    | RadioButton - '原始大小'    (L232, T351, R287, B366)
   |    | ['原始大小', '原始大小RadioButton', 'RadioButton5']
   |    | child_window(title="原始大小", class_name="TRadioButton")
   | 
   | CheckBox - '输出时画面保持原长宽比例'    (L286, T433, R436, B447)
   | ['CheckBox2', '输出时画面保持原长宽比例CheckBox', '输出时画面保持原长宽比例']
   | child_window(title="输出时画面保持原长宽比例", class_name="TCheckBox")
   | 
   | ComboBox - ''    (L314, T410, R457, B429)
   | ['设置为 宽度:ComboBox', 'ComboBox3']
   | child_window(class_name="TComboBox")
   | 
   | ComboBox - '1/2'    (L314, T369, R396, B388)
   | ['ComboBox4', '按倍数缩小为ComboBox']
   | child_window(title="1/2", class_name="TComboBox")
   | 
   | Edit - '300'    (L402, T388, R429, B407)
   | ['设置为 宽度:Edit0', '设置为 宽度:Edit', 'Edit5', '设置为 宽度:Edit1']
   | child_window(title="300", class_name="TEdit")
   | 
   | Edit - '400'    (L314, T388, R341, B407)
   | ['设置为 宽度:Edit2', 'Edit6']
   | child_window(title="400", class_name="TEdit")
   | 
   | RadioButton - '设置为 宽度:'    (L232, T392, R308, B406)
   | ['设置为 宽度:', '设置为 宽度:RadioButton', 'RadioButton3']
   | child_window(title="设置为 宽度:", class_name="TRadioButton")
   | 
   | RadioButton - '按倍数缩小为'    (L232, T372, R314, B386)
   | ['RadioButton4', '按倍数缩小为RadioButton', '按倍数缩小为']
   | child_window(title="按倍数缩小为", class_name="TRadioButton")
   | 
   | RadioButton - '原始大小'    (L232, T351, R287, B366)
   | ['原始大小', '原始大小RadioButton', 'RadioButton5']
   | child_window(title="原始大小", class_name="TRadioButton")

仔细找了一下,文件名后面的编辑框的属性为

   | Edit - ''    (L259, T88, R429, B107)
   | ['EXE/EXE 转 MP4Edit', 'Edit3']

所以我们可以通过控件的text或者title来查找控件。如:

edit = dlg_spec['']   # 1
edit = dlg_spec['Edit2']    # 2
edit = dlg_spec.Edit2   # 3

注意,对于输入控件Edit,一般不建议使用text内容绑定,因为Edit的text内容会发生变化。另外,绑定的控件也可能不唯一。对于title,我这里可能理解不够,属性显示的是Edit3,但实际上绑定的时候用的却是Edit2,也就是数字要减一

如何操作控件

前面已经讲了如何找到一个控件,接下来就是怎么操作它了。对于Edit控件,要么就是向里面写内容,要么就是读里面的内容。
在这里,我们需要向Edit3写入要转换文件的路径(r’E:\test test .exe’),这里的文件名我特意在中间加了空格。

edit.set_text(r'E:\test test .exe')     # 1
edit.type_keys(r'E:\test test .exe',with_spaces = True) # 2

上述代码第一种方法是直接设置edit的text,而第二种是在里面模拟键盘输入(如果字符串中没有空格,可以省略后面的参数),殊途同归。
这里写图片描述
但是实际上,使用第二种方法输入并没有什么效果,因为该编辑框设置了禁止输入(自己手动敲键盘,发现编辑框没有反应)。(这里埋了一个雷,在我们成功将路径写入编辑框的那刻,已经踩着了这颗雷。)
接着配置转换参数。
这里写图片描述
现在是10帧/秒,建议扩帧后不超过15帧/秒。所以也就没有必要扩帧了。
接着直接模拟点击转换吧。

dlg_spec.Button0.click()

不出所料,弹出“另存为”对话框。
这里写图片描述
假设我们按照默认的路径存储,那么这里只需要点击“保存”就可以了。

app['另存为']['保存'].click()

注意,所有窗口都应该在app中索引。
执行完上述语句后,发现弹出错误提示。
这里写图片描述
* 触着的雷终于爆炸了,哈哈!!!*

问题分析

上篇尝试使用脚本进行模拟操作,最后失败了。
这里写图片描述

根据错误提示,应该是转换参数有问题,且应该是源文件的参数有问题。
这里写图片描述
看上面的对话框选项,也没有对源文件配置的选项(除了视频编码器,应该不是这个问题)。
回到最开始的地方——输入文件名。程序设计不允许手动输入文件路径,是不是别有用意呢?
我们点击“浏览”按钮。弹出“打开”对话框。
这里写图片描述
选择“test test .exe”文件,点击“打开”按钮。回到主界面,发现第二步中的参数变了!
这里写图片描述
变成了“5帧/秒”,而且自动扩帧变成了3倍。原来这个“浏览”按钮里有猫腻。也就是说,我们只有按着这个姿势才能正确的转换视频格式。Ok,Go!

解决方案

选择源文件

首先,点击“浏览”按钮,弹出“打开”对话框。

dlg_spec['浏览'].click()

这里写图片描述
先说说手动操作的步骤: 先在查找范围后的ComboBox中找到要转换的源程序所在的文件夹,中间的list中就会出现该文件中的所有文件和文件夹,选中源文件,文件名后面的Edit里面就会显示文件名,最后点击打开按钮完成选择。
如果按照手动操作的步骤进行模拟,脚本编写起来会比较复杂,这里我们使用了一个trick。直接在文件名的编辑框中输入源文件的绝对路径,然后点击打开按钮完成选择。

dlg_open = app['打开']
dlg_open.Edit.set_text(r'E:\test test .exe')
dlg_open['打开'].click()

这里写图片描述
有三点需要说明的地方:
1、 “打开”对话框中只有一个输入框,所以,使用Edit就可以绑定文件名的编辑框。
2、 编辑框是可编辑的,也就是说我们可以使用type_keys()函数模拟键盘输入文件路径,但是需要显式指明字符串中含有空格,不然空格会被忽略掉。而且需要先清空编辑框的内容。

dlg_open.Edit.type_keys(r'E:\test test .exe')

这里写图片描述
图中为没有事先清理编辑框和没有显式指明含有空格的结果。
3、 若文件绝对路径输入错误,点击“打开”按钮会先跳到路径中最后一个文件夹,然后再点击“打开”,提示找不到文件的错误。
这里写图片描述

配置参数

现在变成了5帧/秒,建议扩频后不超过15帧/秒,也就是扩频3倍即可,默认参数也是3,所以只需要勾选上“自动扩频”即可。

dlg_spec.CheckBox0.check()

输出路径选择

勾选上之后,点击“转换”按钮,弹出“另存为”对话框,直接点保存。

dlg_spec['转换'].click()
app['另存为']['保存'].click()

接着就出现正在处理的进度框了,说明配置正常。
这里写图片描述
处理完成后,会弹出一个对话框。
这里写图片描述
我们需要做的就是找到这个对话框,点击里面的OK按钮,结束这个转换流程。

app['屏幕录像专家'].Ok.click()

到此,使用脚本转换一个视频的流程已经走完了,批量转换视频无非就是把前面的文件名改一下,重复这些动作。

等待窗口出现

实际操作中,你可能还会发现一个问题。每个视频的大小不一样,所需的转换时间也不一样,我怎么知道它什么时候处理完呢?最后的OK按钮只有处理完后弹窗后才能点击呀。
这里讲解3种方法。
1、等待法。先预估一个转换所需的最长时间,保证此时已经转换完并弹窗,然后让程序等待这么长时间后在点击Ok按钮。

import time
...
time.sleep(100)
app['屏幕录像专家'].Ok.click()

2、 查询法。写个循环,一直查询是否存在“屏幕录像专家”弹窗,若存在,则退出循环。

...
while(True):
    if app.window(title=r'屏幕录像专家',class_name='TMessageForm').exists():
        break
app['屏幕录像专家'].Ok.click()

注意,在查询的时候,最好不要用app[‘屏幕录像专家’].exists()。这个匹配不精准,如下图中的最后一个句柄。这个句柄在开启程序后就一直存在,且于我们要找的对话框title一样,所以我们在查找的时候需要加上class_name。
这里写图片描述
3、查询等待法。查询有个缺点就是如果一直没出现,就会一直等待。所以我们最好设置一个等待时间限。
使用模块中自带的wait函数就可以实现该功能了,了解更多Waiting for Long Operations

app.window(title=r'屏幕录像专家',class_name='TMessageForm').Wait('enabled',timeout=300)
app['屏幕录像专家'].Ok.click()

好了,大功告成!

完整代码:

# -*- coding: utf-8 -*-
"""
Created on Wed Oct  4 16:52:13 2017

@author: x
"""
from pywinauto.application import Application

program_path = r"D:\Program Files (x86)\tlxsoft\屏幕录像专家 共享版 V2017\屏录专家.exe"
app = Application().start(program_path)
dlg_spec = app.window(title_re='屏幕录像专家.*',class_name='TMainForm')
#dlg_spec.type_keys('%TP')
dlg_spec.menu_select(r"转换工具->EXE/LXE转成MP4")
dlg_spec = app[r'EXE/EXE 转 MP4']
dlg_spec.print_control_identifiers()    # 打印该窗体下的所有控件结构


for line in open('ToBeConvert.txt'):
    filename = line.strip()     # 去掉读取每一行时最后带着的空格和回车符
    dlg_spec.Button3.click()    # 点击“浏览”按钮
    dlg_open = app.window(title=r'打开')  # 获取“打开”对话框句柄
    dlg_open.Edit.type_keys(filename,with_spaces = True)
#    dlg_open.Edit.set_text(filename)     # 将文件绝对路径写入编辑框中
    dlg_open.Button0.click()             # 点击“打开”按钮
    dlg_open.wait_not('visible')

    dlg_spec.CheckBox0.check()          # 勾选自动扩帧
    dlg_spec.Button0.click()            # 点击“转换”

    app['另存为'].Button0.click()        # 点击“另存为”对话框的“保存”按钮

    app.window(title=r'屏幕录像专家',class_name='TMessageForm').Wait('enabled',timeout=300)       # 等待转换结束
    app.window(title=r'屏幕录像专家',class_name='TMessageForm').Ok.click()        # 关闭转换完成后弹出的对话框

用法:

  1. 根据屏幕录像专家程序的安装位置修改变量‘program_path’的值。
  2. 在当前目录新建一个 ToBeConvert.txt 文档,每行写上一个带转换的源文件,目标文件目录默认与源文件相同。
    如:
E:\test test .exe
E:\test test1.exe