Skip to content

Window

Window wraps a top-level window and provides factory methods for creating Locator objects.


Window

Represents a single window (top-level or dialog).

Obtain via :meth:Application.window or :meth:Application.top_window.

Usage::

window = app.window(title_re=".*Notepad")
window.get_by_role("Edit").type_text("hello")
window.screenshot("snap.png")
Source code in src\dolphin_desktop\_window.py
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
class Window:
    """Represents a single window (top-level or dialog).

    Obtain via :meth:`Application.window` or :meth:`Application.top_window`.

    Usage::

        window = app.window(title_re=".*Notepad")
        window.get_by_role("Edit").type_text("hello")
        window.screenshot("snap.png")
    """

    def __init__(self, spec: Any) -> None:
        self._spec = spec

    # ------------------------------------------------------------------
    # Locator factories  (mirror Playwright's page.getBy* API)
    # ------------------------------------------------------------------

    def locator(self, **criteria: Any) -> Any:
        """Find elements by arbitrary pywinauto criteria (title, control_type, auto_id, …)."""
        if self._is_java_window():
            from ._java import JABLocator

            return JABLocator(
                self._java_hwnd(),
                control_type=criteria.get("control_type"),
                title=criteria.get("title"),
                title_re=criteria.get("title_re"),
            )
        return Locator(self, **criteria)

    def get_by_title(self, title: str, *, control_type: str | None = None) -> Any:
        """Find an element by its accessible name / window text."""
        if self._is_java_window():
            from ._java import JABLocator

            return JABLocator(self._java_hwnd(), control_type=control_type, title=title)
        criteria: dict[str, Any] = {"title": title}
        if control_type:
            criteria["control_type"] = control_type
        return Locator(self, **criteria)

    def get_by_role(self, role: str, *, name: str | None = None) -> Any:
        """Find an element by its UIA control type (e.g. 'Button', 'Edit', 'List')."""
        if self._is_java_window():
            from ._java import JABLocator

            return JABLocator(self._java_hwnd(), control_type=role, title=name)
        criteria: dict[str, Any] = {"control_type": role}
        if name:
            criteria["title"] = name
        return Locator(self, **criteria)

    def get_by_automation_id(self, automation_id: str) -> Locator:
        """Find an element by its UIA AutomationId."""
        return Locator(self, auto_id=automation_id)

    def get_by_class(self, class_name: str) -> Locator:
        """Find an element by its Win32 class name."""
        return Locator(self, class_name=class_name)

    def get_by_text(self, text: str) -> Any:
        """Alias for get_by_title — find by exact visible text."""
        if self._is_java_window():
            from ._java import JABLocator

            return JABLocator(self._java_hwnd(), title=text)
        return Locator(self, title=text)

    # ------------------------------------------------------------------
    # Specialized control factories (shorthand for common control types)
    # ------------------------------------------------------------------

    def button(self, name: str | None = None, **kw: Any) -> Button:
        """Find a Button control.

        Usage::

            window.button(name="OK").click()
            window.button(name="Apply").is_enabled()
        """
        criteria: dict[str, Any] = {"control_type": "Button", **kw}
        if name is not None:
            criteria["title"] = name
        return Button(self, **criteria)

    def edit(self, name: str | None = None, **kw: Any) -> Edit:
        """Find an Edit (text input) control.

        Usage::

            window.edit(name="Username").type_text("admin")
            window.edit().set_text("hello")
        """
        criteria: dict[str, Any] = {"control_type": "Edit", **kw}
        if name is not None:
            criteria["title"] = name
        return Edit(self, **criteria)

    def combo_box(self, name: str | None = None, **kw: Any) -> ComboBox:
        """Find a ComboBox (drop-down) control.

        Usage::

            window.combo_box(name="Language").select_item("English")
        """
        criteria: dict[str, Any] = {"control_type": "ComboBox", **kw}
        if name is not None:
            criteria["title"] = name
        return ComboBox(self, **criteria)

    def check_box(self, name: str | None = None, **kw: Any) -> CheckBox:
        """Find a CheckBox control.

        Usage::

            window.check_box(name="Remember me").check()
        """
        criteria: dict[str, Any] = {"control_type": "CheckBox", **kw}
        if name is not None:
            criteria["title"] = name
        return CheckBox(self, **criteria)

    def radio_button(self, name: str | None = None, **kw: Any) -> RadioButton:
        """Find a RadioButton control.

        Usage::

            window.radio_button(name="Option A").select()
        """
        criteria: dict[str, Any] = {"control_type": "RadioButton", **kw}
        if name is not None:
            criteria["title"] = name
        return RadioButton(self, **criteria)

    def menu(self, title: str | None = None, **kw: Any) -> Menu:
        """Find a top-level menu item (e.g. File, Edit, View).

        Usage::

            window.menu("File").item("Save").click()
            window.menu("Edit").item("Find...").click()
        """
        criteria: dict[str, Any] = {"control_type": "MenuItem", **kw}
        if title is not None:
            criteria["title"] = title
        return Menu(self, **criteria)

    def tree(self, name: str | None = None, **kw: Any) -> Tree:
        """Find a Tree (tree view) control.

        Usage::

            window.tree().expand_item("Root", "Branch")
            window.tree().select_item_by_path("Root", "Node")
        """
        criteria: dict[str, Any] = {"control_type": "Tree", **kw}
        if name is not None:
            criteria["title"] = name
        return Tree(self, **criteria)

    def list_box(self, name: str | None = None, **kw: Any) -> ListBox:
        """Find a ListBox control.

        Usage::

            window.list_box().select_item("Option A")
            names = window.list_box().items()
        """
        criteria: dict[str, Any] = {"control_type": "List", **kw}
        if name is not None:
            criteria["title"] = name
        return ListBox(self, **criteria)

    def tab(self, name: str | None = None, **kw: Any) -> Tab:
        """Find a Tab (notebook) control.

        Usage::

            window.tab().select_tab("General")
        """
        criteria: dict[str, Any] = {"control_type": "Tab", **kw}
        if name is not None:
            criteria["title"] = name
        return Tab(self, **criteria)

    def toolbar(self, name: str | None = None, **kw: Any) -> Toolbar:
        """Find a Toolbar control.

        Usage::

            window.toolbar().button("Bold").click()
        """
        criteria: dict[str, Any] = {"control_type": "ToolBar", **kw}
        if name is not None:
            criteria["title"] = name
        return Toolbar(self, **criteria)

    def element(self, alias: str) -> Any:
        """Find an element by alias from the Object Repository.

        Looks up *alias* in the children of the current window's alias first
        (if this window was created via ``app.window("alias")``), then falls
        back to a flat top-level search.

        Usage::

            objects.load("objects/login.yaml")
            win = app.window("login_window")
            win.element("email_input").type_text("user@example.com")
        """
        from .objects import _repository

        parent_alias: str | None = getattr(self, "_alias", None)
        if parent_alias:
            entry = _repository.resolve_child(parent_alias, alias)
        else:
            entry = _repository.resolve(alias)

        return Locator(self, fallback=entry.fallback or None, **entry.selector)

    def image(
        self,
        template: str | Path,
        *,
        confidence: float = 0.85,
        scales: list[float] | None = None,
    ) -> Any:
        """Find an element by template image matching, scoped to this window's bounds.

        Uses OpenCV template matching (requires ``dolphin-desktop[vision]``).

        Usage::

            window.image("btn_ok.png").click()
            window.image("icon.png", confidence=0.9).wait_for(timeout=5)
            window.image("logo.png", scales=[0.8, 1.0, 1.2]).exists()
        """
        from ._image import ImageLocator

        try:
            bb = self.bounding_box()
            region: tuple[int, int, int, int] | None = (
                bb["left"],
                bb["top"],
                bb["right"],
                bb["bottom"],
            )
        except Exception:
            region = None

        return ImageLocator(template, threshold=confidence, scales=scales, region=region)

    def find_by_xpath(self, xpath: str) -> Locator:
        """Find an element using a simplified XPath expression.

        Supports a subset of XPath syntax for navigating the UIA element tree:

        Usage::

            window.find_by_xpath("//Button[@Name='OK']").click()
            window.find_by_xpath("//Edit[@AutomationId='tbSearch']").type_text("q")
            window.find_by_xpath("//MenuBar//MenuItem[@Name='File']")

        Supported syntax:

        * ``//Tag`` or ``/Tag`` — find descendant with that control type
        * ``[@Name='val']``         → ``title='val'``
        * ``[@AutomationId='val']`` → ``auto_id='val'``
        * ``[@ClassName='val']``    → ``class_name='val'``

        Multiple segments chain locators::

            //MenuBar//MenuItem[@Name='File']
        """
        return _parse_xpath(self, xpath)

    # ------------------------------------------------------------------
    # Window actions
    # ------------------------------------------------------------------

    def close(self) -> None:
        self._spec.close()

    def maximize(self) -> None:
        self._spec.maximize()

    def minimize(self) -> None:
        self._spec.minimize()

    def restore(self) -> None:
        self._spec.restore()

    def focus(self) -> None:
        self._spec.set_focus()

    def move(self, x: int, y: int) -> None:
        rect = self._spec.rectangle()
        self._move_window(x, y, rect.right - rect.left, rect.bottom - rect.top)

    def resize(self, width: int, height: int) -> None:
        rect = self._spec.rectangle()
        self._move_window(rect.left, rect.top, width, height)

    def _move_window(self, x: int, y: int, width: int, height: int) -> None:
        """Move/resize the window via Win32 (works on both uia and win32 backends).

        pywinauto's ``move_window`` exists only on the win32 ``HwndWrapper``; the
        UIA wrapper lacks it, so we drive ``win32gui.MoveWindow`` by HWND directly.
        """
        import win32gui  # type: ignore[import-untyped]

        win32gui.MoveWindow(self._spec.handle, x, y, width, height, True)

    # ------------------------------------------------------------------
    # Queries
    # ------------------------------------------------------------------

    def title(self) -> str:
        return self._spec.window_text()

    def exists(self) -> bool:
        return bool(self._spec.exists())

    def is_visible(self) -> bool:
        return bool(self._spec.is_visible())

    def is_active(self) -> bool:
        return bool(self._spec.is_active())

    def bounding_box(self) -> dict[str, int]:
        rect = self._spec.rectangle()
        return {
            "left": rect.left,
            "top": rect.top,
            "right": rect.right,
            "bottom": rect.bottom,
            "width": rect.right - rect.left,
            "height": rect.bottom - rect.top,
        }

    # ------------------------------------------------------------------
    # Screenshot
    # ------------------------------------------------------------------

    def screenshot(self, path: str | Path | None = None) -> Image:
        """Capture the window as a PIL Image, optionally saving to *path*."""
        img = self._spec.capture_as_image()
        if path:
            img.save(path)
        return img

    # ------------------------------------------------------------------
    # Waiting
    # ------------------------------------------------------------------

    def wait_for_close(self, timeout: float = 10.0) -> None:
        """Wait until the window is no longer visible."""
        try:
            self._spec.wait_not("visible", timeout=timeout)
        except Exception as exc:
            raise WaitTimeoutError(f"Window did not close after {timeout}s") from exc

    def wait_until_ready(self, timeout: float = 10.0) -> Window:
        """Wait until the window is ready (not busy)."""
        try:
            self._spec.wait("ready", timeout=timeout)
        except Exception as exc:
            raise WaitTimeoutError(f"Window not ready after {timeout}s") from exc
        return self

    # ------------------------------------------------------------------
    # Internal
    # ------------------------------------------------------------------

    def _is_java_window(self) -> bool:
        try:
            import win32gui  # type: ignore[import-untyped]

            return win32gui.GetClassName(self._java_hwnd()) == "SunAwtFrame"
        except Exception:
            return False

    def _java_hwnd(self) -> int:
        return self._spec.wrapper_object().handle

    def _get_spec(self) -> Any:
        return self._spec

    def __repr__(self) -> str:
        return "Window()"

