Python开发案例之用Python子进程关闭Excel自动化中的弹窗

news2024/11/18 12:23:21

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

假想场景

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

import xlwings as xw

wb = 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代码相应卡死在第一行。
IMG_256
基本思路

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

解决这个问题,需要以下两个知识点(基础知识请课外学习):

Python多线程(本文采用threading.Thread)

Python界面自动化库(本文涉及pywinauto和pywin32)

pywinauto方案

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

from pywinauto.application import Application

win = Application(backend=“win32”).connect(title=‘Microsoft Excel’)

win.Dialog.Button.click()

本文采用自定义线程类的方式,启动线程后自动执行run()函数来完成上述操作。具体代码如下,注意构造函数中的两个参数:

title 需要捕捉的弹窗的标题,例如Excel默认弹窗的标题为Microsoft Excel

interval 监听的频率,即每隔多少秒检查一次

listener.py

import time

from threading import Thread, Event

from pywinauto.application import Application

class 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()  

@property  

def 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', 3)  

t.start()  

time.sleep(10)  

t.stop()

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

启动子线程监听弹窗

主线程中打开Excel开始自动化操作

关闭子线程

import xlwings as xw

from listener 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方案

那就只好转向相对底层的win32gui,所幸完美解决了上述问题。

win32gui是pywin32库的一部分,所以实际安装命令是:

pip install pywin32

整个方案和前文描述完全一致,只是替换MsgBoxListener类中关闭弹窗的方法:

import win32gui, win32con

def _close_msgbox(self):

# find the top window by title  

hwnd = win32gui.FindWindow(None, self._title)  

if not hwnd: return  

# find child button  

h_btn = win32gui.FindWindowEx(hwnd, None,'Button', None)  

if not h_btn: return  

# show text  

text = 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”

那就扩大搜索范围,依次点击所有包含确定性描述的按钮(例如OK,Yes,Confirm)来关闭弹窗。同理替换MsgBoxListener类的_close_msgbox()方法(同时构造函数中不再需要title参数):

def _close_msgbox(self):

'''Click any button ("OK", "Yes" or "Confirm") to close message box.'''  

# get handles of all top windows  

h_windows = []  

win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), h_windows)   

# check each window      

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 text  

    text = win32gui.GetWindowText(h_btn)  

    if not text.lower() in ('ok', 'yes', 'confirm'): continue  

    # 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)

最后,实例演示结束全文,以后再也不用担心意外弹窗
IMG_257
文章来源:网络 版权归原作者所有

上文内容不用于商业目的,如涉及知识产权问题,请权利人联系小编,我们将立即处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/136016.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在专网建设场景,LoRa和NB的技术优劣对比

先说结论:运营商在大铺NB,LoRa更适用于专网。 对于某个企业或者组织的实际应用来说,最后很可能是nb做骨架,lora做补充,混合应用。除非是nb在覆盖继续完善做到无死角 其实,对于物联网复杂的应用场景来说&am…

国产的内网穿透工具也很优秀,这10款工具推荐正在寻找的你!

什么是内网穿透? 首先,我们生活中的网络从应用上可以分为内网和外网; 内网就是你自己的网络环境,就你自己能访问,比如你本地测试进行的localhost; 外网就不言而喻了,你看网页,视频…

利用vite创建vue3工程

目录 什么是vite 优势: 简单理解: 1、创建工程 2、进入工程目录,安装依赖 3、启动​编辑 什么是vite 官方创建的前端构建工具 优势: 1开发环境中,无需打包操作,可快速冷启动 2轻量快速的热重载 3真…

Word文件加密的方法有哪些?两种方法告诉你

日常生活工作中,我们经常会使用到Word文档。有时里面有些比较重要的内容,我们不想别人随便可以更改我们输入的内容、窥探我们的隐私,我们该怎么做?建议给你的word文件加密,这样就能更好保护我们的信息。 操作环境&…

C语言论坛系统[2023-01-03]

C语言论坛系统[2023-01-03] 论坛系统设计 课程说明 需要提交的内容包括两个部分。 第一部分,对代码功能的讲解。 课设要求最后每个同学录制一个讲解视频,对着自己代码的功能进行讲解。 讲解时,主要涉及一个几个标准步骤: 步骤一…

【实操篇】Linux定时任务调度

