"""
uiautomation for Python 3.
Author: yinkaisheng
Source: https://github.com/yinkaisheng/Python-UIAutomation-for-Windows
This module is for UIAutomation on Windows(Windows XP with SP3, Windows Vista and Windows 7/8/8.1/10).
It supports UIAutomation for the applications which implmented IUIAutomation, such as MFC, Windows Form, WPF, Modern UI(Metro UI), Qt, Firefox and Chrome.
Run 'automation.py -h' for help.
uiautomation is shared under the Apache Licene 2.0.
This means that the code can be freely copied and distributed, and costs nothing to use.
"""
from __future__ import annotations
import os
import sys
import time
import datetime
import re
import threading
import ctypes
import ctypes.wintypes
import comtypes
from typing import Any, Callable, Dict, Generator, List, Tuple
from .enums import *
from .core import *
from .core import _AutomationClient
from .patterns import *
METRO_WINDOW_CLASS_NAME = "Windows.UI.Core.CoreWindow" # for Windows 8 and 8.1
SEARCH_INTERVAL = 0.5 # search control interval seconds
MAX_MOVE_SECOND = 1 # simulate mouse move or drag max seconds
TIME_OUT_SECOND = 10
OPERATION_WAIT_TIME = 0.5
MAX_PATH = 260
DEBUG_SEARCH_TIME = False
DEBUG_EXIST_DISAPPEAR = False
S_OK = 0
IsPy38OrHigher = sys.version_info[:2] >= (3, 8)
IsNT6orHigher = os.sys.getwindowsversion().major >= 6
CurrentProcessIs64Bit = sys.maxsize > 0xFFFFFFFF
ProcessTime = time.perf_counter # this returns nearly 0 when first call it if python version <= 3.6
ProcessTime() # need to call it once if python version <= 3.6
TreeNode = Any
class Control:
ValidKeys = set(
[
"ControlType",
"ClassName",
"AutomationId",
"Name",
"SubName",
"RegexName",
"Depth",
"Compare",
]
)
def __init__(
self,
searchFromControl: "Control" | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
ControlType: int | None = None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
"""
searchFromControl: `Control` or its subclass, if it is None, search from root control(Desktop).
searchDepth: int, max search depth from searchFromControl.
foundIndex: int, starts with 1, >= 1.
searchInterval: float, wait searchInterval after every search in self.Refind and self.Exists, the global timeout is TIME_OUT_SECOND.
element: `ctypes.POINTER(IUIAutomationElement)`, internal use only.
ControlType: int, a value in class `ControlType`.
Name: str.
SubName: str, a part str in Name.
RegexName: str, supports regex using re.match.
You can only use one of Name, SubName, RegexName.
ClassName: str.
AutomationId: str.
Depth: int, only search controls in relative depth from searchFromControl, ignore controls in depth(0~Depth-1),
if set, searchDepth will be set to Depth too.
Compare: Callable[[Control, int], bool], custom compare function(control: Control, depth: int) -> bool.
searchProperties, other properties specified for the control, only for debug log.
`Control` wraps IUIAutomationElement.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nn-uiautomationclient-iuiautomationelement
"""
self._element = element
self._elementDirectAssign = True if element else False
self.searchFromControl = searchFromControl
self.searchDepth = Depth or searchDepth
self.searchInterval = searchInterval
self.foundIndex = foundIndex
self.searchProperties = searchProperties
if Name is not None:
searchProperties["Name"] = Name
if SubName is not None:
searchProperties["SubName"] = SubName
if RegexName is not None:
searchProperties["RegexName"] = RegexName
self.regexName = re.compile(RegexName)
else:
self.regexName = None
if ClassName is not None:
searchProperties["ClassName"] = ClassName
if AutomationId is not None:
searchProperties["AutomationId"] = AutomationId
if ControlType is not None:
searchProperties["ControlType"] = ControlType
if Depth is not None:
searchProperties["Depth"] = Depth
if Compare is not None:
searchProperties["Compare"] = Compare
self._supportedPatterns = {}
def __str__(self) -> str:
return "ControlType: {0} ClassName: {1} AutomationId: {2} Rect: {3} Name: {4} Handle: 0x{5:X}({5})".format(
self.ControlTypeName,
self.ClassName,
self.AutomationId,
self.BoundingRectangle,
self.Name,
self.NativeWindowHandle,
)
def __repr__(self) -> str:
return (
"<{0} ClassName={1!r} AutomationId={2} Rect={3} Name={4!r} Handle=0x{5:X}({5})>".format(
self.__class__.__name__,
self.ClassName,
self.AutomationId,
self.BoundingRectangle,
self.Name,
self.NativeWindowHandle,
)
)
def __getitem__(self, pos: int) -> "Control" | None:
if pos == 1:
return self.GetFirstChildControl()
elif pos == -1:
return self.GetLastChildControl()
elif pos > 1:
child = self.GetFirstChildControl()
for _ in range(pos - 1):
if child is None:
return None
child = child.GetNextSiblingControl()
return child
elif pos < -1:
child = self.GetLastChildControl()
for _ in range(-pos - 1):
if child is None:
return None
child = child.GetPreviousSiblingControl()
return child
else:
raise ValueError
@staticmethod
def CreateControlFromElement(
element: "ctypes.POINTER(IUIAutomationElement)",
) -> "Control" | None:
"""
Create a concreate `Control` from a com type `IUIAutomationElement`.
element: `ctypes.POINTER(IUIAutomationElement)`.
Return a subclass of `Control`, an instance of the control's real type.
"""
if element:
controlType = element.CurrentControlType
if controlType in ControlConstructors:
return ControlConstructors[controlType](element=element)
else:
pass
return None
@staticmethod
def CreateControlFromControl(control: "Control") -> "Control" | None:
"""
Create a concreate `Control` from a control instance, copy it.
control: `Control` or its subclass.
Return a subclass of `Control`, an instance of the control's real type.
For example: if control's ControlType is EditControl, return an EditControl.
"""
newControl = Control.CreateControlFromElement(control.Element)
return newControl
def SetSearchFromControl(self, searchFromControl: "Control") -> None:
"""searchFromControl: `Control` or its subclass"""
self.searchFromControl = searchFromControl
def SetSearchDepth(self, searchDepth: int) -> None:
self.searchDepth = searchDepth
def AddSearchProperties(self, **searchProperties) -> None:
"""
Add search properties using `dict.update`.
searchProperties: dict, same as searchProperties in `Control.__init__`.
"""
self.searchProperties.update(searchProperties)
if "Depth" in searchProperties:
self.searchDepth = searchProperties["Depth"]
if "RegexName" in searchProperties:
regName = searchProperties["RegexName"]
self.regexName = re.compile(regName) if regName else None
def RemoveSearchProperties(self, **searchProperties) -> None:
"""
searchProperties: dict, same as searchProperties in `Control.__init__`.
"""
for key in searchProperties:
del self.searchProperties[key]
if key == "RegexName":
self.regexName = None
def GetSearchPropertiesStr(self) -> str:
strs = [
"{}: {}".format(k, ControlTypeNames[v] if k == "ControlType" else repr(v))
for k, v in self.searchProperties.items()
]
return "{" + ", ".join(strs) + "}"
def GetColorfulSearchPropertiesStr(self, keyColor="DarkGreen", valueColor="DarkCyan") -> str:
"""keyColor, valueColor: str, color name in class ConsoleColor"""
strs = [
"<Color={}>{}</Color>: <Color={}>{}</Color>".format(
keyColor if k in Control.ValidKeys else "DarkYellow",
k,
valueColor,
ControlTypeNames[v] if k == "ControlType" else repr(v),
)
for k, v in self.searchProperties.items()
]
return "{" + ", ".join(strs) + "}"
def BuildUpdatedCache(self, cacheRequest: "CacheRequest") -> "Control":
"""
Retrieves a new UI Automation element with an updated cache.
cacheRequest: CacheRequest.
Return a subclass of `Control`, an instance of the control's real type.
"""
updatedElement = self.Element.BuildUpdatedCache(cacheRequest.check_request)
return Control.CreateControlFromElement(updatedElement)
@property
def CachedAcceleratorKey(self) -> str:
"""Get the cached accelerator key."""
return self.Element.CachedAcceleratorKey
@property
def CachedAccessKey(self) -> str:
"""Get the cached access key."""
return self.Element.CachedAccessKey
@property
def CachedAriaProperties(self) -> str:
"""Get the cached aria properties."""
return self.Element.CachedAriaProperties
@property
def CachedAriaRole(self) -> str:
"""Get the cached aria role."""
return self.Element.CachedAriaRole
@property
def CachedAutomationId(self) -> str:
"""Get the cached automation id."""
return self.Element.CachedAutomationId
@property
def CachedBoundingRectangle(self) -> Rect:
"""Get the cached bounding rectangle."""
rect = self.Element.CachedBoundingRectangle
return Rect(rect.left, rect.top, rect.right, rect.bottom)
@property
def CachedClassName(self) -> str:
"""Get the cached class name."""
return self.Element.CachedClassName
@property
def CachedControlType(self) -> int:
"""Get the cached control type."""
return self.Element.CachedControlType
@property
def CachedControlTypeName(self) -> str:
"""Get the cached control type name."""
try:
return ControlTypeNames.get(self.CachedControlType, "Unknown")
except Exception:
return "Unknown"
@property
def CachedControllerFor(self) -> Any:
"""Get the cached controller for."""
return self.Element.CachedControllerFor
@property
def CachedCulture(self) -> int:
"""Get the cached culture."""
return self.Element.CachedCulture
@property
def CachedDescribedBy(self) -> Any:
"""Get the cached described by."""
return self.Element.CachedDescribedBy
@property
def CachedFlowsTo(self) -> Any:
"""Get the cached flows to."""
return self.Element.CachedFlowsTo
@property
def CachedFrameworkId(self) -> str:
"""Get the cached framework id."""
return self.Element.CachedFrameworkId
@property
def CachedHasKeyboardFocus(self) -> bool:
"""Get the cached has keyboard focus."""
return self.Element.CachedHasKeyboardFocus
@property
def CachedHelpText(self) -> str:
"""Get the cached help text."""
return self.Element.CachedHelpText
@property
def CachedIsContentElement(self) -> bool:
"""Get the cached is content element."""
return self.Element.CachedIsContentElement
@property
def CachedIsControlElement(self) -> bool:
"""Get the cached is control element."""
return self.Element.CachedIsControlElement
@property
def CachedIsDataValidForForm(self) -> bool:
"""Get the cached is data valid for form."""
return self.Element.CachedIsDataValidForForm
@property
def CachedIsEnabled(self) -> bool:
"""Get the cached is enabled."""
return self.Element.CachedIsEnabled
@property
def CachedIsKeyboardFocusable(self) -> bool:
"""Get the cached is keyboard focusable."""
return self.Element.CachedIsKeyboardFocusable
@property
def CachedIsOffscreen(self) -> bool:
"""Get the cached is offscreen."""
return self.Element.CachedIsOffscreen
@property
def CachedIsPassword(self) -> bool:
"""Get the cached is password."""
return self.Element.CachedIsPassword
@property
def CachedIsRequiredForForm(self) -> bool:
"""Get the cached is required for form."""
return self.Element.CachedIsRequiredForForm
@property
def CachedItemStatus(self) -> str:
"""Get the cached item status."""
return self.Element.CachedItemStatus
@property
def CachedItemType(self) -> str:
"""Get the cached item type."""
return self.Element.CachedItemType
@property
def CachedLabeledBy(self) -> Any:
"""Get the cached labeled by."""
return self.Element.CachedLabeledBy
@property
def CachedLocalizedControlType(self) -> str:
"""Get the cached localized control type."""
return self.Element.CachedLocalizedControlType
@property
def CachedName(self) -> str:
"""Get the cached name."""
return self.Element.CachedName
@property
def CachedNativeWindowHandle(self) -> int:
"""Get the cached native window handle."""
return self.Element.CachedNativeWindowHandle
@property
def CachedOrientation(self) -> int:
"""Get the cached orientation."""
return self.Element.CachedOrientation
@property
def CachedProcessId(self) -> int:
"""Get the cached process id."""
return self.Element.CachedProcessId
@property
def CachedProviderDescription(self) -> str:
"""Get the cached provider description."""
return self.Element.CachedProviderDescription
@property
def AcceleratorKey(self) -> str:
"""
Property AcceleratorKey.
Call IUIAutomationElement::get_CurrentAcceleratorKey.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentacceleratorkey
"""
return self.Element.CurrentAcceleratorKey
@property
def AccessKey(self) -> str:
"""
Property AccessKey.
Call IUIAutomationElement::get_CurrentAccessKey.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentaccesskey
"""
return self.Element.CurrentAccessKey
@property
def AriaProperties(self) -> str:
"""
Property AriaProperties.
Call IUIAutomationElement::get_CurrentAriaProperties.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentariaproperties
"""
return self.Element.CurrentAriaProperties
@property
def AriaRole(self) -> str:
"""
Property AriaRole.
Call IUIAutomationElement::get_CurrentAriaRole.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentariarole
"""
return self.Element.CurrentAriaRole
@property
def AutomationId(self) -> str:
"""
Property AutomationId.
Call IUIAutomationElement::get_CurrentAutomationId.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentautomationid
"""
return self.Element.CurrentAutomationId
@property
def BoundingRectangle(self) -> Rect:
"""
Property BoundingRectangle.
Call IUIAutomationElement::get_CurrentBoundingRectangle.
Return `Rect`.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentboundingrectangle
rect = control.BoundingRectangle
print(rect.left, rect.top, rect.right, rect.bottom, rect.width(), rect.height(), rect.xcenter(), rect.ycenter())
"""
rect = self.Element.CurrentBoundingRectangle
return Rect(rect.left, rect.top, rect.right, rect.bottom)
@property
def ClassName(self) -> str:
"""
Property ClassName.
Call IUIAutomationElement::get_CurrentClassName.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentclassname
"""
return self.Element.CurrentClassName
@property
def ControlType(self) -> int:
"""
Property ControlType.
Return int, a value in class `ControlType`.
Call IUIAutomationElement::get_CurrentControlType.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentcontroltype
"""
return self.Element.CurrentControlType
# @property
# def ControllerFor(self):
# return self.Element.CurrentControllerFor
@property
def Culture(self) -> int:
"""
Property Culture.
Call IUIAutomationElement::get_CurrentCulture.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentculture
"""
return self.Element.CurrentCulture
# @property
# def DescribedBy(self):
# return self.Element.CurrentDescribedBy
# @property
# def FlowsTo(self):
# return self.Element.CurrentFlowsTo
@property
def FrameworkId(self) -> str:
"""
Property FrameworkId.
Call IUIAutomationElement::get_CurrentFrameworkId.
Return str, such as Win32, WPF...
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentframeworkid
"""
return self.Element.CurrentFrameworkId
@property
def HasKeyboardFocus(self) -> bool:
"""
Property HasKeyboardFocus.
Call IUIAutomationElement::get_CurrentHasKeyboardFocus.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currenthaskeyboardfocus
"""
return bool(self.Element.CurrentHasKeyboardFocus)
@property
def HelpText(self) -> str:
"""
Property HelpText.
Call IUIAutomationElement::get_CurrentHelpText.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currenthelptext
"""
return self.Element.CurrentHelpText
@property
def IsContentElement(self) -> bool:
"""
Property IsContentElement.
Call IUIAutomationElement::get_CurrentIsContentElement.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentiscontentelement
"""
return bool(self.Element.CurrentIsContentElement)
@property
def IsControlElement(self) -> bool:
"""
Property IsControlElement.
Call IUIAutomationElement::get_CurrentIsControlElement.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentiscontrolelement
"""
return bool(self.Element.CurrentIsControlElement)
@property
def IsDataValidForForm(self) -> bool:
"""
Property IsDataValidForForm.
Call IUIAutomationElement::get_CurrentIsDataValidForForm.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentisdatavalidforform
"""
return bool(self.Element.CurrentIsDataValidForForm)
@property
def IsEnabled(self) -> bool:
"""
Property IsEnabled.
Call IUIAutomationElement::get_CurrentIsEnabled.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentisenabled
"""
return bool(self.Element.CurrentIsEnabled)
@property
def IsKeyboardFocusable(self) -> bool:
"""
Property IsKeyboardFocusable.
Call IUIAutomationElement::get_CurrentIsKeyboardFocusable.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentiskeyboardfocusable
"""
return bool(self.Element.CurrentIsKeyboardFocusable)
@property
def IsOffscreen(self) -> bool:
"""
Property IsOffscreen.
Call IUIAutomationElement::get_CurrentIsOffscreen.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentisoffscreen
"""
return bool(self.Element.CurrentIsOffscreen)
@property
def IsPassword(self) -> bool:
"""
Property IsPassword.
Call IUIAutomationElement::get_CurrentIsPassword.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentispassword
"""
return bool(self.Element.CurrentIsPassword)
@property
def IsRequiredForForm(self) -> bool:
"""
Property IsRequiredForForm.
Call IUIAutomationElement::get_CurrentIsRequiredForForm.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentisrequiredforform
"""
return bool(self.Element.CurrentIsRequiredForForm)
@property
def ItemStatus(self) -> str:
"""
Property ItemStatus.
Call IUIAutomationElement::get_CurrentItemStatus.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentitemstatus
"""
return self.Element.CurrentItemStatus
@property
def ItemType(self) -> str:
"""
Property ItemType.
Call IUIAutomationElement::get_CurrentItemType.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentitemtype
"""
return self.Element.CurrentItemType
# @property
# def LabeledBy(self):
# return self.Element.CurrentLabeledBy
@property
def LocalizedControlType(self) -> str:
"""
Property LocalizedControlType.
Call IUIAutomationElement::get_CurrentLocalizedControlType.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentlocalizedcontroltype
"""
return self.Element.CurrentLocalizedControlType
@property
def Name(self) -> str:
"""
Property Name.
Call IUIAutomationElement::get_CurrentName.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentname
"""
return self.Element.CurrentName or "" # CurrentName may be None
@property
def NativeWindowHandle(self) -> int:
"""
Property NativeWindowHandle.
Call IUIAutomationElement::get_CurrentNativeWindowHandle.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentnativewindowhandle
"""
try:
handle = self.Element.CurrentNativeWindowHandle
except comtypes.COMError:
return 0
return 0 if handle is None else handle
@property
def Orientation(self) -> int:
"""
Property Orientation.
Return int, a value in class `OrientationType`.
Call IUIAutomationElement::get_CurrentOrientation.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentorientation
"""
return self.Element.CurrentOrientation
@property
def ProcessId(self) -> int:
"""
Property ProcessId.
Call IUIAutomationElement::get_CurrentProcessId.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentprocessid
"""
return self.Element.CurrentProcessId
@property
def ProviderDescription(self) -> str:
"""
Property ProviderDescription.
Call IUIAutomationElement::get_CurrentProviderDescription.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-get_currentproviderdescription
"""
return self.Element.CurrentProviderDescription
def FindAll(self, scope: int, condition) -> List["Control"]:
"""
Find all UI Automation elements that satisfy the specified condition.
Call IUIAutomationElement::FindAll.
scope: int, a value in class `TreeScope`, specifying the scope of the search.
condition: a condition object from IUIAutomation.CreateTrueCondition() or similar.
Return List[Control], a list of `Control` subclasses that match the condition.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-findall
"""
elementArray = self.Element.FindAll(scope, condition)
if not elementArray:
return []
controls = []
length = elementArray.Length
for i in range(length):
element = elementArray.GetElement(i)
control = Control.CreateControlFromElement(element)
if control:
controls.append(control)
return controls
def FindAllBuildCache(
self, scope: int, condition, cacheRequest: "CacheRequest"
) -> List["Control"]:
"""
Find all UI Automation elements that satisfy the specified condition, and cache properties and patterns.
Call IUIAutomationElement::FindAllBuildCache.
scope: int, a value in class `TreeScope`, specifying the scope of the search.
condition: a condition object from IUIAutomation.CreateTrueCondition() or similar.
cacheRequest: CacheRequest, specifies the properties and patterns to cache.
Return List[Control], a list of `Control` subclasses that match the condition with cached data.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-findallbuildcache
"""
elementArray = self.Element.FindAllBuildCache(scope, condition, cacheRequest.check_request)
if not elementArray:
return []
controls = []
length = elementArray.Length
for i in range(length):
element = elementArray.GetElement(i)
control = Control.CreateControlFromElement(element)
if control:
controls.append(control)
return controls
def FindFirst(self, scope: int, condition) -> "Control" | None:
"""
Find the first UI Automation element that satisfies the specified condition.
Call IUIAutomationElement::FindFirst.
scope: int, a value in class `TreeScope`, specifying the scope of the search.
condition: a condition object from IUIAutomation.CreateTrueCondition() or similar.
Return `Control` subclass or None.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-findfirst
"""
element = self.Element.FindFirst(scope, condition)
return Control.CreateControlFromElement(element)
def FindFirstBuildCache(
self, scope: int, condition, cacheRequest: "CacheRequest"
) -> "Control" | None:
"""
Find the first UI Automation element that satisfies the specified condition, and cache properties and patterns.
Call IUIAutomationElement::FindFirstBuildCache.
scope: int, a value in class `TreeScope`, specifying the scope of the search.
condition: a condition object from IUIAutomation.CreateTrueCondition() or similar.
cacheRequest: CacheRequest, specifies the properties and patterns to cache.
Return `Control` subclass or None with cached data.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-findfirstbuildcache
"""
element = self.Element.FindFirstBuildCache(scope, condition, cacheRequest.check_request)
return Control.CreateControlFromElement(element)
def GetCachedChildren(self) -> List["Control"]:
"""
Retrieve the cached child elements of this UI Automation element.
Call IUIAutomationElement::GetCachedChildren.
Return List[Control], a list of cached child `Control` subclasses.
Note: Children are cached only if the scope of the cache request included TreeScope_Subtree,
TreeScope_Children, or TreeScope_Descendants.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcachedchildren
"""
try:
elementArray = self.Element.GetCachedChildren()
if not elementArray:
return []
controls = []
length = elementArray.Length
for i in range(length):
element = elementArray.GetElement(i)
control = Control.CreateControlFromElement(element)
if control:
controls.append(control)
return controls
except comtypes.COMError:
return []
def GetCachedParent(self) -> "Control" | None:
"""
Retrieve the cached parent of this UI Automation element.
Call IUIAutomationElement::GetCachedParent.
Return `Control` subclass or None.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcachedparent
"""
try:
element = self.Element.GetCachedParent()
return Control.CreateControlFromElement(element)
except comtypes.COMError:
return None
def GetCachedPatternAs(self, patternId: int, riid):
"""
Retrieve a cached pattern interface from this UI Automation element, with a specific interface ID.
Call IUIAutomationElement::GetCachedPatternAs.
patternId: int, a value in class `PatternId`.
riid: GUID, the interface identifier.
Return a pattern object if the pattern was cached, else None.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcachedpatternas
"""
try:
return self.Element.GetCachedPatternAs(patternId, riid)
except comtypes.COMError:
return None
def GetCachedPropertyValue(self, propertyId: int) -> Any:
"""
Retrieve a cached property value from this UI Automation element.
Call IUIAutomationElement::GetCachedPropertyValue.
propertyId: int, a value in class `PropertyId`.
Return Any, the cached property value corresponding to propertyId.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcachedpropertyvalue
"""
try:
return self.Element.GetCachedPropertyValue(propertyId)
except comtypes.COMError:
return None
def GetCachedPropertyValueEx(self, propertyId: int, ignoreDefaultValue: int) -> Any:
"""
Retrieve a cached property value from this UI Automation element, optionally ignoring the default value.
Call IUIAutomationElement::GetCachedPropertyValueEx.
propertyId: int, a value in class `PropertyId`.
ignoreDefaultValue: int, 0 or 1. If 1, a default value is not returned.
Return Any, the cached property value corresponding to propertyId.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcachedpropertyvalueex
"""
try:
return self.Element.GetCachedPropertyValueEx(propertyId, ignoreDefaultValue)
except comtypes.COMError:
return None
def GetClickablePoint(self) -> Tuple[int, int, bool]:
"""
Call IUIAutomationElement::GetClickablePoint.
Return Tuple[int, int, bool], three items tuple (x, y, gotClickable), such as (20, 10, True)
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getclickablepoint
"""
point, gotClickable = self.Element.GetClickablePoint()
return (point.x, point.y, bool(gotClickable))
def GetPattern(self, patternId: int):
"""
Call IUIAutomationElement::GetCurrentPattern.
Get a new pattern by pattern id if it supports the pattern.
patternId: int, a value in class `PatternId`.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcurrentpattern
"""
try:
pattern = self.Element.GetCurrentPattern(patternId)
if pattern:
subPattern = CreatePattern(patternId, pattern)
self._supportedPatterns[patternId] = subPattern
return subPattern
except comtypes.COMError:
pass
def GetPatternAs(self, patternId: int, riid):
"""
Call IUIAutomationElement::GetCurrentPatternAs.
Get a new pattern by pattern id if it supports the pattern, todo.
patternId: int, a value in class `PatternId`.
riid: GUID.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcurrentpatternas
"""
return self.Element.GetCurrentPatternAs(patternId, riid)
def GetPropertyValue(self, propertyId: int) -> Any:
"""
Call IUIAutomationElement::GetCurrentPropertyValue.
propertyId: int, a value in class `PropertyId`.
Return Any, corresponding type according to propertyId.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcurrentpropertyvalue
"""
return self.Element.GetCurrentPropertyValue(propertyId)
def GetPropertyValueEx(self, propertyId: int, ignoreDefaultValue: int) -> Any:
"""
Call IUIAutomationElement::GetCurrentPropertyValueEx.
propertyId: int, a value in class `PropertyId`.
ignoreDefaultValue: int, 0 or 1.
Return Any, corresponding type according to propertyId.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcurrentpropertyvalueex
"""
return self.Element.GetCurrentPropertyValueEx(propertyId, ignoreDefaultValue)
def GetRuntimeId(self) -> List[int]:
"""
Call IUIAutomationElement::GetRuntimeId.
Return List[int], a list of int.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getruntimeid
"""
return self.Element.GetRuntimeId()
# QueryInterface
# Release
def SetFocus(self) -> bool:
"""
Call IUIAutomationElement::SetFocus.
Refer https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-setfocus
"""
try:
return self.Element.SetFocus() == S_OK
except comtypes.COMError:
return False
@property
def Element(self):
"""
Property Element.
Return `ctypes.POINTER(IUIAutomationElement)`.
"""
if not self._element:
self.Refind(
maxSearchSeconds=TIME_OUT_SECOND,
searchIntervalSeconds=self.searchInterval,
)
return self._element
@property
def ControlTypeName(self) -> str:
"""
Property ControlTypeName.
"""
return ControlTypeNames[self.ControlType]
def GetCachedPattern(self, patternId: int, cache: bool):
"""
Get a pattern by patternId.
patternId: int, a value in class `PatternId`.
Return a pattern if it supports the pattern else None.
cache: bool, if True, store the pattern for later use, if False, get a new pattern by `self.GetPattern`.
"""
if cache:
pattern = self._supportedPatterns.get(patternId, None)
if pattern:
return pattern
else:
pattern = self.GetPattern(patternId)
if pattern:
self._supportedPatterns[patternId] = pattern
return pattern
else:
pattern = self.GetPattern(patternId)
if pattern:
self._supportedPatterns[patternId] = pattern
return pattern
def GetLegacyIAccessiblePattern(self) -> LegacyIAccessiblePattern:
"""
Return `LegacyIAccessiblePattern` if it supports the pattern else None.
"""
return self.GetPattern(PatternId.LegacyIAccessiblePattern)
def GetAncestorControl(self, condition: Callable[["Control", int], bool]) -> "Control" | None:
"""
Get an ancestor control that matches the condition.
condition: Callable[[Control, int], bool], function(control: Control, depth: int) -> bool,
depth starts with -1 and decreses when search goes up.
Return `Control` subclass or None.
"""
ancestor = self
depth = 0
while ancestor is not None:
ancestor = ancestor.GetParentControl()
depth -= 1
if ancestor:
if condition(ancestor, depth):
return ancestor
return None
def GetParentControl(self) -> "Control" | None:
"""
Return `Control` subclass or None.
"""
ele = _AutomationClient.instance().ViewWalker.GetParentElement(self.Element)
return Control.CreateControlFromElement(ele)
def GetFirstChildControl(self) -> "Control" | None:
"""
Return `Control` subclass or None.
"""
ele = _AutomationClient.instance().ViewWalker.GetFirstChildElement(self.Element)
return Control.CreateControlFromElement(ele)
def GetLastChildControl(self) -> "Control" | None:
"""
Return `Control` subclass or None.
"""
ele = _AutomationClient.instance().ViewWalker.GetLastChildElement(self.Element)
return Control.CreateControlFromElement(ele)
def GetNextSiblingControl(self) -> "Control" | None:
"""
Return `Control` subclass or None.
"""
ele = _AutomationClient.instance().ViewWalker.GetNextSiblingElement(self.Element)
return Control.CreateControlFromElement(ele)
def GetPreviousSiblingControl(self) -> "Control" | None:
"""
Return `Control` subclass or None.
"""
ele = _AutomationClient.instance().ViewWalker.GetPreviousSiblingElement(self.Element)
return Control.CreateControlFromElement(ele)
def GetSiblingControl(
self, condition: Callable[["Control"], bool], forward: bool = True
) -> "Control" | None:
"""
Get a sibling control that matches the condition.
forward: bool, if True, only search next siblings, if False, search pervious siblings first, then search next siblings.
condition: Callable[[Control], bool], function(control: Control) -> bool.
Return `Control` subclass or None.
"""
if not forward:
prev = self
while True:
prev = prev.GetPreviousSiblingControl()
if prev:
if condition(prev):
return prev
else:
break
next_ = self
while True:
next_ = next_.GetNextSiblingControl()
if next_:
if condition(next_):
return next_
else:
break
def GetChildren(self) -> List["Control"]:
"""
Return List[Control], a list of `Control` subclasses.
"""
children = []
child = self.GetFirstChildControl()
while child:
children.append(child)
child = child.GetNextSiblingControl()
return children
def _CompareFunction(self, control: "Control", depth: int) -> bool:
"""
Define how to search.
control: `Control` or its subclass.
depth: int, tree depth from searchFromControl.
Return bool.
"""
compareFunc = None
for key, value in self.searchProperties.items():
if "ControlType" == key:
if value != control.ControlType:
return False
elif "ClassName" == key:
if value != control.ClassName:
return False
elif "AutomationId" == key:
if value != control.AutomationId:
return False
elif "Depth" == key:
if value != depth:
return False
elif "Name" == key:
if value != control.Name:
return False
elif "SubName" == key:
if value not in control.Name:
return False
elif "RegexName" == key:
if not self.regexName.match(control.Name):
return False
elif "Compare" == key:
compareFunc = value
# use Compare at last
if compareFunc and not compareFunc(control, depth):
return False
return True
def Exists(
self,
maxSearchSeconds: float = 5,
searchIntervalSeconds: float = SEARCH_INTERVAL,
printIfNotExist: bool = False,
) -> bool:
"""
maxSearchSeconds: float
searchIntervalSeconds: float
Find control every searchIntervalSeconds seconds in maxSearchSeconds seconds.
Return bool, True if find
"""
if self._element and self._elementDirectAssign:
# if element is directly assigned, not by searching, just check whether self._element is valid
# but I can't find an API in UIAutomation that can directly check
rootElement = _AutomationClient.instance().IUIAutomation.GetRootElement()
if _AutomationClient.instance().IUIAutomation.CompareElements(
self._element, rootElement
):
return True
else:
parentElement = _AutomationClient.instance().ViewWalker.GetParentElement(
self._element
)
if parentElement:
return True
else:
return False
# find the element
if not self.searchProperties:
raise LookupError("control's searchProperties must not be empty!")
self._element = None
startTime = ProcessTime()
# Use same timeout(s) parameters for resolve all parents
prev = self.searchFromControl
if prev and not prev._element and not prev.Exists(maxSearchSeconds, searchIntervalSeconds):
if printIfNotExist or DEBUG_EXIST_DISAPPEAR:
pass
return False
startTime2 = ProcessTime()
if DEBUG_SEARCH_TIME:
startDateTime = datetime.datetime.now()
while True:
control = FindControl(
self.searchFromControl,
self._CompareFunction,
self.searchDepth,
False,
self.foundIndex,
)
if control:
self._element = control.Element
control._element = 0 # control will be destroyed, but the element needs to be stored in self._element
if DEBUG_SEARCH_TIME:
pass
return True
else:
remain = startTime + maxSearchSeconds - ProcessTime()
if remain > 0:
time.sleep(min(remain, searchIntervalSeconds))
else:
if printIfNotExist or DEBUG_EXIST_DISAPPEAR:
pass
return False
def Disappears(
self,
maxSearchSeconds: float = 5,
searchIntervalSeconds: float = SEARCH_INTERVAL,
printIfNotDisappear: bool = False,
) -> bool:
"""
maxSearchSeconds: float
searchIntervalSeconds: float
Check if control disappears every searchIntervalSeconds seconds in maxSearchSeconds seconds.
Return bool, True if control disappears.
"""
global DEBUG_EXIST_DISAPPEAR
start = ProcessTime()
while True:
temp = DEBUG_EXIST_DISAPPEAR
DEBUG_EXIST_DISAPPEAR = False # do not print for Exists
if not self.Exists(0, 0, False):
DEBUG_EXIST_DISAPPEAR = temp
return True
DEBUG_EXIST_DISAPPEAR = temp
remain = start + maxSearchSeconds - ProcessTime()
if remain > 0:
time.sleep(min(remain, searchIntervalSeconds))
else:
if printIfNotDisappear or DEBUG_EXIST_DISAPPEAR:
pass
return False
def Refind(
self,
maxSearchSeconds: float = TIME_OUT_SECOND,
searchIntervalSeconds: float = SEARCH_INTERVAL,
raiseException: bool = True,
) -> bool:
"""
Refind the control every searchIntervalSeconds seconds in maxSearchSeconds seconds.
maxSearchSeconds: float.
searchIntervalSeconds: float.
raiseException: bool, if True, raise a LookupError if timeout.
Return bool, True if find.
"""
if not self.Exists(
maxSearchSeconds,
searchIntervalSeconds,
False if raiseException else DEBUG_EXIST_DISAPPEAR,
):
if raiseException:
raise LookupError(
"Find Control Timeout({}s): {}".format(
maxSearchSeconds, self.GetSearchPropertiesStr()
)
)
else:
return False
return True
def GetPosition(self, ratioX: float = 0.5, ratioY: float = 0.5) -> Tuple[int, int] | None:
"""
Gets the position of the center of the control.
ratioX: float.
ratioY: float.
Return Tuple[int, int], two ints tuple (x, y), the cursor positon relative to screen(0, 0)
"""
rect = self.BoundingRectangle
if rect.width() == 0 or rect.height() == 0:
return None
x = rect.left + int(rect.width() * ratioX)
y = rect.top + int(rect.height() * ratioY)
return x, y
def MoveCursorToInnerPos(
self,
x: int | None = None,
y: int | None = None,
ratioX: float = 0.5,
ratioY: float = 0.5,
simulateMove: bool = True,
) -> Tuple[int, int] | None:
"""
Move cursor to control's internal position, default to center.
x: int, if < 0, move to self.BoundingRectangle.right + x, if not None, ignore ratioX.
y: int, if < 0, move to self.BoundingRectangle.bottom + y, if not None, ignore ratioY.
ratioX: float.
ratioY: float.
simulateMove: bool.
Return Tuple[int, int], two ints tuple (x, y), the cursor positon relative to screen(0, 0)
after moving or None if control's width or height is 0.
"""
rect = self.BoundingRectangle
if rect.width() == 0 or rect.height() == 0:
return None
if x is None:
x = rect.left + int(rect.width() * ratioX)
else:
x = (rect.left if x >= 0 else rect.right) + x
if y is None:
y = rect.top + int(rect.height() * ratioY)
else:
y = (rect.top if y >= 0 else rect.bottom) + y
if simulateMove and MAX_MOVE_SECOND > 0:
MoveTo(x, y, waitTime=0)
else:
SetCursorPos(x, y)
return x, y
def MoveCursorToMyCenter(self, simulateMove: bool = True) -> Tuple[int, int] | None:
"""
Move cursor to control's center.
Return Tuple[int, int], two ints tuple (x, y), the cursor positon relative to screen(0, 0) after moving.
"""
return self.MoveCursorToInnerPos(simulateMove=simulateMove)
def Click(
self,
x: int | None = None,
y: int | None = None,
ratioX: float = 0.5,
ratioY: float = 0.5,
simulateMove: bool = True,
waitTime: float = OPERATION_WAIT_TIME,
) -> None:
"""
x: int, if < 0, click self.BoundingRectangle.right + x, if not None, ignore ratioX.
y: int, if < 0, click self.BoundingRectangle.bottom + y, if not None, ignore ratioY.
ratioX: float.
ratioY: float.
simulateMove: bool, if True, first move cursor to control smoothly.
waitTime: float.
Click(), Click(ratioX=0.5, ratioY=0.5): click center.
Click(10, 10): click left+10, top+10.
Click(-10, -10): click right-10, bottom-10.
"""
point = self.MoveCursorToInnerPos(x, y, ratioX, ratioY, simulateMove)
if point:
Click(point[0], point[1], waitTime)
def MiddleClick(
self,
x: int | None = None,
y: int | None = None,
ratioX: float = 0.5,
ratioY: float = 0.5,
simulateMove: bool = True,
waitTime: float = OPERATION_WAIT_TIME,
) -> None:
"""
x: int, if < 0, middle click self.BoundingRectangle.right + x, if not None, ignore ratioX.
y: int, if < 0, middle click self.BoundingRectangle.bottom + y, if not None, ignore ratioY.
ratioX: float.
ratioY: float.
simulateMove: bool, if True, first move cursor to control smoothly.
waitTime: float.
MiddleClick(), MiddleClick(ratioX=0.5, ratioY=0.5): middle click center.
MiddleClick(10, 10): middle click left+10, top+10.
MiddleClick(-10, -10): middle click right-10, bottom-10.
"""
point = self.MoveCursorToInnerPos(x, y, ratioX, ratioY, simulateMove)
if point:
MiddleClick(point[0], point[1], waitTime)
def RightClick(
self,
x: int | None = None,
y: int | None = None,
ratioX: float = 0.5,
ratioY: float = 0.5,
simulateMove: bool = True,
waitTime: float = OPERATION_WAIT_TIME,
) -> None:
"""
x: int, if < 0, right click self.BoundingRectangle.right + x, if not None, ignore ratioX.
y: int, if < 0, right click self.BoundingRectangle.bottom + y, if not None, ignore ratioY.
ratioX: float.
ratioY: float.
simulateMove: bool, if True, first move cursor to control smoothly.
waitTime: float.
RightClick(), RightClick(ratioX=0.5, ratioY=0.5): right click center.
RightClick(10, 10): right click left+10, top+10.
RightClick(-10, -10): right click right-10, bottom-10.
"""
point = self.MoveCursorToInnerPos(x, y, ratioX, ratioY, simulateMove)
if point:
RightClick(point[0], point[1], waitTime)
def DoubleClick(
self,
x: int | None = None,
y: int | None = None,
ratioX: float = 0.5,
ratioY: float = 0.5,
simulateMove: bool = True,
waitTime: float = OPERATION_WAIT_TIME,
) -> None:
"""
x: int, if < 0, right click self.BoundingRectangle.right + x, if not None, ignore ratioX.
y: int, if < 0, right click self.BoundingRectangle.bottom + y, if not None, ignore ratioY.
ratioX: float.
ratioY: float.
simulateMove: bool, if True, first move cursor to control smoothly.
waitTime: float.
DoubleClick(), DoubleClick(ratioX=0.5, ratioY=0.5): double click center.
DoubleClick(10, 10): double click left+10, top+10.
DoubleClick(-10, -10): double click right-10, bottom-10.
"""
x, y = self.MoveCursorToInnerPos(x, y, ratioX, ratioY, simulateMove)
Click(x, y, GetDoubleClickTime() * 1.0 / 2000)
Click(x, y, waitTime)
def DragDrop(
self,
x1: int,
y1: int,
x2: int,
y2: int,
moveSpeed: float = 1,
waitTime: float = OPERATION_WAIT_TIME,
) -> None:
rect = self.BoundingRectangle
if rect.width() == 0 or rect.height() == 0:
return
x1 = (rect.left if x1 >= 0 else rect.right) + x1
y1 = (rect.top if y1 >= 0 else rect.bottom) + y1
x2 = (rect.left if x2 >= 0 else rect.right) + x2
y2 = (rect.top if y2 >= 0 else rect.bottom) + y2
DragDrop(x1, y1, x2, y2, moveSpeed, waitTime)
def RightDragDrop(
self,
x1: int,
y1: int,
x2: int,
y2: int,
moveSpeed: float = 1,
waitTime: float = OPERATION_WAIT_TIME,
) -> None:
rect = self.BoundingRectangle
if rect.width() == 0 or rect.height() == 0:
return
x1 = (rect.left if x1 >= 0 else rect.right) + x1
y1 = (rect.top if y1 >= 0 else rect.bottom) + y1
x2 = (rect.left if x2 >= 0 else rect.right) + x2
y2 = (rect.top if y2 >= 0 else rect.bottom) + y2
RightDragDrop(x1, y1, x2, y2, moveSpeed, waitTime)
def WheelDown(
self,
x: int | None = None,
y: int | None = None,
ratioX: float = 0.5,
ratioY: float = 0.5,
wheelTimes: int = 1,
interval: float = 0.05,
waitTime: float = OPERATION_WAIT_TIME,
) -> None:
"""
Make control have focus first, move cursor to the specified position and mouse wheel down.
x: int, if < 0, move x cursor to self.BoundingRectangle.right + x, if not None, ignore ratioX.
y: int, if < 0, move y cursor to self.BoundingRectangle.bottom + y, if not None, ignore ratioY.
ratioX: float.
ratioY: float.
wheelTimes: int.
interval: float.
waitTime: float.
"""
cursorX, cursorY = GetCursorPos()
self.SetFocus()
self.MoveCursorToInnerPos(x, y, ratioX, ratioY, simulateMove=False)
WheelDown(wheelTimes, interval, waitTime)
SetCursorPos(cursorX, cursorY)
def WheelUp(
self,
x: int | None = None,
y: int | None = None,
ratioX: float = 0.5,
ratioY: float = 0.5,
wheelTimes: int = 1,
interval: float = 0.05,
waitTime: float = OPERATION_WAIT_TIME,
) -> None:
"""
Make control have focus first, move cursor to the specified position and mouse wheel up.
x: int, if < 0, move x cursor to self.BoundingRectangle.right + x, if not None, ignore ratioX.
y: int, if < 0, move y cursor to self.BoundingRectangle.bottom + y, if not None, ignore ratioY.
ratioX: float.
ratioY: float.
wheelTimes: int.
interval: float.
waitTime: float.
"""
cursorX, cursorY = GetCursorPos()
self.SetFocus()
self.MoveCursorToInnerPos(x, y, ratioX, ratioY, simulateMove=False)
WheelUp(wheelTimes, interval, waitTime)
SetCursorPos(cursorX, cursorY)
def ShowWindow(self, cmdShow: int, waitTime: float = OPERATION_WAIT_TIME) -> bool | None:
"""
Get a native handle from self or ancestors until valid and call native `ShowWindow` with cmdShow.
cmdShow: int, a value in in class `SW`.
waitTime: float.
Return bool, True if succeed otherwise False and None if the handle could not be gotten.
"""
handle = self.NativeWindowHandle
if not handle:
control = self
while not handle and control:
control = control.GetParentControl()
if control:
handle = control.NativeWindowHandle
else:
handle = 0
break
if handle:
ret = ShowWindow(handle, cmdShow)
time.sleep(waitTime)
return ret
return None
def Show(self, waitTime: float = OPERATION_WAIT_TIME) -> bool | None:
"""
Call native `ShowWindow(SW.Show)`.
Return bool, True if succeed otherwise False and None if the handle could not be gotten.
"""
return self.ShowWindow(SW.Show, waitTime)
def Hide(self, waitTime: float = OPERATION_WAIT_TIME) -> bool | None:
"""
Call native `ShowWindow(SW.Hide)`.
waitTime: float
Return bool, True if succeed otherwise False and None if the handle could not be gotten.
"""
return self.ShowWindow(SW.Hide, waitTime)
def MoveWindow(self, x: int, y: int, width: int, height: int, repaint: bool = True) -> bool:
"""
Call native MoveWindow if control has a valid native handle.
x: int.
y: int.
width: int.
height: int.
repaint: bool.
Return bool, True if succeed otherwise False.
"""
handle = self.NativeWindowHandle
if handle:
return MoveWindow(handle, x, y, width, height, int(repaint))
return False
def GetWindowText(self) -> str | None:
"""
Call native GetWindowText if control has a valid native handle.
"""
handle = self.NativeWindowHandle
if handle:
return GetWindowText(handle)
return None
def SetWindowText(self, text: str) -> bool:
"""
Call native SetWindowText if control has a valid native handle.
"""
handle = self.NativeWindowHandle
if handle:
return SetWindowText(handle, text)
return False
def SendKey(self, key: int, waitTime: float = OPERATION_WAIT_TIME) -> None:
"""
Make control have focus first and type a key.
`self.SetFocus` may not work for some controls, you may need to click it to make it have focus.
key: int, a key code value in class Keys.
waitTime: float.
"""
self.SetFocus()
SendKey(key, waitTime)
def SendKeys(
self,
text: str,
interval: float = 0.01,
waitTime: float = OPERATION_WAIT_TIME,
charMode: bool = True,
) -> None:
"""
Make control have focus first and type keys.
`self.SetFocus` may not work for some controls, you may need to click it to make it have focus.
text: str, keys to type, see the docstring of `SendKeys`.
interval: float, seconds between keys.
waitTime: float.
charMode: bool, if False, the text typied is depend on the input method if a input method is on.
"""
self.SetFocus()
SendKeys(text, interval, waitTime, charMode)
def IsTopLevel(self) -> bool:
"""Determine whether current control is top level."""
handle = self.NativeWindowHandle
if handle:
return GetAncestor(handle, GAFlag.Root) == handle
return False
def GetTopLevelControl(self) -> "Control" | None:
"""
Get the top level control which current control lays.
If current control is top level, return self.
If current control is root control, return None.
Return `PaneControl` or `WindowControl` or None.
"""
handle = self.NativeWindowHandle
if handle:
topHandle = GetAncestor(handle, GAFlag.Root)
if topHandle:
if topHandle == handle:
return self
else:
return ControlFromHandle(topHandle)
else:
# self is root control
pass
else:
control = self
while True:
control = control.GetParentControl()
if not control:
break
handle = control.NativeWindowHandle
if handle:
topHandle = GetAncestor(handle, GAFlag.Root)
return ControlFromHandle(topHandle)
return None
def Control(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "Control":
return Control(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ButtonControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ButtonControl":
return ButtonControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def CalendarControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "CalendarControl":
return CalendarControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def CheckBoxControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "CheckBoxControl":
return CheckBoxControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ComboBoxControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ComboBoxControl":
return ComboBoxControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def CustomControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "CustomControl":
return CustomControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def DataGridControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "DataGridControl":
return DataGridControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def DataItemControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "DataItemControl":
return DataItemControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def DocumentControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "DocumentControl":
return DocumentControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def EditControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "EditControl":
return EditControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GroupControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "GroupControl":
return GroupControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def HeaderControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "HeaderControl":
return HeaderControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def HeaderItemControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "HeaderItemControl":
return HeaderItemControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def HyperlinkControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "HyperlinkControl":
return HyperlinkControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ImageControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ImageControl":
return ImageControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ListControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ListControl":
return ListControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ListItemControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ListItemControl":
return ListItemControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def MenuControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "MenuControl":
return MenuControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def MenuBarControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "MenuBarControl":
return MenuBarControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def MenuItemControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "MenuItemControl":
return MenuItemControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def PaneControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "PaneControl":
return PaneControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ProgressBarControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ProgressBarControl":
return ProgressBarControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def RadioButtonControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "RadioButtonControl":
return RadioButtonControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ScrollBarControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ScrollBarControl":
return ScrollBarControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def SemanticZoomControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "SemanticZoomControl":
return SemanticZoomControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def SeparatorControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "SeparatorControl":
return SeparatorControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def SliderControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "SliderControl":
return SliderControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def SpinnerControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "SpinnerControl":
return SpinnerControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def SplitButtonControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "SplitButtonControl":
return SplitButtonControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def StatusBarControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "StatusBarControl":
return StatusBarControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def TabControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "TabControl":
return TabControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def TabItemControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "TabItemControl":
return TabItemControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def TableControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "TableControl":
return TableControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def TextControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "TextControl":
return TextControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ThumbControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ThumbControl":
return ThumbControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def TitleBarControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "TitleBarControl":
return TitleBarControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ToolBarControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ToolBarControl":
return ToolBarControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def ToolTipControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "ToolTipControl":
return ToolTipControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def TreeControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "TreeControl":
return TreeControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def TreeItemControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "TreeItemControl":
return TreeItemControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def WindowControl(
self,
searchDepth=0xFFFFFFFF,
searchInterval=SEARCH_INTERVAL,
foundIndex=1,
element=0,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
) -> "WindowControl":
return WindowControl(
searchFromControl=self,
searchDepth=searchDepth,
searchInterval=searchInterval,
foundIndex=foundIndex,
element=element,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
class AppBarControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.AppBarControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
class ButtonControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ButtonControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetInvokePattern(self) -> InvokePattern:
"""
Return `InvokePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.InvokePattern)
def GetTogglePattern(self) -> TogglePattern:
"""
Return `TogglePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TogglePattern)
class CalendarControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.CalendarControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetGridPattern(self) -> GridPattern:
"""
Return `GridPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.GridPattern)
def GetTablePattern(self) -> TablePattern:
"""
Return `TablePattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.TablePattern)
def GetScrollPattern(self) -> ScrollPattern:
"""
Return `ScrollPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollPattern)
def GetSelectionPattern(self) -> SelectionPattern:
"""
Return `SelectionPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionPattern)
class CheckBoxControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.CheckBoxControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetTogglePattern(self) -> TogglePattern:
"""
Return `TogglePattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.TogglePattern)
def SetChecked(self, checked: bool) -> bool:
"""Return True if set successfully"""
tp = self.GetTogglePattern()
if tp:
return tp.SetToggleState(ToggleState.On if checked else ToggleState.Off)
return False
class ComboBoxControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ComboBoxControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetSelectionPattern(self) -> SelectionPattern:
"""
Return `SelectionPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionPattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
def Select(
self,
itemName: str = "",
condition: Callable[[str], bool] | None = None,
simulateMove: bool = True,
waitTime: float = OPERATION_WAIT_TIME,
) -> bool:
"""
Show combobox's popup menu and select a item by name.
itemName: str.
condition: Callable[[str], bool], function(comboBoxItemName: str) -> bool, if condition is valid, ignore itemName.
waitTime: float.
Some comboboxs doesn't support SelectionPattern, here is a workaround.
This method tries to add selection support.
It may not work for some comboboxes, such as comboboxes in older Qt version.
If it doesn't work, you should write your own version Select, or it doesn't support selection at all.
"""
expandCollapsePattern = self.GetExpandCollapsePattern()
if expandCollapsePattern:
expandCollapsePattern.Expand()
else:
# Windows Form's ComboBoxControl doesn't support ExpandCollapsePattern
self.Click(x=-10, ratioY=0.5, simulateMove=simulateMove)
find = False
if condition:
listItemControl = self.ListItemControl(Compare=lambda c, d: condition(c.Name))
else:
listItemControl = self.ListItemControl(Name=itemName)
if listItemControl.Exists(1):
scrollItemPattern = listItemControl.GetScrollItemPattern()
if scrollItemPattern:
scrollItemPattern.ScrollIntoView(waitTime=0.1)
listItemControl.Click(simulateMove=simulateMove, waitTime=waitTime)
find = True
else:
# some ComboBox's popup window is a child of root control
listControl = ListControl(searchDepth=1)
if listControl.Exists(1):
if condition:
listItemControl = listControl.ListItemControl(
Compare=lambda c, d: condition(c.Name)
)
else:
listItemControl = listControl.ListItemControl(Name=itemName)
if listItemControl.Exists(0, 0):
scrollItemPattern = listItemControl.GetScrollItemPattern()
if scrollItemPattern:
scrollItemPattern.ScrollIntoView(waitTime=0.1)
listItemControl.Click(simulateMove=simulateMove, waitTime=waitTime)
find = True
if not find:
if expandCollapsePattern:
expandCollapsePattern.Collapse(waitTime)
else:
self.Click(x=-10, ratioY=0.5, simulateMove=simulateMove, waitTime=waitTime)
return find
class CustomControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.CustomControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
class DataGridControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.DataGridControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetGridPattern(self) -> GridPattern:
"""
Return `GridPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.GridPattern)
def GetScrollPattern(self) -> ScrollPattern:
"""
Return `ScrollPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollPattern)
def GetSelectionPattern(self) -> SelectionPattern:
"""
Return `SelectionPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionPattern)
def GetTablePattern(self) -> TablePattern:
"""
Return `TablePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TablePattern)
class DataItemControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.DataItemControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetSelectionItemPattern(self) -> SelectionItemPattern:
"""
Return `SelectionItemPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionItemPattern)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetGridItemPattern(self) -> GridItemPattern:
"""
Return `GridItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.GridItemPattern)
def GetScrollItemPattern(self) -> ScrollItemPattern:
"""
Return `ScrollItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollItemPattern)
def GetTableItemPattern(self) -> TableItemPattern:
"""
Return `TableItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TableItemPattern)
def GetTogglePattern(self) -> TogglePattern:
"""
Return `TogglePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TogglePattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
class DocumentControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.DocumentControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetTextPattern(self) -> TextPattern:
"""
Return `TextPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.TextPattern)
def GetScrollPattern(self) -> ScrollPattern:
"""
Return `ScrollPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollPattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
class EditControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.EditControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetRangeValuePattern(self) -> RangeValuePattern:
"""
Return `RangeValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.RangeValuePattern)
def GetTextPattern(self) -> TextPattern:
"""
Return `TextPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TextPattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
class GroupControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.GroupControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
class HeaderControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.HeaderControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetTransformPattern(self) -> TransformPattern:
"""
Return `TransformPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TransformPattern)
class HeaderItemControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.HeaderItemControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetInvokePattern(self) -> InvokePattern:
"""
Return `InvokePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.InvokePattern)
def GetTransformPattern(self) -> TransformPattern:
"""
Return `TransformPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TransformPattern)
class HyperlinkControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.HyperlinkControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetInvokePattern(self) -> InvokePattern:
"""
Return `InvokePattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.InvokePattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
class ImageControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ImageControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetGridItemPattern(self) -> GridItemPattern:
"""
Return `GridItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.GridItemPattern)
def GetTableItemPattern(self) -> TableItemPattern:
"""
Return `TableItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TableItemPattern)
class ListControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ListControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetGridPattern(self) -> GridPattern:
"""
Return `GridPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.GridPattern)
def GetMultipleViewPattern(self) -> MultipleViewPattern:
"""
Return `MultipleViewPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.MultipleViewPattern)
def GetScrollPattern(self) -> ScrollPattern:
"""
Return `ScrollPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollPattern)
def GetSelectionPattern(self) -> SelectionPattern:
"""
Return `SelectionPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionPattern)
class ListItemControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ListItemControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetSelectionItemPattern(self) -> SelectionItemPattern:
"""
Return `SelectionItemPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionItemPattern)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetGridItemPattern(self) -> GridItemPattern:
"""
Return `GridItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.GridItemPattern)
def GetInvokePattern(self) -> InvokePattern:
"""
Return `InvokePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.InvokePattern)
def GetScrollItemPattern(self) -> ScrollItemPattern:
"""
Return `ScrollItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollItemPattern)
def GetTogglePattern(self) -> TogglePattern:
"""
Return `TogglePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TogglePattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
class MenuControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.MenuControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
class MenuBarControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.MenuBarControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetDockPattern(self) -> DockPattern:
"""
Return `DockPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.DockPattern)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetTransformPattern(self) -> TransformPattern:
"""
Return `TransformPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TransformPattern)
class MenuItemControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.MenuItemControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetInvokePattern(self) -> InvokePattern:
"""
Return `InvokePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.InvokePattern)
def GetSelectionItemPattern(self) -> SelectionItemPattern:
"""
Return `SelectionItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionItemPattern)
def GetTogglePattern(self) -> TogglePattern:
"""
Return `TogglePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TogglePattern)
class TopLevel:
"""Class TopLevel"""
def SetTopmost(self, isTopmost: bool = True, waitTime: float = OPERATION_WAIT_TIME) -> bool:
"""
Set top level window topmost.
isTopmost: bool.
waitTime: float.
"""
if self.IsTopLevel():
ret = SetWindowTopmost(self.NativeWindowHandle, isTopmost)
time.sleep(waitTime)
return ret
return False
def IsTopmost(self) -> bool:
if self.IsTopLevel():
WS_EX_TOPMOST = 0x00000008
return bool(GetWindowLong(self.NativeWindowHandle, GWL.ExStyle) & WS_EX_TOPMOST)
return False
def SwitchToThisWindow(self, waitTime: float = OPERATION_WAIT_TIME) -> None:
if self.IsTopLevel():
SwitchToThisWindow(self.NativeWindowHandle)
time.sleep(waitTime)
def Maximize(self, waitTime: float = OPERATION_WAIT_TIME) -> bool:
"""
Set top level window maximize.
"""
if self.IsTopLevel():
return self.ShowWindow(SW.ShowMaximized, waitTime)
return False
def IsMaximize(self) -> bool:
if self.IsTopLevel():
return bool(IsZoomed(self.NativeWindowHandle))
return False
def Minimize(self, waitTime: float = OPERATION_WAIT_TIME) -> bool:
if self.IsTopLevel():
return self.ShowWindow(SW.Minimize, waitTime)
return False
def IsMinimize(self) -> bool:
if self.IsTopLevel():
return bool(IsIconic(self.NativeWindowHandle))
return False
def Restore(self, waitTime: float = OPERATION_WAIT_TIME) -> bool:
"""
Restore window to normal state.
Similar to SwitchToThisWindow.
"""
if self.IsTopLevel():
return self.ShowWindow(SW.Restore, waitTime)
return False
def MoveToCenter(self) -> bool:
"""
Move window to screen center.
"""
if self.IsTopLevel():
rect = self.BoundingRectangle
screenWidth, screenHeight = GetScreenSize()
x, y = (
(screenWidth - rect.width()) // 2,
(screenHeight - rect.height()) // 2,
)
if x < 0:
x = 0
if y < 0:
y = 0
return SetWindowPos(self.NativeWindowHandle, SWP.HWND_Top, x, y, 0, 0, SWP.SWP_NoSize)
return False
def SetActive(self, waitTime: float = OPERATION_WAIT_TIME) -> bool:
"""Set top level window active."""
if self.IsTopLevel():
handle = self.NativeWindowHandle
if IsIconic(handle):
ret = ShowWindow(handle, SW.Restore)
elif not IsWindowVisible(handle):
ret = ShowWindow(handle, SW.Show)
ret = SetForegroundWindow(
handle
) # may fail if foreground windows's process is not python
time.sleep(waitTime)
return ret
return False
class PaneControl(Control, TopLevel):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.PaneControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetDockPattern(self) -> DockPattern:
"""
Return `DockPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.DockPattern)
def GetScrollPattern(self) -> ScrollPattern:
"""
Return `ScrollPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollPattern)
def GetTransformPattern(self) -> TransformPattern:
"""
Return `TransformPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TransformPattern)
class ProgressBarControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ProgressBarControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetRangeValuePattern(self) -> RangeValuePattern:
"""
Return `RangeValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.RangeValuePattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
class RadioButtonControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.RadioButtonControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetSelectionItemPattern(self) -> SelectionItemPattern:
"""
Return `SelectionItemPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionItemPattern)
class ScrollBarControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ScrollBarControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetRangeValuePattern(self) -> RangeValuePattern:
"""
Return `RangeValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.RangeValuePattern)
class SemanticZoomControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.SemanticZoomControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
class SeparatorControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.SeparatorControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
class SliderControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.SliderControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetRangeValuePattern(self) -> RangeValuePattern:
"""
Return `RangeValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.RangeValuePattern)
def GetSelectionPattern(self) -> SelectionPattern:
"""
Return `SelectionPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionPattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
class SpinnerControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.SpinnerControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetRangeValuePattern(self) -> RangeValuePattern:
"""
Return `RangeValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.RangeValuePattern)
def GetSelectionPattern(self) -> SelectionPattern:
"""
Return `SelectionPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionPattern)
def GetValuePattern(self) -> ValuePattern:
"""
Return `ValuePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ValuePattern)
class SplitButtonControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.SplitButtonControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetInvokePattern(self) -> InvokePattern:
"""
Return `InvokePattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.InvokePattern)
class StatusBarControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.StatusBarControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetGridPattern(self) -> GridPattern:
"""
Return `GridPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.GridPattern)
class TabControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.TabControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetSelectionPattern(self) -> SelectionPattern:
"""
Return `SelectionPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionPattern)
def GetScrollPattern(self) -> ScrollPattern:
"""
Return `ScrollPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollPattern)
class TabItemControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.TabItemControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetSelectionItemPattern(self) -> SelectionItemPattern:
"""
Return `SelectionItemPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionItemPattern)
class TableControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.TableControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetGridPattern(self) -> GridPattern:
"""
Return `GridPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.GridPattern)
def GetGridItemPattern(self) -> GridItemPattern:
"""
Return `GridItemPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.GridItemPattern)
def GetTablePattern(self) -> TablePattern:
"""
Return `TablePattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.TablePattern)
def GetTableItemPattern(self) -> TableItemPattern:
"""
Return `TableItemPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.TableItemPattern)
def GetTableItemsValue(self, row: int = -1, column: int = -1):
"""
Get the value of a table
row: int. Position of the row in the table
column: int. Position of the column in the table
Return a list with values in the table.
If a row and column is specified, return a cell value.
If only a row is specified, return a list with row values
"""
table = []
for item in self.GetChildren():
table.append([cell.GetLegacyIAccessiblePattern().Value for cell in item.GetChildren()])
if row > 0 and column > 0:
return table[row][column]
if row > 0:
return table[row]
return table
class TextControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.TextControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetGridItemPattern(self) -> GridItemPattern:
"""
Return `GridItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.GridItemPattern)
def GetTableItemPattern(self) -> TableItemPattern:
"""
Return `TableItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TableItemPattern)
def GetTextPattern(self) -> TextPattern:
"""
Return `TextPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TextPattern)
class ThumbControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ThumbControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetTransformPattern(self) -> TransformPattern:
"""
Return `TransformPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.TransformPattern)
class TitleBarControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.TitleBarControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
class ToolBarControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ToolBarControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetDockPattern(self) -> DockPattern:
"""
Return `DockPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.DockPattern)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetTransformPattern(self) -> TransformPattern:
"""
Return `TransformPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TransformPattern)
class ToolTipControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.ToolTipControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetTextPattern(self) -> TextPattern:
"""
Return `TextPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TextPattern)
def GetWindowPattern(self) -> WindowPattern:
"""
Return `WindowPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.WindowPattern)
class TreeControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.TreeControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetScrollPattern(self) -> ScrollPattern:
"""
Return `ScrollPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollPattern)
def GetSelectionPattern(self) -> SelectionPattern:
"""
Return `SelectionPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionPattern)
class TreeItemControl(Control):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.TreeItemControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
def GetExpandCollapsePattern(self) -> ExpandCollapsePattern:
"""
Return `ExpandCollapsePattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.ExpandCollapsePattern)
def GetInvokePattern(self) -> InvokePattern:
"""
Return `InvokePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.InvokePattern)
def GetScrollItemPattern(self) -> ScrollItemPattern:
"""
Return `ScrollItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.ScrollItemPattern)
def GetSelectionItemPattern(self) -> SelectionItemPattern:
"""
Return `SelectionItemPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.SelectionItemPattern)
def GetTogglePattern(self) -> TogglePattern:
"""
Return `TogglePattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.TogglePattern)
class WindowControl(Control, TopLevel):
def __init__(
self,
searchFromControl: Control | None = None,
searchDepth: int = 0xFFFFFFFF,
searchInterval: float = SEARCH_INTERVAL,
foundIndex: int = 1,
element=None,
Name: str | None = None,
SubName: str | None = None,
RegexName: str | None = None,
ClassName: str | None = None,
AutomationId: str | None = None,
Depth: int | None = None,
Compare: Callable[[TreeNode], bool] | None = None,
**searchProperties,
):
Control.__init__(
self,
searchFromControl,
searchDepth,
searchInterval,
foundIndex,
element,
ControlType=ControlType.WindowControl,
Name=Name,
SubName=SubName,
RegexName=RegexName,
ClassName=ClassName,
AutomationId=AutomationId,
Depth=Depth,
Compare=Compare,
**searchProperties,
)
self._DockPattern = None
self._TransformPattern = None
def GetTransformPattern(self) -> TransformPattern:
"""
Return `TransformPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.TransformPattern)
def GetWindowPattern(self) -> WindowPattern:
"""
Return `WindowPattern` if it supports the pattern else None(Must support according to MSDN).
"""
return self.GetPattern(PatternId.WindowPattern)
def GetDockPattern(self) -> DockPattern:
"""
Return `DockPattern` if it supports the pattern else None(Conditional support according to MSDN).
"""
return self.GetPattern(PatternId.DockPattern)
def MetroClose(self, waitTime: float = OPERATION_WAIT_TIME) -> None:
"""
Only work on Windows 8/8.1, if current window is Metro UI.
waitTime: float.
"""
if self.ClassName == METRO_WINDOW_CLASS_NAME:
screenWidth, screenHeight = GetScreenSize()
MoveTo(screenWidth // 2, 0, waitTime=0)
DragDrop(screenWidth // 2, 0, screenWidth // 2, screenHeight, waitTime=waitTime)
else:
pass
ControlConstructors = {
ControlType.AppBarControl: AppBarControl,
ControlType.ButtonControl: ButtonControl,
ControlType.CalendarControl: CalendarControl,
ControlType.CheckBoxControl: CheckBoxControl,
ControlType.ComboBoxControl: ComboBoxControl,
ControlType.CustomControl: CustomControl,
ControlType.DataGridControl: DataGridControl,
ControlType.DataItemControl: DataItemControl,
ControlType.DocumentControl: DocumentControl,
ControlType.EditControl: EditControl,
ControlType.GroupControl: GroupControl,
ControlType.HeaderControl: HeaderControl,
ControlType.HeaderItemControl: HeaderItemControl,
ControlType.HyperlinkControl: HyperlinkControl,
ControlType.ImageControl: ImageControl,
ControlType.ListControl: ListControl,
ControlType.ListItemControl: ListItemControl,
ControlType.MenuBarControl: MenuBarControl,
ControlType.MenuControl: MenuControl,
ControlType.MenuItemControl: MenuItemControl,
ControlType.PaneControl: PaneControl,
ControlType.ProgressBarControl: ProgressBarControl,
ControlType.RadioButtonControl: RadioButtonControl,
ControlType.ScrollBarControl: ScrollBarControl,
ControlType.SemanticZoomControl: SemanticZoomControl,
ControlType.SeparatorControl: SeparatorControl,
ControlType.SliderControl: SliderControl,
ControlType.SpinnerControl: SpinnerControl,
ControlType.SplitButtonControl: SplitButtonControl,
ControlType.StatusBarControl: StatusBarControl,
ControlType.TabControl: TabControl,
ControlType.TabItemControl: TabItemControl,
ControlType.TableControl: TableControl,
ControlType.TextControl: TextControl,
ControlType.ThumbControl: ThumbControl,
ControlType.TitleBarControl: TitleBarControl,
ControlType.ToolBarControl: ToolBarControl,
ControlType.ToolTipControl: ToolTipControl,
ControlType.TreeControl: TreeControl,
ControlType.TreeItemControl: TreeItemControl,
ControlType.WindowControl: WindowControl,
}
class UIAutomationInitializerInThread:
def __init__(self, debug: bool = False):
self.debug = debug
InitializeUIAutomationInCurrentThread()
self.inited = True
if self.debug:
th = threading.currentThread()
print(
"\ncall InitializeUIAutomationInCurrentThread in {}, inited {}".format(
th, self.inited
)
)
def __del__(self):
self.Uninitialize()
def __enter__(self):
return self
def __exit__(self, exceptionType, exceptionValue, exceptionTraceback):
self.Uninitialize()
def Uninitialize(self):
if self.inited:
UninitializeUIAutomationInCurrentThread()
self.inited = False
if self.debug:
th = threading.currentThread()
print("\ncall UninitializeUIAutomationInCurrentThread in {}".format(th))
def InitializeUIAutomationInCurrentThread() -> None:
"""
Initialize UIAutomation in a new thread.
If you want to use functionalities related to Controls and Patterns in a new thread.
You must call this function first in the new thread.
But you can't use use a Control or a Pattern created in a different thread.
So you can't create a Control or a Pattern in main thread and then pass it to a new thread and use it.
"""
comtypes.CoInitializeEx()
def UninitializeUIAutomationInCurrentThread() -> None:
"""
Uninitialize UIAutomation in a new thread after calling InitializeUIAutomationInCurrentThread.
You must call this function when the new thread exits if you have called InitializeUIAutomationInCurrentThread in the same thread.
"""
comtypes.CoUninitialize()
def SetGlobalSearchTimeout(seconds: float) -> None:
"""
seconds: float.
To make this available, you need explicitly import uiautomation:
from uiautomation import uiautomation as auto
auto.SetGlobalSearchTimeout(10)
"""
global TIME_OUT_SECOND
TIME_OUT_SECOND = seconds
def WaitForExist(control: Control, timeout: float) -> bool:
"""
Check if control exists in timeout seconds.
control: `Control` or its subclass.
timeout: float.
Return bool.
"""
return control.Exists(timeout, 1)
def WaitForDisappear(control: Control, timeout: float) -> bool:
"""
Check if control disappears in timeout seconds.
control: `Control` or its subclass.
timeout: float.
Return bool.
"""
return control.Disappears(timeout, 1)
def WalkTree(
top,
getChildren: Callable[[TreeNode], List[TreeNode]] | None = None,
getFirstChild: Callable[[TreeNode], TreeNode] | None = None,
getNextSibling: Callable[[TreeNode], TreeNode] | None = None,
yieldCondition: Callable[[TreeNode, int], bool] | None = None,
includeTop: bool = False,
maxDepth: int = 0xFFFFFFFF,
):
"""
Walk a tree not using recursive algorithm.
top: a tree node.
getChildren: Callable[[TreeNode], List[TreeNode]], function(treeNode: TreeNode) -> List[TreeNode].
getNextSibling: Callable[[TreeNode], TreeNode], function(treeNode: TreeNode) -> TreeNode.
getNextSibling: Callable[[TreeNode], TreeNode], function(treeNode: TreeNode) -> TreeNode.
yieldCondition: Callable[[TreeNode, int], bool], function(treeNode: TreeNode, depth: int) -> bool.
includeTop: bool, if True yield top first.
maxDepth: int, enum depth.
If getChildren is valid, ignore getFirstChild and getNextSibling,
yield 3 items tuple: (treeNode, depth, remain children count in current depth).
If getChildren is not valid, using getFirstChild and getNextSibling,
yield 2 items tuple: (treeNode, depth).
If yieldCondition is not None, only yield tree nodes that yieldCondition(treeNode: TreeNode, depth: int)->bool returns True.
For example:
def GetDirChildren(dir_):
if os.path.isdir(dir_):
return [os.path.join(dir_, it) for it in os.listdir(dir_)]
for it, depth, leftCount in WalkTree('D:\\', getChildren= GetDirChildren):
print(it, depth, leftCount)
"""
if maxDepth <= 0:
return
depth = 0
if getChildren:
if includeTop:
if not yieldCondition or yieldCondition(top, 0):
yield top, 0, 0
children = getChildren(top)
childList = [children]
while depth >= 0: # or while childList:
lastItems = childList[-1]
if lastItems:
if not yieldCondition or yieldCondition(lastItems[0], depth + 1):
yield lastItems[0], depth + 1, len(lastItems) - 1
if depth + 1 < maxDepth:
children = getChildren(lastItems[0])
if children:
depth += 1
childList.append(children)
del lastItems[0]
else:
del childList[depth]
depth -= 1
elif getFirstChild and getNextSibling:
if includeTop:
if not yieldCondition or yieldCondition(top, 0):
yield top, 0
child = getFirstChild(top)
childList = [child]
while depth >= 0: # or while childList:
lastItem = childList[-1]
if lastItem:
if not yieldCondition or yieldCondition(lastItem, depth + 1):
yield lastItem, depth + 1
child = getNextSibling(lastItem)
childList[depth] = child
if depth + 1 < maxDepth:
child = getFirstChild(lastItem)
if child:
depth += 1
childList.append(child)
else:
del childList[depth]
depth -= 1
def GetRootControl() -> PaneControl:
"""
Get root control, the Desktop window.
Return `PaneControl`.
"""
control = Control.CreateControlFromElement(
_AutomationClient.instance().IUIAutomation.GetRootElement()
)
if isinstance(control, PaneControl):
return control
if control is None:
raise AssertionError("Expected valid root element")
raise AssertionError(
"Expected root element to be a PaneControl. Found: %s (%s)" % (type(control), control)
)
def GetFocusedControl() -> Control | None:
"""Return `Control` subclass."""
return Control.CreateControlFromElement(
_AutomationClient.instance().IUIAutomation.GetFocusedElement()
)
def GetForegroundControl() -> Control:
"""Return `Control` subclass."""
return ControlFromHandle(GetForegroundWindow())
# another implement
# focusedControl = GetFocusedControl()
# parentControl = focusedControl
# controlList = []
# while parentControl:
# controlList.insert(0, parentControl)
# parentControl = parentControl.GetParentControl()
# if len(controlList) == 1:
# parentControl = controlList[0]
# else:
# parentControl = controlList[1]
# return parentControl
def GetConsoleWindow() -> WindowControl | None:
"""Return `WindowControl` or None, a console window that runs python."""
consoleWindow = ControlFromHandle(ctypes.windll.kernel32.GetConsoleWindow())
if consoleWindow and consoleWindow.ClassName == "PseudoConsoleWindow":
# Windows Terminal
consoleWindow = consoleWindow.GetParentControl()
return consoleWindow
def ControlFromPoint(x: int, y: int) -> Control | None:
"""
Call IUIAutomation ElementFromPoint x,y. May return None if mouse is over cmd's title bar icon.
Return `Control` subclass or None.
"""
element = _AutomationClient.instance().IUIAutomation.ElementFromPoint(
ctypes.wintypes.POINT(x, y)
)
return Control.CreateControlFromElement(element)
def ControlFromPoint2(x: int, y: int) -> Control | None:
"""
Get a native handle from point x,y and call IUIAutomation.ElementFromHandle.
Return `Control` subclass.
"""
return Control.CreateControlFromElement(
_AutomationClient.instance().IUIAutomation.ElementFromHandle(WindowFromPoint(x, y))
)
def ControlFromCursor() -> Control | None:
"""
Call ControlFromPoint with current cursor point.
Return `Control` subclass.
"""
x, y = GetCursorPos()
return ControlFromPoint(x, y)
def ControlFromCursor2() -> Control | None:
"""
Call ControlFromPoint2 with current cursor point.
Return `Control` subclass.
"""
x, y = GetCursorPos()
return ControlFromPoint2(x, y)
def ControlFromHandle(handle: int) -> Control | None:
"""
Call IUIAutomation.ElementFromHandle with a native handle.
handle: int, a native window handle.
Return `Control` subclass or None.
"""
if handle:
return Control.CreateControlFromElement(
_AutomationClient.instance().IUIAutomation.ElementFromHandle(handle)
)
return None
def ControlsAreSame(control1: Control, control2: Control) -> bool:
"""
control1: `Control` or its subclass.
control2: `Control` or its subclass.
Return bool, True if control1 and control2 represent the same control otherwise False.
"""
return bool(
_AutomationClient.instance().IUIAutomation.CompareElements(
control1.Element, control2.Element
)
)
def WalkControl(
control: Control, includeTop: bool = False, maxDepth: int = 0xFFFFFFFF
) -> Generator[Tuple[Control, int], None, None]:
"""
control: `Control` or its subclass.
includeTop: bool, if True, yield (control, 0) first.
maxDepth: int, enum depth.
Yield 2 items tuple (control: Control, depth: int).
"""
if includeTop:
yield control, 0
if maxDepth <= 0:
return
depth = 0
child = control.GetFirstChildControl()
controlList = [child]
while depth >= 0:
lastControl = controlList[-1]
if lastControl:
yield lastControl, depth + 1
child = lastControl.GetNextSiblingControl()
controlList[depth] = child
if depth + 1 < maxDepth:
child = lastControl.GetFirstChildControl()
if child:
depth += 1
controlList.append(child)
else:
del controlList[depth]
depth -= 1
def LogControl(
control: Control, depth: int = 0, showAllName: bool = True, showPid: bool = False
) -> None:
"""
Print and log control's properties.
control: `Control` or its subclass.
depth: int, current depth.
showAllName: bool, if False, print the first 30 characters of control.Name.
"""
indent = " " * depth * 4
if showPid:
pass
supportedPatterns = list(
filter(
lambda t: t[0],
((control.GetPattern(id_), name) for id_, name in PatternIdNames.items()),
)
)
for pt, name in supportedPatterns:
if isinstance(pt, ValuePattern):
pass
elif isinstance(pt, RangeValuePattern):
pass
elif isinstance(pt, TogglePattern):
pass
elif isinstance(pt, SelectionItemPattern):
pass
elif isinstance(pt, ExpandCollapsePattern):
pass
elif isinstance(pt, ScrollPattern):
pass
elif isinstance(pt, GridPattern):
pass
elif isinstance(pt, GridItemPattern):
pass
elif isinstance(pt, TextPattern):
# issue 49: CEF Control as DocumentControl have no "TextPattern.Text" property, skip log this part.
# https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationtextpattern-get_documentrange
try:
pass
except comtypes.COMError:
pass
for pt, name in supportedPatterns:
pass
def EnumAndLogControl(
control: Control,
maxDepth: int = 0xFFFFFFFF,
showAllName: bool = True,
showPid: bool = False,
startDepth: int = 0,
) -> None:
"""
Print and log control and its descendants' propertyies.
control: `Control` or its subclass.
maxDepth: int, enum depth.
showAllName: bool, if False, print the first 30 characters of control.Name.
startDepth: int, control's current depth.
"""
for c, d in WalkControl(control, True, maxDepth):
LogControl(c, d + startDepth, showAllName, showPid)
def EnumAndLogControlAncestors(
control: Control, showAllName: bool = True, showPid: bool = False
) -> None:
"""
Print and log control and its ancestors' propertyies.
control: `Control` or its subclass.
showAllName: bool, if False, print the first 30 characters of control.Name.
"""
curr = control
lists = []
while curr:
lists.insert(0, curr)
curr = curr.GetParentControl()
for i, curr in enumerate(lists):
LogControl(curr, i, showAllName, showPid)
def FindControl(
control: Control | None,
compare: Callable[[Control, int], bool],
maxDepth: int = 0xFFFFFFFF,
findFromSelf: bool = False,
foundIndex: int = 1,
) -> Control | None:
"""
control: `Control` or its subclass.
compare: Callable[[Control, int], bool], function(control: Control, depth: int) -> bool.
maxDepth: int, enum depth.
findFromSelf: bool, if False, do not compare self.
foundIndex: int, starts with 1, >= 1.
Return `Control` subclass or None if not find.
"""
foundCount = 0
if not control:
control = GetRootControl()
traverseCount = 0
for child, depth in WalkControl(control, findFromSelf, maxDepth):
traverseCount += 1
if compare(child, depth):
foundCount += 1
if foundCount == foundIndex:
child.traverseCount = traverseCount
return child
return None
def ShowDesktop(waitTime: float = 1) -> None:
"""Show Desktop by pressing win + d"""
SendKeys("{Win}d", waitTime=waitTime)
# another implement
# paneTray = PaneControl(searchDepth = 1, ClassName = 'Shell_TrayWnd')
# if paneTray.Exists():
# WM_COMMAND = 0x111
# MIN_ALL = 419
# MIN_ALL_UNDO = 416
# PostMessage(paneTray.NativeWindowHandle, WM_COMMAND, MIN_ALL, 0)
# time.sleep(1)
def WaitHotKeyReleased(hotkey: Tuple[int, int]) -> None:
"""hotkey: Tuple[int, int], two ints tuple (modifierKey, key)"""
mod = {
ModifierKey.Alt: Keys.VK_MENU,
ModifierKey.Control: Keys.VK_CONTROL,
ModifierKey.Shift: Keys.VK_SHIFT,
ModifierKey.Win: Keys.VK_LWIN,
}
while True:
time.sleep(0.05)
if IsKeyPressed(hotkey[1]):
continue
for k, v in mod.items():
if k & hotkey[0]:
if IsKeyPressed(v):
break
else:
break
def RunByHotKey(
keyFunctions: Dict[Tuple[int, int], Callable],
stopHotKey: Tuple[int, int] | None = None,
exitHotKey: Tuple[int, int] = (ModifierKey.Control, Keys.VK_D),
waitHotKeyReleased: bool = True,
) -> None:
"""
Bind functions with hotkeys, the function will be run or stopped in another thread when the hotkey is pressed.
keyFunctions: Dict[Tuple[int, int], Callable], such as {(uiautomation.ModifierKey.Control, uiautomation.Keys.VK_1) : function}
stopHotKey: hotkey tuple
exitHotKey: hotkey tuple
waitHotKeyReleased: bool, if True, hotkey function will be triggered after the hotkey is released
def main(stopEvent):
while True:
if stopEvent.is_set(): # must check stopEvent.is_set() if you want to stop when stop hotkey is pressed
break
print(n)
n += 1
stopEvent.wait(1)
print('main exit')
uiautomation.RunByHotKey({(uiautomation.ModifierKey.Control, uiautomation.Keys.VK_1) : main}
, (uiautomation.ModifierKey.Control | uiautomation.ModifierKey.Shift, uiautomation.Keys.VK_2))
"""
import traceback
def getModName(theDict, theValue):
name = ""
for key in theDict:
if isinstance(theDict[key], int) and theValue & theDict[key]:
if name:
name += "|"
name += key
return name
def releaseAllKeys():
for key, value in Keys.__dict__.items():
if isinstance(value, int) and key.startswith("VK"):
if IsKeyPressed(value):
ReleaseKey(value)
def threadFunc(function, stopEvent, hotkey, hotkeyName):
if waitHotKeyReleased:
WaitHotKeyReleased(hotkey)
try:
function(stopEvent)
except Exception as ex:
print(traceback.format_exc())
finally:
releaseAllKeys() # need to release keys if some keys were pressed
stopHotKeyId = 1
exitHotKeyId = 2
hotKeyId = 3
registed = True
id2HotKey = {}
id2Function = {}
id2Thread = {}
id2Name = {}
for hotkey in keyFunctions:
id2HotKey[hotKeyId] = hotkey
id2Function[hotKeyId] = keyFunctions[hotkey]
id2Thread[hotKeyId] = None
modName = getModName(ModifierKey.__dict__, hotkey[0])
keyName = _GetDictKeyName(Keys.__dict__, hotkey[1])
id2Name[hotKeyId] = str((modName, keyName))
if ctypes.windll.user32.RegisterHotKey(0, hotKeyId, hotkey[0], hotkey[1]):
pass
else:
registed = False
hotKeyId += 1
if stopHotKey and len(stopHotKey) == 2:
modName = getModName(ModifierKey.__dict__, stopHotKey[0])
keyName = _GetDictKeyName(Keys.__dict__, stopHotKey[1])
if ctypes.windll.user32.RegisterHotKey(0, stopHotKeyId, stopHotKey[0], stopHotKey[1]):
pass
else:
registed = False
if not registed:
return
if exitHotKey and len(exitHotKey) == 2:
modName = getModName(ModifierKey.__dict__, exitHotKey[0])
keyName = _GetDictKeyName(Keys.__dict__, exitHotKey[1])
if ctypes.windll.user32.RegisterHotKey(0, exitHotKeyId, exitHotKey[0], exitHotKey[1]):
pass
else:
pass
funcThread = None
livingThreads = []
stopEvent = threading.Event()
msg = ctypes.wintypes.MSG()
while (
ctypes.windll.user32.GetMessageW(
ctypes.byref(msg), ctypes.c_void_p(0), ctypes.c_uint(0), ctypes.c_uint(0)
)
!= 0
):
if msg.message == 0x0312: # WM_HOTKEY=0x0312
if msg.wParam in id2HotKey:
if (
msg.lParam & 0x0000FFFF == id2HotKey[msg.wParam][0]
and msg.lParam >> 16 & 0x0000FFFF == id2HotKey[msg.wParam][1]
):
if not id2Thread[msg.wParam]:
stopEvent.clear()
funcThread = threading.Thread(
None,
threadFunc,
args=(
id2Function[msg.wParam],
stopEvent,
id2HotKey[msg.wParam],
id2Name[msg.wParam],
),
)
funcThread.start()
id2Thread[msg.wParam] = funcThread
else:
if id2Thread[msg.wParam].is_alive():
pass
else:
stopEvent.clear()
funcThread = threading.Thread(
None,
threadFunc,
args=(
id2Function[msg.wParam],
stopEvent,
id2HotKey[msg.wParam],
id2Name[msg.wParam],
),
)
funcThread.start()
id2Thread[msg.wParam] = funcThread
elif stopHotKeyId == msg.wParam:
if (
msg.lParam & 0x0000FFFF == stopHotKey[0]
and msg.lParam >> 16 & 0x0000FFFF == stopHotKey[1]
):
stopEvent.set()
for id_ in id2Thread:
if id2Thread[id_]:
if id2Thread[id_].is_alive():
livingThreads.append((id2Thread[id_], id2Name[id_]))
id2Thread[id_] = None
elif exitHotKeyId == msg.wParam:
if (
msg.lParam & 0x0000FFFF == exitHotKey[0]
and msg.lParam >> 16 & 0x0000FFFF == exitHotKey[1]
):
stopEvent.set()
for id_ in id2Thread:
if id2Thread[id_]:
if id2Thread[id_].is_alive():
livingThreads.append((id2Thread[id_], id2Name[id_]))
id2Thread[id_] = None
break
for thread, hotkeyName in livingThreads:
if thread.is_alive():
thread.join(2)
exit()