locator

locator(**criteria: Any) -> Any

Find elements by arbitrary pywinauto criteria (title, control_type, auto_id, …).

Source code in src\dolphin_desktop\_window.py
def locator(self, **criteria: Any) -> Any:
    """Find elements by arbitrary pywinauto criteria (title, control_type, auto_id, …)."""
    if self._is_java_window():
        from ._java import JABLocator

        return JABLocator(
            self._java_hwnd(),
            control_type=criteria.get("control_type"),
            title=criteria.get("title"),
            title_re=criteria.get("title_re"),
        )
    return Locator(self, **criteria)

get_by_title

get_by_title(
    title: str, *, control_type: str | None = None
) -> Any

Find an element by its accessible name / window text.

Source code in src\dolphin_desktop\_window.py
def get_by_title(self, title: str, *, control_type: str | None = None) -> Any:
    """Find an element by its accessible name / window text."""
    if self._is_java_window():
        from ._java import JABLocator

        return JABLocator(self._java_hwnd(), control_type=control_type, title=title)
    criteria: dict[str, Any] = {"title": title}
    if control_type:
        criteria["control_type"] = control_type
    return Locator(self, **criteria)

get_by_role

get_by_role(role: str, *, name: str | None = None) -> Any

Find an element by its UIA control type (e.g. 'Button', 'Edit', 'List').