目录 ●crond任务调度 简要介绍 基本语法 常用选项 参数细节说明 典型案例 应用实例 ●crond任务调度 简要介绍: 任务调度,它是指系统在某个特定时间去执行的特定命令或程序。它分为两类,第一类为系统工作(一些周…

自动驾驶数据集(一):KITTI数据集介绍

如有错误,恳请指出。 文章目录0. 数据集下载1. 标注数据label_22. 校准数据calib3. 点云数据velodyne4. 图像数据image_20. 数据集下载 KITTI数据集的下载地址:https://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark3d,下载…

redis集群简介

集群的概念 所谓的集群,就是通过添加服务器的数量,提供相同的服务,从而让服务器达到一个稳定、高效的状态。 1.1.1 使用redis集群的必要性 问题:我们已经部署好了redis,并且能启动一个redis,实现数据的读写…

鸿蒙 HDF 框架介绍

鸿蒙 HDF 框架介绍鸿蒙 HDF 框架介绍HDF 驱动框架框图HDF 驱动框架工作原理HDF 驱动框架工作原理框图:HDF 驱动加载过程分析HDF 驱动加载过程分析——驱动实现1HDF 驱动加载过程分析——驱动实现2HDF 驱动加载过程分析——获取驱动列表HDF 驱动加载过程分析——获取…

buu刷题记录

[ACTF新生赛2020]crypto-aes from Cryptodome.Cipher import AES import os import gmpy2 from flag import FLAG from Cryptodome.Util.number import *def main():keyos.urandom(2)*16ivos.urandom(16)print(bytes_to_long(key)^bytes_to_long(iv))aesAES.new(key,AES.MODE_…

第五章. 可视化数据分析图表—综合应用(双y轴,堆叠柱形图,颜色渐变饼形图,等高线图)

第五章. 可视化数据分析图 5.7 综合应用 1.双Y轴可视化数据分析图表的实现 (柱形图折线图) 双y轴,顾名思义就是两个y轴,可以通过双y轴看出发展情况的同时,还可以看到正常速度。 1).注意: add_subplot一定要…

【自学Python】Python2代码转Python3代码

Python2代码转Python3代码 Python2代码转Python3代码教程 由于 Python 存在 Python2 和 Python3 两个主要的版本方向,经常会有将 Python2 的代码转到 Python3 的环境下运行的需求。 尤其是跑一些神经网络的代码时有很多是在 Python2 的环境下写的。在 Python3 下…

EXCEL的查找:如何按 行号+列号 进行查询

0 首先用match()等取得行号,列号 如果想根据行号列号,精确查找,另外一个区域的数据,可以用如下方法 INDIRECT("Sheet2!r"&MATCH($C11,Sheet2!$A:$A,0)&"C"&MATCH(D$10,Sheet2!$1:$1,0),FALSE) …

使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus)

vite官网 一:初始化项目 1.需要在创建项目的位置cmd目录下执行 2. npm init vitelatest 回车 npm init vitelatest3.填上自己的项目名称 回车 4.选择vue 回车 5.选择TypeScript回车 6.项目创建完成 或者一步到位通过附加的命令行选项直接指定项目名称和你想要使用的…

网络流量监控为某图书馆系统排忧解难(一)

前言 某学校图书馆信息中心老师反应,用户反馈系统有访问慢的情况,需要通过流量分析系统来了解图书馆系统的运行情况,此报告专门针对图书馆系统的性能数据做了分析。 信息中心已部署NetInside流量分析系统,使用流量分析系统提供实…

PB数据库开发技术(七)-PowerBuilder小型数据库应用系统开发

PowerBuilder小型数据库应用系统开发 实验目的 利用前面学过的知识设计一个“图书馆管理系统”,从而进一步掌握powerbuilder数据库开发的基本步骤和方法。 二.实验步骤 建立数据库“图书管理系统”,向数据库中添加操作员表、借书还书表、图书表以及相应数据

Unity 项目中怎样正确的使用 Lua?

(图源siki学院-狸墨老师) 什么是Lua Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。可以方便的与c/c进行相互调用。但…

Redis:二、Redis常见命令

2. Redis常见命令 2.1 Redis数据结构介绍 Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样 Redis为了方便我们学习,将操作不同数据类型的命令也做了分组,在官网( http://www.redis.cn/…

【JAVA进阶】常用API

📃个人主页:个人主页 🔥系列专栏:JAVASE基础 目录 1.API概述 2.Object类 3.Objects 4.StringBuilder 5.日期与时间 Date 类 SimpleDateFormat Calendar 6.JDK8新增日期类 1.API概述 什么是API? API(Application Program…

Kettle(二)数据同步、迁移(基础版)

目录 1.配置源数据库A 1.1 文件-->数据库连接 1.2 配置数据库,选择自己的数据库并配置。 1.3 数据库配置可能会报错,原因是缺少数据库驱动 2.配置目标数据库(与源数据库一致) 3.数据迁移(举例) 3.…