OpenSpace / openspace /local_server /utils /accessibility.py
darkfire514's picture
Upload 160 files
399b80c verified
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
}