pyjab is the only way to talk to the Java Access Bridge from Python, but raw pyjab has sharp
edges. This library already works around them; this page explains each one so you know why the
code is shaped the way it is — and how to patch pyjab yourself if you ever need to.
Problem. pyjab.JABDriver(title) defaults to loading WindowsAccessBridge-<bits>.dll from
%JAVA_HOME%\jre\bin. Modern JDKs (9+) have no jre\bin, so it fails to find the DLL.
Fix (in swing_tester). find_bridge_dll() searches System32 / SysWOW64 and
%JAVA_HOME%\bin, and every JABDriver is constructed with an explicit bridge_dll= path. pyjab
loads it with ctypes, so the JDK bin copy works without an admin System32 deployment.
You do not need to copy the DLL into C:\Windows\System32. (Deploying it there is only required
for UI Automation-based assistive tech, which is not what this uses.)
Problem. pyjab finds windows with fnmatch.fnmatch(window_title, requested_title) — i.e. the
title you pass is treated as a glob pattern. A title containing [, ], *, or ? is
interpreted as a glob metacharacter and never matches. Symptom:
no java window found by title 'App [James Smith]' in 15 seconds.
Fix. Keep Java frame titles plain text. Use App - James Smith, not App [James Smith].
If you own the app, change the JFrame/JDialog titles. If you don't, pass a glob that matches
(escaping is awkward — renaming the title is far simpler).
Problem. When you hide a panel with setVisible(false), its children keep their own
isVisible() flag true — only isShowing() (self and all ancestors on screen) goes false.
JAB exposes both as states. Asserting on the visible state gives false positives.
Fix. JabActor.has_visible(name) checks the JAB showing state. Use it for reveal/hide
assertions. (JabNode.visible is visible || showing and is only used for the human-readable
[hidden] flag in compact().)
Problem. A modal dialog's open action calls setVisible(true), which enters a nested event
loop that does not return until the dialog closes. pyjab's programmatic click
(doAccessibleActions) waits for the action to return — so JabActor.click() on the button that
opens a modal hangs forever.
Fix. JabActor.click_mouse(name) calls pyjab's click(simulate=True), which foregrounds the
window and synthesizes a real OS mouse click at the element's centre — it does not wait on the
action thread, so it drives modal dialogs (both opening them and clicking controls inside them).
Reading the tree is unaffected by modality.
click_mouse uses screen coordinates from the JAB bounds. On scaled displays (125/150/200%), make
the driving Python process DPI-aware before clicking, or the coordinates miss:
import ctypes
try:
ctypes.windll.shcore.SetProcessDpiAwareness(2) # per-monitor v2
except Exception:
ctypes.windll.user32.SetProcessDPIAware()Problem. pyjab.JABElement.select(option) only understands a few item roles. Swing JComboBox
renders its popup items as label role, so select() raises KeyError: 'label'.
Workarounds.
- Prefer radio buttons / checkboxes / push buttons as the triggers for cascading UI — those
drive cleanly via
click(). - If you must change a combo, rely on the app's default selection, or open the popup with
click_mouseand click the item label withclick_mouse(coordinate-based, less robust).
Everything above is handled in swing_tester without editing pyjab. If you ever need to patch
pyjab itself (e.g. a new version regresses), the relevant spots are:
pyjab/jabdriver.py→get_java_window_hwnd/wait_java_window_by_title(title matching).pyjab/common/win32utils.py→get_hwnds_by_title(thefnmatchcall).pyjab/jabelement.py→click(simulate=...),select(...),send_text(...),bounds.
Prefer wrapping over editing site-packages. If you do patch, pin the pyjab version in
requirements.txt and document the patch here.