利用Python进行Excel自动化操作的过程中,尤其是涉及VBA时,可能遇到消息框/弹窗(MsgBox)。此时需要人为响应,否则代码卡死直至超时 1 2。根本的解决方法是VBA代码中不要出现类似弹窗,但有时我们无权修改被操作的Excel文件,例如这是我们进行自动化测试的对象。所以本文记录从代码角度解决此类问题的方法。

假想场景

使用xlwings(或者其他自动化库)打开Excel文件test.xlsm,读取Sheet1!A1单元格内容。很简单的一个操作:

import xlwings as xwwb = xw.Book('test.xlsm')
msg = wb.sheets('Sheet1').range('A1').value
print(msg)
wb.close()

然而不幸的是,这个文件在打开工作簿时进行了热情的欢迎仪式:

Private Sub Workbook_Open()MsgBox "Welcome"MsgBox "to open"MsgBox "this file."
End Sub

第一个弹窗Welcome就卡住了Excel,Python代码相应卡死在第一行。

基本思路

主程序中不可能直接处理或者绕过此类问题,也不能奢望有人随时蹲守解决此类问题——那就开启一个子线程来坚守吧。因此,解决方案是利用子线程监听并随时关闭弹窗,直到主程序圆满结束。

Excel中MsgBox弹窗的默认标题是Microsoft Excel,接下来以此为例捕捉窗口,并通过点击按钮关闭它。

pywinauto方案

pywinauto顾名思义是Windows界面自动化库,模拟鼠标和键盘操作窗体和控件 3。不同于先获取句柄再获取属性的传统方式,pywinauto的API更加友好和pythonic。例如,两行代码就可以实现窗口捕捉和点击:

win = Application(backend="win32").connect(title='Microsoft Excel')
win.Dialog.Button.click()

有关Python多线程的知识不在此展开,本文采用自定义线程类的方式,启动线程后自动执行run()函数。具体代码如下,构造函数中的两个参数:

  • title 需要捕捉的弹窗的标题,例如Excel弹窗默认为Microsoft Excel
  • interval 监听的频率,即每隔多少秒就检查一次
# listener_pywinauto.py
import time
from threading import Thread, Event
from pywinauto.application import Applicationclass MsgBoxListener(Thread):def __init__(self, title:str, interval:int):Thread.__init__(self)self._title = title self._interval = interval self._stop_event = Event()   def stop(self): self._stop_event.set()@propertydef is_running(self): return not self._stop_event.is_set()def run(self):while self.is_running:try:time.sleep(self._interval)self._close_msgbox()except Exception as e:print(e, flush=True)def _close_msgbox(self):'''Close the default Excel MsgBox with title "Microsoft Excel".'''        win = Application(backend="win32").connect(title=self._title)win.Dialog.Button.click()if __name__=='__main__':t = MsgBoxListener('Microsoft Excel', 1)t.start()time.sleep(10)t.stop()

于是,整个过程分为三步:

  • 启动子线程监听弹窗
  • 主线程中打开Excel开始自动化操作
  • 关闭子线程
import xlwings as xw
from listener_pywinauto import MsgBoxListener# start listen thread
listener = MsgBoxListener('Microsoft Excel', 3)
listener.start()# main process as before
wb = xw.Book('test.xlsm')
msg = wb.sheets('Sheet1').range('A1').value
print(msg)
wb.close()# stop listener thread
listener.stop()

到此问题基本解决,本地运行效果完全达到预期。但我的真实需求是以系统服务方式在服务器上进行Excel文件自动化测试,后续发现,当以系统服务方式运行时,pywinauto竟然捕捉不到弹窗!这或许是pywinauto一个潜在的问题 4

win32gui方案

那就只好转向相对底层的win32guipywin32库的一部分),所幸解决了上述问题。

# pip install pywin32
import win32gui
import win32con

以下仅列出MsgBoxListener类中关闭弹窗的步骤,其余代码完全一致:

def _close_msgbox(self):# find the top window by titlehwnd = win32gui.FindWindow(None, self._title)if not hwnd: return# find child buttonh_btn = win32gui.FindWindowEx(hwnd, None,'Button', None)if not h_btn: return# show texttext = win32gui.GetWindowText(h_btn)print(text)# click button        win32gui.PostMessage(h_btn, win32con.WM_LBUTTONDOWN, None, None)time.sleep(0.2)win32gui.PostMessage(h_btn, win32con.WM_LBUTTONUP, None, None)time.sleep(0.2)

更一般地,当同时存在默认标题和自定义标题的弹窗时,就不便于根据标题进行捕捉。例如

MsgBox "Message with default title.", vbInformation,
MsgBox "Message with title My App 1", vbInformation, "My App 1"
MsgBox "Message with title My App 2", vbInformation, "My App 2"

那就扩大范围,尝试点击任何包含确定性描述按钮(例如OKYesConfirm)来关闭窗口。

def _close_msgbox(self):'''Click button to close message box if has text "OK", "Yes" or "Confirm".'''# Get handles of all top wondowsh_windows = []win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), h_windows) for h_window in h_windows:            # Get child button with text OK, Yes or Confirm of given window.h_btn = win32gui.FindWindowEx(h_window, None,'Button', None)if not h_btn: continue# check button texttext = win32gui.GetWindowText(h_btn)if not text.lower() in ('ok', 'yes', 'confirm'): continue# click buttonwin32gui.PostMessage(h_btn, win32con.WM_LBUTTONDOWN, None, None)time.sleep(0.2)win32gui.PostMessage(h_btn, win32con.WM_LBUTTONUP, None, None)time.sleep(0.2)

