Python调用外部EXE程序遍历窗体及控件并获取控件信息。

背景

  我的工作中经常手工运行一个windows程序(密码生成工具),获取该程序的计算结果,手工填到登录表单的中。该程序非常久远,已无人维护。根据凡是重复2次以上的工作都应该自动化原则,那么我来写个自动化脚本,减轻手工工作。
  关键点是如何运行外部exe、获取句柄及一些操作。
  
  ***喜欢此文档的朋友,请点个赞吧!***

步骤

1. 启动应用程序,采用非阻塞式启动应用程序。 `subprocess.Popen([name])`
2. 关闭应用程序       `os.system(r'taskkill /F /IM {}'.format(name))`
3. 遍历窗体。    `hwnd = win32gui.FindWindow(None, maintitlename)  # 获取主窗口句柄`
4. 遍历控件        `win32gui.EnumChildWindows(hwnd, lambda hwnd, param: param.append(hwnd), hwnd_child_list)`
5. 触发按钮。    `win32gui.SendMessage(btngenerate, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, 0)
                                win32gui.SendMessage(btngenerate, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, 0)`
6. 获取文本框内容。  `win32gui.SendMessage(edit_hwnd, win32con.WM_GETTEXT, len_text, buffer)  # 发送消息,获取控件内容`

环境

1.程序使用到了Spy++ 工具。

代码

# _*_ encoding:utf-8 _*_
import time
import logging
import win32gui
import win32con
import os
import subprocess
import psutil
from timeloop import Timeloop
from datetime import timedelta

# 创建记录器logger
logger = logging.getLogger('DevicePassword')
logger.setLevel(logging.DEBUG)
# 创建文件处理器FileHandler
ch_file = logging.FileHandler('logger.log', encoding='utf-8')  #  输出到文件
ch_file.setLevel(logging.INFO)
ch_Terminal = logging.StreamHandler()  # 输出到屏幕
ch_Terminal.setLevel(logging.INFO)

# 创建格式器Formatter
formater = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 配置logger
ch_file.setFormatter(formater)
ch_Terminal.setFormatter(formater)
logger.addHandler(ch_file)
logger.addHandler(ch_Terminal)

appname = 'app.exe'  #  你的程序文件名称
titlename = 'PasswordGen'   # 程序主窗口的标题名称,如果不知道,可通过Spy++获取。


def startapp(name):
    """
    启动外部EXE程序
    :param name: 程序名称
    :return: None
    """
    if exe_is_active(name):  # 若外部程序已存在,则终止外部程序。
        os.system(r'taskkill /F /IM {}'.format(name))
        time.sleep(2)

    if os.path.exists(name):  # 非阻塞启动外部EXE程序。
        subprocess.Popen([name])
    else:
        print("软件不存在,请将{}复制到程序相同目录下。".format(appname))


def closeapp(name):
    """
    关闭外部EXE程序
    :param name:
    :return:
    """
    if exe_is_active(name):  # 若外部程序已存在,则终止外部程序。
        os.system(r'taskkill /F /IM {}'.format(name))
        time.sleep(2)


def exe_is_active(name):
    """
    判断外部EXE程序是否已经启动
    :param name: 外部程序名
    :return: TRUE  or  FALSE
    """
    processes_name = []
    pids = psutil.pids()
    for pid in pids:
        p = psutil.Process(pid)
        processes_name.append(p.name())
    if name in processes_name:
        print('{} 已存在'.format(name))
        return True
    else:
        print('{} 不存在'.format(name))
        return False


def click_generate(btngenerate):
    """
    点击Generate按钮,生成密码
    :param btngenerate: 按钮句柄
    :return:None
    """
    win32gui.SendMessage(btngenerate, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, 0)
    win32gui.SendMessage(btngenerate, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, 0)


def find_control(maintitlename):
    hwnd = win32gui.FindWindow(None, maintitlename)  # 获取主窗口句柄
    hwnd_child_list = []   #  子控件数组,存储子控件
    if hwnd > 0:
        win32gui.EnumChildWindows(hwnd, lambda hwnd, param: param.append(hwnd), hwnd_child_list)
    return hwnd_child_list  #  返回子控件数组


