ProjectOpenTARS4's picture
Mirror of openTARS 5.2 Ranger (revision 2026110101)
2695555 verified
Raw History Blame Contribute Delete
5.43 kB
"""Acessibilidade no macOS pela API AX (pyobjc ApplicationServices), para o tars_acessibilidade: devolve os mesmos nomes
de papel e de estado do AT-SPI, então a varredura, a busca pelo nome e o que a IA vê são os do Linux. Precisa que a pessoa
libere o openTARS em Ajustes > Privacidade e Segurança > Acessibilidade (o macOS pergunta na primeira vez)."""
PAPEIS = {
"AXButton": "push-button", "AXMenuButton": "push-button", "AXCheckBox": "check-box", "AXRadioButton": "radio-button",
"AXMenuItem": "menu-item", "AXMenuBarItem": "menu", "AXMenuBar": "menu-bar", "AXMenu": "panel", "AXLink": "link",
"AXPopUpButton": "combo-box", "AXComboBox": "combo-box", "AXRow": "list-item", "AXCell": "table-cell",
"AXIncrementor": "spin-button", "AXSlider": "slider", "AXDisclosureTriangle": "toggle-button", "AXTextField": "entry",
"AXTextArea": "text", "AXStaticText": "label", "AXHeading": "heading", "AXWindow": "frame", "AXSheet": "dialog",
"AXGroup": "panel", "AXScrollArea": "scroll-pane", "AXToolbar": "tool-bar", "AXWebArea": "document-web",
"AXList": "list", "AXTable": "table", "AXOutline": "tree", "AXImage": "image", "AXTabGroup": "page-tab-list",
"AXSplitGroup": "split-pane",
}
SUBPAPEIS = {"AXSecureTextField": "password-text", "AXSearchField": "search-box", "AXTabButton": "page-tab",
"AXOutlineRow": "tree-item", "AXSwitch": "switch"}
ACOES = ("AXPress", "AXPick", "AXConfirm", "AXOpen", "AXShowMenu")
def _ax():
import ApplicationServices
return ApplicationServices
def _attr(no, nome):
erro, valor = _ax().AXUIElementCopyAttributeValue(no, nome, None)
return valor if erro == 0 else None
def erro():
try:
AX = _ax()
except ImportError as e:
return f"pyobjc ApplicationServices missing ({e}); reinstall openTARS"
# na primeira vez o macOS mostra o pedido de permissão (uma vez por abertura do openTARS)
if not AX.AXIsProcessTrustedWithOptions({AX.kAXTrustedCheckOptionPrompt: True}):
return ("macOS has not allowed openTARS in Accessibility yet (System Settings > Privacy & Security > "
"Accessibility: turn on openTARS, then try again)")
return None
def janelas():
from AppKit import NSApplicationActivationPolicyRegular, NSWorkspace
AX = _ax()
saida = []
for app in NSWorkspace.sharedWorkspace().runningApplications():
if app.activationPolicy() != NSApplicationActivationPolicyRegular or app.isHidden():
continue
pid = app.processIdentifier()
elemento = AX.AXUIElementCreateApplication(pid)
AX.AXUIElementSetMessagingTimeout(elemento, 1.0) # app travado não trava o openTARS
for janela in _attr(elemento, "AXWindows") or []:
if _attr(janela, "AXMinimized"):
continue
saida.append({"no": janela, "app": str(app.localizedName() or ""), "pid": pid,
"titulo": str(_attr(janela, "AXTitle") or ""),
"ativa": bool(app.isActive() and _attr(janela, "AXMain"))})
return saida
def papel(no):
sub = _attr(no, "AXSubrole")
if sub in SUBPAPEIS:
return SUBPAPEIS[sub]
if sub in ("AXDialog", "AXSystemDialog"):
return "dialog"
return PAPEIS.get(str(_attr(no, "AXRole") or ""), "unknown")
def nome(no):
for atributo in ("AXTitle", "AXDescription"):
valor = _attr(no, atributo)
if valor:
return str(valor)
if _attr(no, "AXRole") == "AXStaticText":
return str(_attr(no, "AXValue") or "")
return ""
def descricao(no):
return str(_attr(no, "AXHelp") or "")
def estados(no):
saida = {"showing", "visible"} # a AX não diz o que está fora da tela
if _attr(no, "AXEnabled") is not False:
saida |= {"sensitive", "enabled"}
if _attr(no, "AXFocused"):
saida.add("focused")
if _attr(no, "AXRole") in ("AXTextField", "AXTextArea"):
erro, pode = _ax().AXUIElementIsAttributeSettable(no, "AXValue", None)
if erro == 0 and pode:
saida.add("editable")
return saida
def filhos(no):
return list(_attr(no, "AXChildren") or [])
def texto(no):
valor = _attr(no, "AXValue")
return str(valor) if isinstance(valor, str) else ""
def acionar(no):
AX = _ax()
erro, nomes = AX.AXUIElementCopyActionNames(no, None)
nomes = list(nomes or []) if erro == 0 else []
acao = next((a for a in ACOES if a in nomes), None)
if acao:
return AX.AXUIElementPerformAction(no, acao) == 0
erro, pode = AX.AXUIElementIsAttributeSettable(no, "AXSelected", None)
if erro == 0 and pode: # linha de lista sem ação: selecionar é o "clique"
return AX.AXUIElementSetAttributeValue(no, "AXSelected", True) == 0
return False
def retangulo(no):
AX = _ax()
posicao, tamanho = _attr(no, "AXPosition"), _attr(no, "AXSize")
if posicao is None or tamanho is None:
return None
_ok, ponto = AX.AXValueGetValue(posicao, AX.kAXValueCGPointType, None)
_ok, caixa = AX.AXValueGetValue(tamanho, AX.kAXValueCGSizeType, None)
return (int(ponto.x), int(ponto.y), int(caixa.width), int(caixa.height)) # pontos da tela, os mesmos do pyautogui
def escrever(no, valor):
return _ax().AXUIElementSetAttributeValue(no, "AXValue", valor) == 0
def focar(no):
_ax().AXUIElementSetAttributeValue(no, "AXFocused", True)