Skip to content

Latest commit

 

History

History
86 lines (62 loc) · 4.39 KB

File metadata and controls

86 lines (62 loc) · 4.39 KB

Patching & gotchas

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.

1. The bridge-DLL path (handled — no patch needed)

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

2. Glob-safe window titles (you must follow this)

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

3. showing vs visible for progressive disclosure (handled)

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

4. Modal dialogs hang click() (use the mouse fallback)

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.

DPI note for synthesized clicks

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

5. Combo-box selection (KeyError: 'label')

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_mouse and click the item label with click_mouse (coordinate-based, less robust).

Patching pyjab directly (rarely needed)

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.pyget_java_window_hwnd / wait_java_window_by_title (title matching).
  • pyjab/common/win32utils.pyget_hwnds_by_title (the fnmatch call).
  • pyjab/jabelement.pyclick(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.