def get_edit(edit_hwnd):
    """
    获取密码控件内容
    :param edit_hwnd: 密码控件句柄
    :return: 控件内容text
    """
    len_text = win32gui.SendMessage(edit_hwnd, win32con.WM_GETTEXTLENGTH) + 1  # 获取文本内容长度
    buffer = win32gui.PyMakeBuffer(len_text)  # 初始化缓存
    win32gui.SendMessage(edit_hwnd, win32con.WM_GETTEXT, len_text, buffer)  # 发送消息,获取控件内容
    address, length = win32gui.PyGetBufferAddressAndLen(buffer)  # 转换缓存内容
    text = win32gui.PyGetString(address, length - 1)
    return text


def sync_cb_device_password(pwd):   # 将获取的密码更新到密码表中,供设备使用。这样就不用我每次手动更新了。
    sql = 'UPDATE device set DeviceUserName= %s , DevicePassword = %s'
    params = ('admin', pwd)
    with DBMysql(log_time=False) as db:  # DBMysql自定义数据库类,执行SQL。
        db.cursor.execute(sql, params)
        data = db.cursor.rowcount
        print('同步设备密码-sync_device_password:{},更改记录数量:{}'.format(pwd, data))
        if not data or data is None or data == 0 or data > 1:
            isset = False
        else:
            isset = True
    return isset


def main():
    startapp(appname)
    time.sleep(5)
    # 获取窗体控件
    handle = find_control(titlename)
    # 点击按钮,生成密码
    click_generate(handle[5])   #  handle[5] 是按钮的句柄,通过Spy++获取。  这里应该可以通过控件名称获取,以后优化。
    # 获取密码内容
    password = get_edit(handle[4])  #  handle[4] 是按钮的句柄,通过Spy++获取。  这里应该可以通过控件名称或类名获取,以后优化。
    print("密码:{}".format(password))

    time.sleep(3)
    sync_cb_device_password(password)
    closeapp(appname)
    logger.info('今天的密码是:{}'.format(password))

### 回答1: 在Python中,可以使用Tkinter库来创建窗体控件,并使用相应的方法获取窗体中的所有控件。以下是一个示例代码: ```python import tkinter as tk def get_all_widgets(widget): # 获取窗体中的所有控件 widget_list = [widget] for child in widget.winfo_children(): widget_list.extend(get_all_widgets(child)) return widget_list # 创建一个窗体 window = tk.Tk() # 添加控件窗体 label = tk.Label(window, text="Hello World!") button = tk.Button(window, text="Click Me!") # 打印窗体中的所有控件 widget_list = get_all_widgets(window) for widget in widget_list: print(widget) # 运行窗体的消息循环 window.mainloop() ``` 以上代码中,首先导入了Tkinter库,并定义了一个`get_all_widgets`函数,该函数递归地获取窗体中的所有控件。然后创建了一个窗体对象`window`,并在窗体中添加了一个`label`标签和一个`button`按钮。最后,通过调用`get_all_widgets`函数并遍历打印出窗体中的所有控件。 你可以根据实际需求对控件进行相应的处理,例如对按钮添加事件处理等。 ### 回答2: 要获取窗体中的所有控件,我们可以使用Python中的第三方库PyQt或Tkinter。以PyQt为例,我们可以使用递归方法实现该功能。 首先,我们需要导入PyQt库中的所有控件模块。然后,创建一个递归函数,该函数接受一个控件作为参数。在函数内部,我们通过判断当前控件是否有子控件,如果有,则递归调用该函数来获取控件。同时,我们可以将控件的属性和信息保存到列表或字典中,以便后续使用。 下面是一个使用PyQt获取窗体所有控件的示例代码: ```python from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QVBoxLayout from PyQt5 import sip def get_all_widgets(container): for widget in container.children(): if isinstance(widget, QWidget): # 在这里可以保存控件的属性和信息 print(widget.objectName(), type(widget)) get_all_widgets(widget) else: # 在这里可以保存控件的属性和信息 print(widget.objectName(), type(widget)) if __name__ == '__main__': app = QApplication([]) window = QWidget() layout = QVBoxLayout() button = QPushButton('Button') label = QLabel('Label') layout.addWidget(button) layout.addWidget(label) window.setLayout(layout) get_all_widgets(window) sip.delete(window) window = None app.exec_() ``` 在以上示例代码中,我们首先创建了一个QWidget窗体,并添加了两个控件,一个QPushButton按钮和一个QLabel标签。然后,我们调用get_all_widgets函数并传入容器window,函数将通过递归获取窗体中的所有控件,并打印控件的对象名称和类型。最后,我们释放内存资源。 通过运行以上示例代码,我们可以在控制台看到输出窗体中所有控件的对象名称和类型。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值