Source code in src\dolphin_desktop\_window.py
def get_by_role(self, role: str, *, name: str | None = None) -> Any:
    """Find an element by its UIA control type (e.g. 'Button', 'Edit', 'List')."""
    if self._is_java_window():
        from ._java import JABLocator

        return JABLocator(self._java_hwnd(), control_type=role, title=name)
    criteria: dict[str, Any] = {"control_type": role}
    if name:
        criteria["title"] = name
    return Locator(self, **criteria)

get_by_automation_id

get_by_automation_id(automation_id: str) -> Locator

Find an element by its UIA AutomationId.

Source code in src\dolphin_desktop\_window.py
def get_by_automation_id(self, automation_id: str) -> Locator:
    """Find an element by its UIA AutomationId."""
    return Locator(self, auto_id=automation_id)

get_by_class

get_by_class(class_name: str) -> Locator

Find an element by its Win32 class name.

Source code in src\dolphin_desktop\_window.py
def get_by_class(self, class_name: str) -> Locator:
    """Find an element by its Win32 class name."""
    return Locator(self, class_name=class_name)

get_by_text

get_by_text(text: str) -> Any

Alias for get_by_title — find by exact visible text.

Source code in src\dolphin_desktop\_window.py
def get_by_text(self, text: str) -> Any:
    """Alias for get_by_title — find by exact visible text."""
    if self._is_java_window():
        from ._java import JABLocator

        return JABLocator(self._java_hwnd(), title=text)
    return Locator(self, title=text)

