Download app/tars_plataforma/ax.py from OpenTARS/openTARS: direct link, hf CLI and curl.
- Browser
- Download file 5.43 kB
-
https://huggingface.co/OpenTARS/openTARS/resolve/main/app/tars_plataforma/ax.py
- Command line
-
hf download hf://OpenTARS/openTARS/app/tars_plataforma/ax.py
-
curl -L -o ax.py https://huggingface.co/OpenTARS/openTARS/resolve/main/app/tars_plataforma/ax.py
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) | |