python自动化控制程序(Python自动化控制javaswing)
python自动化控制程序(Python自动化控制javaswing)https://github.com/gaozhao1989/pyjabpip install pyjab也可以访问pyjab github源码https://docs.oracle.com/javase/accessBridge/2.0.2/setup.htm解压下载的jab压缩文件,并将文件复制到相应的文件夹中(此处是重点,很多网址中没有这一步)。java7之后就已经集成了JAB,如果安装的目录中含有对应需要安装的文件,可以不用覆盖。当jab安装好了以后,随意打开一个java swing程序。然后打开JavaMonkey-64.exe(如果安装的是64位java,打开javaMonkey.exe没有效果),会出现当前窗口所有控件的属性结构图,表明jab安装成功。
java自身提供了swing程序化控制接口,即Java Access Bridge(jab)。百度上有很多相关的资料,但是总是存在一些问题导致无法控制。
一. 安装jabjab下载地址
https://www.Oracle.com/java/technologies/javase-jab-2-0-2-downloads.html
Oracle官方jab安装教程(推荐直接阅读官方安装教程)
https://docs.oracle.com/javase/accessBridge/2.0.2/setup.htm
解压下载的jab压缩文件,并将文件复制到相应的文件夹中(此处是重点,很多网址中没有这一步)。java7之后就已经集成了JAB,如果安装的目录中含有对应需要安装的文件,可以不用覆盖。
当jab安装好了以后,随意打开一个java swing程序。然后打开JavaMonkey-64.exe(如果安装的是64位java,打开javaMonkey.exe没有效果),会出现当前窗口所有控件的属性结构图,表明jab安装成功。
pip install pyjab
也可以访问pyjab github源码
https://github.com/gaozhao1989/pyjab
于是在python中也可以像selenium操控网页一样操控java swing了。由于pyjab项目处于新生状态,可能存在部分bug。
很感谢pyjab的作者分享,这样我们就不用重复造轮子也不用去面对底层api。
三. 后记- 根据pyjab官方示例,JABDriver需要输入title。但是有些java swing仅有class,title为空。
class JABDriver(Service ActorScheduler):
"""Controls a Java application by Java Access Bridge.
Args:
Service ([type]): Host system to initialize the JAB and load JAB dll file.
ActorScheduler ([type]): Set message pump to interacte with windows system.
"""
def __init__(
self
title: str = ""
bridge_dll: str = ""
hwnd: HWND = None
vmid: c_long = None
accessible_context: JOBJECT64 = None
timeout: int = TIMEOUT
) -> None:
于是可以使用pywin32获取到窗口的句柄,然后传入JABDriver中。但是此时会报错
FileNotFoundError: WindowsAccessBridge dll not found please set correct path for environment variable or check the passed customized WindowsAccessBridge dll.
需要将WindowsAccessBridge的绝对路径传进去,例如:
handle = win32gui.FindWindow('SunAwtDialog' None)
# Create a JABDriver object.
jabdriver = JABDriver(bridge_dll=r'C:\Windows\SysWOW64\WindowsAccessBridge-32.dll' hwnd=handle)
- 虽然pyjab支持xpath语法,但是很多语法支持程度不够。目前已知的支持的元素类型如下
"""
The By implementation.
"""
class By(object):
"""
Set of supported locator strategies.
"""
XPATH = "xpath"
NAME = "name"
ROLE = "role"
STATES = "states"
OBJECT_DEPTH = "object_depth"
CHILDREN_COUNT = "children_count"
INDEX_IN_PARENT = "index_in_parent"
- 推荐一款java swing元素定位工具access-bridge-explorer
https://github.com/google/access-bridge-explorer
- 若元素采用win32接口方式点击 simulate=True
此处源码中并没有计算屏幕缩放,应在点击前(DOWN动作之前),计算屏幕缩放比例。
def click(self simulate: bool = False) -> None:
"""Simulates clicking to JABElement.
Default will use JAB Accessible Action.
Set parameter 'simulate' to True if internal action does not work.
Args:
simulate (bool optional): Simulate user click action by mouse event. Defaults to False.
Raises:
ValueError: Raise ValueError when JABElement width or height is 0.
Use this to send simple mouse events or to click form fields::
form_button = driver.find_element_by_name('button')
form_button.click()
form_button.click(simulate=True)
"""
if simulate:
self.win32_utils._set_window_foreground(hwnd=self.hwnd.value)
self.set_element_information()
x = self.bounds.get("x")
y = self.bounds.get("y")
width = self.bounds.get("width")
height = self.bounds.get("height")
if width == 0 or height == 0:
raise ValueError("element width or height is 0")
position_x = round(x width / 2)
position_y = round(y height / 2)
self.win32_utils._click_mouse(x=position_x y=position_y)
else:
self._do_accessible_action(action="click")
def _click_mouse(self x: int y: int hold: int = 0) -> None:
win32api.SetCursorPos((x y))
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN x y 0 0)
if hold:
time.sleep(hold)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP x y 0 0)
计算屏幕缩放比例可参考如下,从百度中复制,并未验证
from win32.lib import win32con
import win32api win32gui win32print
def get_real_resolution():
"""获取真实的分辨率"""
hDC = win32gui.GetDC(0)
wide = win32print.GetDeviceCaps(hDC win32con.DESKTOPHORZRES)
high = win32print.GetDeviceCaps(hDC win32con.DESKTOPVERTRES)
return {"wide": wide "high": high}
def get_screen_size():
"""获取缩放后的分辨率"""
wide = win32api.GetSystemMetrics(0)
high = win32api.GetSystemMetrics(1)
return {"wide": wide "high": high}
def get_scaling():
'''获取屏幕的缩放比例'''
real_resolution = get_real_resolution()
screen_size = get_screen_size()
proportion = round(real_resolution['wide'] / screen_size['wide'] 2)
return proportion