button

button(name: str | None = None, **kw: Any) -> Button

Find a Button control.

Usage::

window.button(name="OK").click()
window.button(name="Apply").is_enabled()
Source code in src\dolphin_desktop\_window.py
def button(self, name: str | None = None, **kw: Any) -> Button:
    """Find a Button control.

    Usage::

        window.button(name="OK").click()
        window.button(name="Apply").is_enabled()
    """
    criteria: dict[str, Any] = {"control_type": "Button", **kw}
    if name is not None:
        criteria["title"] = name
    return Button(self, **criteria)

edit

edit(name: str | None = None, **kw: Any) -> Edit

Find an Edit (text input) control.

Usage::

window.edit(name="Username").type_text("admin")
window.edit().set_text("hello")
Source code in src\dolphin_desktop\_window.py
def edit(self, name: str | None = None, **kw: Any) -> Edit:
    """Find an Edit (text input) control.

    Usage::

        window.edit(name="Username").type_text("admin")
        window.edit().set_text("hello")
    """
    criteria: dict[str, Any] = {"control_type": "Edit", **kw}
    if name is not None:
        criteria["title"] = name
    return Edit(self, **criteria)

combo_box

combo_box(name: str | None = None, **kw: Any) -> ComboBox

Find a ComboBox (drop-down) control.

Usage::

window.combo_box(name="Language").select_item("English")
Source code in src\dolphin_desktop\_window.py
def combo_box(self, name: str | None = None, **kw: Any) -> ComboBox:
    """Find a ComboBox (drop-down) control.

    Usage::

        window.combo_box(name="Language").select_item("English")
    """
    criteria: dict[str, Any] = {"control_type": "ComboBox", **kw}
    if name is not None:
        criteria["title"] = name
    return ComboBox(self, **criteria)

check_box

check_box(name: str | None = None, **kw: Any) -> CheckBox

Find a CheckBox control.

Usage::

window.check_box(name="Remember me").check()
Source code in src\dolphin_desktop\_window.py
def check_box(self, name: str | None = None, **kw: Any) -> CheckBox:
    """Find a CheckBox control.

    Usage::

        window.check_box(name="Remember me").check()
    """
    criteria: dict[str, Any] = {"control_type": "CheckBox", **kw}
    if name is not None:
        criteria["title"] = name
    return CheckBox(self, **criteria)

radio_button

radio_button(
    name: str | None = None, **kw: Any
) -> RadioButton

Find a RadioButton control.

Usage::

window.radio_button(name="Option A").select()
Source code in src\dolphin_desktop\_window.py
def radio_button(self, name: str | None = None, **kw: Any) -> RadioButton:
    """Find a RadioButton control.

    Usage::

        window.radio_button(name="Option A").select()
    """
    criteria: dict[str, Any] = {"control_type": "RadioButton", **kw}
    if name is not None:
        criteria["title"] = name
    return RadioButton(self, **criteria)

menu

menu(title: str | None = None, **kw: Any) -> Menu

Find a top-level menu item (e.g. File, Edit, View).

