+++ /dev/null
-import dbus
-
-class AccessibleObjectDoesNotExist(Exception):
- def __init__(self, path):
- self.path = path
-
- def __str__(self):
- return "Object %s does not exist" % (self.path)
-
-class AccessibleTreeCache():
- """
- Caches a collection of Accessible Objects.
- """
-
- _TREE_INTERFACE = 'org.freedesktop.atspi.Tree'
- _UPDATE_SIGNAL = 'updateTree'
-
- def __init__(self, connection, busName, objectStorePath):
- """
- Creates a cache for accessible objects.
-
- All accessible objects are created and accessed through this cache.
-
- Parameters:
- connection - DBus connection.
- busName - DBus bus name where accessible tree resides.
- objectStorePath - Path where the accessible tree can be accessed.
- """
- storeObject = connection.get_object(busName, objectStorePath)
-
- self._busName = busName
- self._accessibleStore = dbus.Interface(storeObject, self._TREE_INTERFACE)
- self._objects = {}
- self._root = self._accessibleStore.getRoot()
-
- self._updateObjects(self._accessibleStore.getTree())
-
- #Connect to update signal
- self._signalMatch = self._accessibleStore.connect_to_signal(self._UPDATE_SIGNAL,
- self._updateHandler)
-
- def getRootAccessible(self):
- """
- Gets the accessible object at the root of the tree.
- """
- return self.getAccessible(self._root)
-
- def getAccessible(self, objectPath):
- """
- Gets the accessible object for the given object path.
- """
- if objectPath in self._objects:
- return self._objects[objectPath]
- else:
- raise AccessibleObjectDoesNotExist(objectPath)
-
- def _updateObjects(self, objects):
- """
- Updates the object cache from an
- array of wire format Accessible objects.
- """
- for object in objects:
- (path,
- parent,
- children,
- interfaces,
- name,
- role,
- description) = object
- if path in self._objects:
- self._objects[path].update(path,
- parent,
- children,
- interfaces,
- name,
- role,
- description)
- else:
- acc = AccessibleObjectProxy(self,
- self._busName,
- path,
- parent,
- children,
- interfaces,
- name,
- role,
- description)
- self._objects[path] = acc;
-
- def _removeObjects(self, paths):
- for path in paths:
- #Probably want to set object as invalid first
- del(self._objects[path])
-
- def _updateHandler(self, updates):
- objects, paths = updates
- self._removeObjects(paths)
- self._updateObjects(objects)
-
-class AccessibleObjectProxy():
- """
- A D-Bus proxy for an element that implements one or more of the AT-SPI
- Accessibility interfaces.
- """
-
- def __init__(self, cache, bus,
- path, parent,
- children, interfaces,
- name, role, description):
- """
- Create an accessible object.
-
- Parameters:
-
- cache - The accessible cache that this object is owned by.
- bus - Bus name where proxied object can be located.
- path - The D-Bus object path of object proxied by this object.
- parent - The parent accessible or '/' if the root object.
- children - List of child accessible objects.
- interfaces - List of interfaces supported by this object.
- name - Name of the accessible.
- role - The accessibles role.
- description - Description of the accessible
- """
- self._cache = cache
- self.bus = bus
- self.update(path, parent, children, interfaces, name, role, description)
-
- def update(self, path, parent,
- children, interfaces,
- name, role, description):
- """
- update an accessible object.
-
- Parameters:
-
- path - The D-Bus object path of object proxied by this object.
- parent - The parent accessible or '/' if the root object.
- children - List of child accessible objects.
- interfaces - List of interfaces supported by this object.
- name - Name of the accessible.
- role - The accessibles role.
- description - Description of the accessible
- """
- self.path = path
- self.parent = parent
- self.children = children
- self.interfaces = interfaces
- self.name = name
- self.role = role
- self.description = description
-
- def getChildren(self):
- return [self._cache.getAccessible(path) for path in self.children]
--- /dev/null
+#Copyright (C) 2008 Codethink Ltd
+
+#his program is free software; you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation; either version 2 of the License, or
+#(at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import dbus
+
+from weakref import ref
+from dbus.proxies import Interface, ProxyObject
+
+ATSPI_ACCESSIBLE = 'org.freedesktop.atspi.Accessible'
+ATSPI_ACCESSIBLE_TREE = 'org.freedesktop.atspi.Tree'
+
+class AccessibleObjectDoesNotExist(Exception):
+ pass
+
+class AccessibleInterfaceNotSupported(Exception):
+ pass
+
+class _CacheData(object):
+ __slots__ = [
+ 'parent',
+ 'interfaces',
+ 'children',
+ 'role',
+ 'name',
+ 'description',
+ 'proxy'
+ ]
+
+ def __init__(self, data):
+ self.update(data)
+ self.proxy = None
+
+ def update(self, data):
+ #Don't cache the path here, used as lookup in cache object dict.
+ (path,
+ self.parent,
+ self.children,
+ self.interfaces,
+ self.name,
+ self.role,
+ self.description) = data
+
+class AccessibleCache(object):
+ """
+ Caches data for a collection of accessible object proxies.
+
+ The cache only holds a weak reference to any D-Bus proxies,
+ they are created on demand.
+ """
+
+ _UPDATE_SIGNAL = 'updateTree'
+
+ def __init__(self, connection, busName, objectStorePath):
+ """
+ Creates a cache for accessible object data.
+
+ All accessible object proxies are created and accessed through this cache.
+
+ connection - DBus connection.
+ busName - DBus bus name where accessible tree resides.
+ objectStorePath - Path where the accessible tree can be accessed.
+ """
+ storeObject = connection.get_object(busName, objectStorePath)
+
+ self._connection = connection
+ self._busName = busName
+ self._accessibleStore = dbus.Interface(storeObject, ATSPI_ACCESSIBLE_TREE)
+ self._objects = {}
+ #TODO are we caching the root accessible or not?
+ #Do we need a roundtrip to access this?
+ #Does the root accessible ever change?
+ #Probably not as the root accessible is the 'Application'
+ self._root = self._accessibleStore.getRoot()
+
+ self._updateObjects(self._accessibleStore.getTree())
+
+ #Connect to update signal
+ self._signalMatch = self._accessibleStore.connect_to_signal(self._UPDATE_SIGNAL,
+ self._updateHandler)
+
+ def getRootAccessible(self):
+ """
+ Gets the accessible object at the root of the tree.
+ """
+ return self.getAccessible(self._root)
+
+ def getAccessible(self, path):
+ """
+ Gets a D-Bus proxy object for the given object path.
+
+ path - The D-Bus path of the remote object.
+ """
+ if path in self._objects:
+ cachedata = self._objects[path]
+ proxy = None
+ if cachedata.proxy is not None:
+ proxy = cachedata.proxy()
+ if proxy is None:
+ proxy = AccessibleObjectProxy(self,
+ cachedata,
+ self._connection,
+ self._busName,
+ path)
+ cachedata.proxy = ref(proxy)
+ return proxy
+ else:
+ raise AccessibleObjectDoesNotExist, "D-Bus reference not found in cache"
+
+ def _updateObjects(self, objects):
+ """
+ Updates the object cache from an
+ array of accessible object cache data.
+ """
+ for data in objects:
+ #First element is the object path.
+ path = data[0]
+ if path in self._objects:
+ cachedata = self._objects[path]
+ cachedata.update(data)
+ else:
+ self._objects[path] = _CacheData(data)
+
+ def _removeObjects(self, paths):
+ """
+ Removes the object data from the cache.
+ """
+ for path in paths:
+ del(self._objects[path])
+
+ def _updateHandler(self, updates):
+ objects, paths = updates
+ self._removeObjects(paths)
+ self._updateObjects(objects)
+
+class AccessibleObjectProxy(ProxyObject):
+ """
+ A D-Bus proxy for a remote object that implements one or more of the AT-SPI
+ Accessibility interfaces.
+ """
+
+ def __init__(self, cache, data, connection, busName, path):
+ """
+ Create an accessible object.
+
+ cache - The accessible cache that this object is owned by.
+ connection - The D-Bus connection
+ busName - The location on the bus where the remote object is located.
+ path - The object path of the remote object.
+ """
+ ProxyObject.__init__(self, connection, busName, path, introspect=False)
+
+ self._cache = cache
+ self._path = path
+
+ @property
+ def _data(self):
+ try:
+ data = self._cache._objects[self._path]
+ except KeyError:
+ raise AccessibleObjectDoesNotExist(
+ 'Cache data cannot be found for path %s' % (self._path,))
+ return data
+
+ @property
+ def path(self):
+ return self._path
+
+ @property
+ def parent(self):
+ return self._cache.getAccessible(self._data.parent)
+
+ @property
+ def numChildren(self):
+ return len(self._data.children)
+
+ def getChild(self, index):
+ return self._cache.getAccessible(self._data.children[index])
+
+ @property
+ def interfaces(self):
+ return self._data.interfaces
+
+ def getInterface(self, interface):
+ if interface in self._data.interfaces:
+ return Interface(self, interface)
+ else:
+ raise AccessibleInterfaceNotSupported(
+ "%s not supported by accessible object at path %s"
+ % (interface, self.path))
+
+ @property
+ def name(self):
+ return self._data.name
+
+ @property
+ def role(self):
+ return self._data.role
+
+ @property
+ def description(self):
+ return self._data.description
from xml.dom import minidom
-from AccessibleTreeCache import AccessibleTreeCache, AccessibleObjectProxy
+from accessible_cache import AccessibleCache
DBusGMainLoop(set_as_default=True)
e = minidom.Element("accessible")
e.attributes["reference"] = accessible.path
- e.attributes["parent"] = accessible.parent
+ try:
+ e.attributes["parent"] = accessible.parent.path
+ except:
+ pass
e.attributes["name"] = accessible.name
e.attributes["role"] = str(int(accessible.role))
e.attributes["description"] = accessible.description
itf.attributes["name"] = i
e.appendChild(itf)
- for c in accessible.getChildren():
- createNode(c, accessible.path, e)
+ for i in range(0, accessible.numChildren):
+ createNode(accessible.getChild(i), accessible.path, e)
parentElement.appendChild(e)
loop = gobject.MainLoop()
- cache = AccessibleTreeCache(bus, 'test.atspi.tree', '/org/freedesktop/atspi/tree')
+ cache = AccessibleCache(bus, 'test.atspi.tree', '/org/freedesktop/atspi/tree')
root = cache.getRootAccessible()
doc = minidom.Document()