Spaces:
Running
Running
| import platform | |
| from openspace.utils.logging import Logger | |
| from typing import Dict, Any, Optional | |
| logger = Logger.get_logger(__name__) | |
| platform_name = platform.system() | |
| class AccessibilityHelper: | |
| def __init__(self): | |
| self.platform = platform_name | |
| self.adapter = None | |
| try: | |
| if platform_name == "Darwin": | |
| from ..platform_adapters.macos_adapter import MacOSAdapter | |
| self.adapter = MacOSAdapter() | |
| elif platform_name == "Linux": | |
| from ..platform_adapters.linux_adapter import LinuxAdapter | |
| self.adapter = LinuxAdapter() | |
| elif platform_name == "Windows": | |
| from ..platform_adapters.windows_adapter import WindowsAdapter | |
| self.adapter = WindowsAdapter() | |
| except ImportError as e: | |
| logger.warning(f"Failed to import platform adapter: {e}") | |
| def get_tree(self, max_depth: int = 10) -> Dict[str, Any]: | |
| if not self.adapter: | |
| return { | |
| 'error': f'No adapter available for {self.platform}', | |
| 'platform': self.platform | |
| } | |
| try: | |
| return self.adapter.get_accessibility_tree(max_depth=max_depth) | |
| except Exception as e: | |
| logger.error(f"Failed to get accessibility tree: {e}") | |
| return { | |
| 'error': str(e), | |
| 'platform': self.platform | |
| } | |
| def is_available(self) -> bool: | |
| return self.adapter is not None and hasattr(self.adapter, 'available') and self.adapter.available | |
| def find_element_by_name(self, tree: Dict[str, Any], name: str) -> Optional[Dict[str, Any]]: | |
| if not tree or 'tree' not in tree: | |
| return None | |
| return self._search_tree(tree['tree'], 'name', name) | |
| def find_element_by_role(self, tree: Dict[str, Any], role: str) -> Optional[Dict[str, Any]]: | |
| if not tree or 'tree' not in tree: | |
| return None | |
| return self._search_tree(tree['tree'], 'role', role) | |
| def _search_tree(self, node: Dict[str, Any], key: str, value: str) -> Optional[Dict[str, Any]]: | |
| if not node: | |
| return None | |
| # Check current node | |
| if key in node and node[key] == value: | |
| return node | |
| # Recursively search child nodes | |
| if 'children' in node: | |
| for child in node['children']: | |
| result = self._search_tree(child, key, value) | |
| if result: | |
| return result | |
| return None | |
| def flatten_tree(self, tree: Dict[str, Any]) -> list: | |
| if not tree or 'tree' not in tree: | |
| return [] | |
| result = [] | |
| self._flatten_node(tree['tree'], result) | |
| return result | |
| def _flatten_node(self, node: Dict[str, Any], result: list): | |
| """Recursively flatten nodes""" | |
| if not node: | |
| return | |
| # Add current node (remove children) | |
| node_copy = {k: v for k, v in node.items() if k != 'children'} | |
| result.append(node_copy) | |
| # Recursively process child nodes | |
| if 'children' in node: | |
| for child in node['children']: | |
| self._flatten_node(child, result) | |
| def get_visible_elements(self, tree: Dict[str, Any]) -> list: | |
| all_elements = self.flatten_tree(tree) | |
| visible = [] | |
| for element in all_elements: | |
| if self.platform == "Linux": | |
| if 'states' in element and 'showing' in element.get('states', []): | |
| visible.append(element) | |
| elif self.platform == "Darwin": | |
| if element.get('enabled', False): | |
| visible.append(element) | |
| elif self.platform == "Windows": | |
| if element.get('states', {}).get('is_visible', False): | |
| visible.append(element) | |
| return visible | |
| def get_clickable_elements(self, tree: Dict[str, Any]) -> list: | |
| all_elements = self.flatten_tree(tree) | |
| clickable_roles = [ | |
| 'button', 'push-button', 'toggle-button', 'radio-button', | |
| 'link', 'menu-item', 'AXButton', 'AXLink', 'AXMenuItem' | |
| ] | |
| clickable = [] | |
| for element in all_elements: | |
| role = element.get('role', '').lower() | |
| if any(cr in role for cr in clickable_roles): | |
| clickable.append(element) | |
| return clickable | |
| def get_statistics(self, tree: Dict[str, Any]) -> Dict[str, Any]: | |
| all_elements = self.flatten_tree(tree) | |
| # Count roles | |
| roles = {} | |
| for element in all_elements: | |
| role = element.get('role', 'unknown') | |
| roles[role] = roles.get(role, 0) + 1 | |
| return { | |
| 'total_elements': len(all_elements), | |
| 'visible_elements': len(self.get_visible_elements(tree)), | |
| 'clickable_elements': len(self.get_clickable_elements(tree)), | |
| 'roles': roles, | |
| 'platform': self.platform | |
| } | |