Usage::

window.menu("File").item("Save").click()
window.menu("Edit").item("Find...").click()
Source code in src\dolphin_desktop\_window.py
def menu(self, title: str | None = None, **kw: Any) -> Menu:
    """Find a top-level menu item (e.g. File, Edit, View).

    Usage::

        window.menu("File").item("Save").click()
        window.menu("Edit").item("Find...").click()
    """
    criteria: dict[str, Any] = {"control_type": "MenuItem", **kw}
    if title is not None:
        criteria["title"] = title
    return Menu(self, **criteria)

tree

tree(name: str | None = None, **kw: Any) -> Tree

Find a Tree (tree view) control.

Usage::

window.tree().expand_item("Root", "Branch")
window.tree().select_item_by_path("Root", "Node")
Source code in src\dolphin_desktop\_window.py
def tree(self, name: str | None = None, **kw: Any) -> Tree:
    """Find a Tree (tree view) control.

    Usage::

        window.tree().expand_item("Root", "Branch")
        window.tree().select_item_by_path("Root", "Node")
    """
    criteria: dict[str, Any] = {"control_type": "Tree", **kw}
    if name is not None:
        criteria["title"] = name
    return Tree(self, **criteria)

list_box

list_box(name: str | None = None, **kw: Any) -> ListBox

Find a ListBox control.

Usage::

window.list_box().select_item("Option A")
names = window.list_box().items()
Source code in src\dolphin_desktop\_window.py
def list_box(self, name: str | None = None, **kw: Any) -> ListBox:
    """Find a ListBox control.

    Usage::

        window.list_box().select_item("Option A")
        names = window.list_box().items()
    """
    criteria: dict[str, Any] = {"control_type": "List", **kw}
    if name is not None:
        criteria["title"] = name
    return ListBox(self, **criteria)

tab

tab(name: str | None = None, **kw: Any) -> Tab

Find a Tab (notebook) control.

Usage::

window.tab().select_tab("General")
Source code in src\dolphin_desktop\_window.py
def tab(self, name: str | None = None, **kw: Any) -> Tab:
    """Find a Tab (notebook) control.

    Usage::

        window.tab().select_tab("General")
    """
    criteria: dict[str, Any] = {"control_type": "Tab", **kw}
    if name is not None:
        criteria["title"] = name
    return Tab(self, **criteria)

toolbar

toolbar(name: str | None = None, **kw: Any) -> Toolbar

Find a Toolbar control.

Usage::

window.toolbar().button("Bold").click()
Source code in src\dolphin_desktop\_window.py
def toolbar(self, name: str | None = None, **kw: Any) -> Toolbar:
    """Find a Toolbar control.

    Usage::

        window.toolbar().button("Bold").click()
    """
    criteria: dict[str, Any] = {"control_type": "ToolBar", **kw}
    if name is not None:
        criteria["title"] = name
    return Toolbar(self, **criteria)

element

element(alias: str) -> Any

Find an element by alias from the Object Repository.

Looks up alias in the children of the current window's alias first (if this window was created via app.window("alias")), then falls back to a flat top-level search.

Usage::

objects.load("objects/login.yaml")
win = app.window("login_window")
win.element("email_input").type_text("user@example.com")
Source code in src\dolphin_desktop\_window.py
def element(self, alias: str) -> Any:
    """Find an element by alias from the Object Repository.

    Looks up *alias* in the children of the current window's alias first
    (if this window was created via ``app.window("alias")``), then falls
    back to a flat top-level search.

    Usage::

        objects.load("objects/login.yaml")
        win = app.window("login_window")
        win.element("email_input").type_text("user@example.com")
    """
    from .objects import _repository

    parent_alias: str | None = getattr(self, "_alias", None)
    if parent_alias:
        entry = _repository.resolve_child(parent_alias, alias)
    else:
        entry = _repository.resolve(alias)

    return Locator(self, fallback=entry.fallback or None, **entry.selector)

image

image(
    template: str | Path,
    *,
    confidence: float = 0.85,
    scales: list[float] | None = None,
) -> Any

Find an element by template image matching, scoped to this window's bounds.

Uses OpenCV template matching (requires dolphin-desktop[vision]).

Usage::