全文结束,以后再也不怕意外弹窗了。


  1. Handling VBA popup message boxes in Microsoft Excel ↩︎

  2. Trying to catch MsgBox text and press button in xlwings ↩︎

  3. What is pywinauto ↩︎

  4. Remote Execution Guide ↩︎

利用Python子进程关闭Excel自动化过程出现的弹窗相关推荐

  1. python对数据进行合并的函数_利用Python pandas对Excel进行合并的方法示例

    前言 在网上找了很多Python处理Excel的方法和代码,都不是很尽人意,所以自己综合网上各位大佬的方法,自己进行了优化,具体的代码如下. 博主也是新手一枚,代码肯定有很多需要优化的地方,欢迎各位大 ...

  2. python自动汇总excel_RPA手把手:Python轻松实现EXCEL自动化

    原标题:RPA手把手:Python轻松实现EXCEL自动化 了解RPA请访问: www.i-search.com.cn 艺赛旗-RPA机器人免费下载: www.i-search.com.cn/inde ...

  3. python利用pandas合并excel表格代码_利用Python pandas对Excel进行合并的方法示例

    前言 在网上找了很多Python处理Excel的方法和代码,都不是很尽人意,所以自己综合网上各位大佬的方法,自己进行了优化,具体的代码如下. 博主也是新手一枚,代码肯定有很多需要优化的地方,欢迎各位大 ...

  4. python pandas合并单元格_利用Python pandas对Excel进行合并的方法示例

    前言 在网上找了很多Python处理Excel的方法和代码,都不是很尽人意,所以自己综合网上各位大佬的方法,自己进行了优化,具体的代码如下. 博主也是新手一枚,代码肯定有很多需要优化的地方,欢迎各位大 ...

  5. 2020互联网数据分析师教程视频 统计学分析与数据实战 r语言数据分析实战 python数据分析实战 excel自动化报表分析实战 excel数据分析处理实战

    2020互联网数据分析师教程视频 统计学分析与数据实战 r语言数据分析实战 python数据分析实战 excel自动化报表分析实战 excel数据分析处理实战

  6. python自动化测试脚本可以测php吗_利用Python语言实现实验室自动化

    作为一名系统和应用工程师,得益于自动化仪器和软件,我已经节省了不计其数的日日夜夜;例如LabVIEW,这是一款系统设计平台和开发环境,支持可视化编程语言.LabVIEW支持用户利用友好的图形用户界面( ...

  7. 怎么用python编写个apk_【android】如何利用python做Android项目自动化构建,并一键实现构建结果发送到钉钉通知以及通过二维码下载apk或者其他处理等功能...

    今天我们来谈一谈用python做Android项目自动化构建的过程.我们知道在常规的Android开发过程中,开发人员打包的时候需要在Android Studio当中进行,或者通过gradle命令,但 ...

  8. 利用python实现百度竞价自动化效果监控

    百度竞价效果监控 很多客户朋友做了百度推广,全国或者多地区推广,很多时候监控不到本地以外城市的推广情况,不方便推广的调整. 问题 百度推广可以选择不同地区不同出价策略和不同的创意等,但是监控不到数据的 ...

  9. 【利用python+pandas 拆分excel表格】

    利用python拆分excel表格 我们常常会遇到一个表格中包含各种类型的数据,想要把表格按照不同类型拆分到多个工作簿,使用python几行代码就可以轻松搞定 首先是安装需要的包 先cmd命令行安装下 ...

最新文章

  1. 我用YOLOX露了一手,记录一下模型部署、优化及训练的实现全过程
  2. MySQL索引管理及执行计划
  3. python多进程共享内存
  4. Android进阶知识:绘制流程(上)
  5. shell命令tree
  6. 开源分布式中间件 DBLE 快速入门指南
  7. C++ 有参构造 无参构造 拷贝构造 以及参数化列表 成员对象之间的执行关系
  8. oracle database 11g plsql 程序设计,oracle-database-11g-plsql-编程实战笔记
  9. socket接收时信号量阻塞了会丢数据吗_浅谈Java网络编程——非阻塞I/O
  10. PDFsam Basic for mac(合并拆分PDF文档)支持m1
  11. USACO Section 1.2 Name That Number
  12. C语言猜数字小游戏---详解+源码
  13. 高等数学(第七版)同济大学 习题2-2 个人解答(前7题)
  14. 芝诺数解|「十一」千里姻缘一“线”牵—重庆网络婚恋分析报告
  15. 快手申请快手联盟商选商标,商标注册的申请程序有哪些?
  16. 打开浏览器默认打开hao123网站的主页怎么取消设置?
  17. ElementUI table 单元格编辑合并
  18. 李佳琦以特殊人才落户上海,“带货一哥”即将成为“新上海人”
  19. HDFS RPC 调度策略 DecayRpcScheduler 与 BackOff
  20. Studio5000和SE中如何使用替换功能

热门文章

  1. 【饮食的迷思--人类必看的真正的饮食长寿指南--来自英国国王学院顶级遗传性流行病教授】---智人必须会的生活技能 by Tim Spector--饮食的迷思
  2. Anaconda => PyCharm => CUDA => cudnn => PyTorch 环境配置
  3. mac下编译安卓源码
  4. 【JavaScript】案例1:使用JS完成注册页面校验
  5. 神州优车粗暴裁员:人与人之间最起码的尊重呢?
  6. 客户体验决胜2022,低代码是快速取胜之道
  7. css实现带边框,半透明气泡定位浮层效果
  8. 安卓搭建文件共享服务器,安卓文件共享云服务器
  9. Unity Sunny Land开发流程(二)
  10. 第二章:Java面向对象:抽象(abstract)类、模板方法设计模式、接口(interface)、关键字-implements(实现)、代理模式