window.image("btn_ok.png").click()
window.image("icon.png", confidence=0.9).wait_for(timeout=5)
window.image("logo.png", scales=[0.8, 1.0, 1.2]).exists()
Source code in src\dolphin_desktop\_window.py
def image(
    self,
    template: str | Path,
    *,
    confidence: float = 0.85,
    scales: list[float] | None = None,
) -> Any:
    """Find an element by template image matching, scoped to this window's bounds.

    Uses OpenCV template matching (requires ``dolphin-desktop[vision]``).

    Usage::

        window.image("btn_ok.png").click()
        window.image("icon.png", confidence=0.9).wait_for(timeout=5)
        window.image("logo.png", scales=[0.8, 1.0, 1.2]).exists()
    """
    from ._image import ImageLocator

    try:
        bb = self.bounding_box()
        region: tuple[int, int, int, int] | None = (
            bb["left"],
            bb["top"],
            bb["right"],
            bb["bottom"],
        )
    except Exception:
        region = None

    return ImageLocator(template, threshold=confidence, scales=scales, region=region)

find_by_xpath

find_by_xpath(xpath: str) -> Locator

Find an element using a simplified XPath expression.

Supports a subset of XPath syntax for navigating the UIA element tree:

Usage::

window.find_by_xpath("//Button[@Name='OK']").click()
window.find_by_xpath("//Edit[@AutomationId='tbSearch']").type_text("q")
window.find_by_xpath("//MenuBar//MenuItem[@Name='File']")

Supported syntax:

  • //Tag or /Tag — find descendant with that control type
  • [@Name='val']title='val'
  • [@AutomationId='val']auto_id='val'
  • [@ClassName='val']class_name='val'

Multiple segments chain locators::

//MenuBar//MenuItem[@Name='File']
Source code in src\dolphin_desktop\_window.py
def find_by_xpath(self, xpath: str) -> Locator:
    """Find an element using a simplified XPath expression.

    Supports a subset of XPath syntax for navigating the UIA element tree:

    Usage::

        window.find_by_xpath("//Button[@Name='OK']").click()
        window.find_by_xpath("//Edit[@AutomationId='tbSearch']").type_text("q")
        window.find_by_xpath("//MenuBar//MenuItem[@Name='File']")

    Supported syntax:

    * ``//Tag`` or ``/Tag`` — find descendant with that control type
    * ``[@Name='val']``         → ``title='val'``
    * ``[@AutomationId='val']`` → ``auto_id='val'``
    * ``[@ClassName='val']``    → ``class_name='val'``

    Multiple segments chain locators::

        //MenuBar//MenuItem[@Name='File']
    """
    return _parse_xpath(self, xpath)

screenshot

screenshot(path: str | Path | None = None) -> Image

Capture the window as a PIL Image, optionally saving to path.

Source code in src\dolphin_desktop\_window.py
def screenshot(self, path: str | Path | None = None) -> Image:
    """Capture the window as a PIL Image, optionally saving to *path*."""
    img = self._spec.capture_as_image()
    if path:
        img.save(path)
    return img

wait_for_close

wait_for_close(timeout: float = 10.0) -> None

Wait until the window is no longer visible.

Source code in src\dolphin_desktop\_window.py
def wait_for_close(self, timeout: float = 10.0) -> None:
    """Wait until the window is no longer visible."""
    try:
        self._spec.wait_not("visible", timeout=timeout)
    except Exception as exc:
        raise WaitTimeoutError(f"Window did not close after {timeout}s") from exc

wait_until_ready

wait_until_ready(timeout: float = 10.0) -> Window

Wait until the window is ready (not busy).

Source code in src\dolphin_desktop\_window.py
def wait_until_ready(self, timeout: float = 10.0) -> Window:
    """Wait until the window is ready (not busy)."""
    try:
        self._spec.wait("ready", timeout=timeout)
    except Exception as exc:
        raise WaitTimeoutError(f"Window not ready after {timeout}s") from exc
    return self

Locator factory methods

# By UIA control type (role)
win.get_by_role("Button")
win.get_by_role("Edit", name="Username")

# By visible text / label
win.get_by_title("Save")
win.get_by_text("Submit")        # alias for get_by_title

# By AutomationId (most stable)
win.get_by_automation_id("btnSave")

# By Win32 class name
win.get_by_class("TButton")

# Arbitrary pywinauto criteria
win.locator(control_type="Button", title="OK")
win.locator(title_re=".*Save.*")
win.locator(auto_id="btnSave", found_index=0)

Window actions

win.maximize()
win.minimize()
win.restore()
win.close()
win.move(x=100, y=100)
win.resize(width=800, height=600)
win.focus()
win.wait_for_close(timeout=10)

Screenshots

img = win.screenshot()                  # PIL.Image
img = win.screenshot("output.png")      # save to file