def onFocus(self, event): """Callback for focus: accessibility events.""" # NOTE: This event type is deprecated and Orca should no longer use it. # This callback remains just to handle bugs in applications and toolkits # that fail to reliably emit object:state-changed:focused events. if self.utilities.isLayoutOnly(event.source): return if self.utilities.isTypeahead(orca_state.locusOfFocus) \ and "Table" in pyatspi.listInterfaces(event.source) \ and not event.source.getState().contains(pyatspi.STATE_FOCUSED): return ancestor = pyatspi.findAncestor(orca_state.locusOfFocus, lambda x: x == event.source) if not ancestor: orca.setLocusOfFocus(event, event.source) return if ancestor and "Table" in pyatspi.listInterfaces(ancestor): return isMenu = lambda x: x and x.getRole() == pyatspi.ROLE_MENU if isMenu(ancestor) and not pyatspi.findAncestor(ancestor, isMenu): return orca.setLocusOfFocus(event, event.source)
def onFocus(self, event): """Callback for focus: accessibility events.""" # NOTE: This event type is deprecated and Orca should no longer use it. # This callback remains just to handle bugs in applications and toolkits # that fail to reliably emit object:state-changed:focused events. if self.utilities.eventIsCanvasNoise(event): return if self.utilities.isLayoutOnly(event.source): return if self.utilities.isTypeahead(orca_state.locusOfFocus) \ and "Table" in pyatspi.listInterfaces(event.source) \ and not event.source.getState().contains(pyatspi.STATE_FOCUSED): return ancestor = pyatspi.findAncestor(orca_state.locusOfFocus, lambda x: x == event.source) if not ancestor: orca.setLocusOfFocus(event, event.source) return if ancestor and "Table" in pyatspi.listInterfaces(ancestor): return isMenu = lambda x: x and x.getRole() == pyatspi.ROLE_MENU if isMenu(ancestor) and not pyatspi.findAncestor(ancestor, isMenu): return orca.setLocusOfFocus(event, event.source)
def onSelectedChanged(self, event): """Callback for object:state-changed:selected accessibility events.""" if event.source.getRole() == pyatspi.ROLE_PAGE_TAB and event.detail1: oldName = event.source.name event.source.clearCache() newName = event.source.name if oldName != newName: msg = "CHROMIUM: NO NAME CHANGE HACK: (name should be: '%s')" % newName debug.println(debug.LEVEL_INFO, msg, True) # Other apps and toolkits implement the selection interface, which is # what we use to present active-descendanty selection changes, leaving # state-changed:selected for notifications related to toggling the # selected state of the currently-focused item (e.g. pressing ctrl+space # in a file explorer). While handling active-descendanty changes here is # not technically a HACK, once Chromium implements the selection interface, # we should remove this code and defer to Orca's default handling. if event.detail1 and not self.utilities.isLayoutOnly(event.source) \ and not "Selection" in pyatspi.listInterfaces(event.source.parent) \ and self.utilities.canBeActiveWindow(self.utilities.topLevelObject(event.source)): msg = "CHROMIUM: NO SELECTION IFACE HACK: Setting %s to locusOfFocus" % event.source debug.println(debug.LEVEL_INFO, msg, True) orca.setLocusOfFocus(event, event.source) return if super().onSelectedChanged(event): return msg = "CHROMIUM: Passing along event to default script" debug.println(debug.LEVEL_INFO, msg, True) default.Script.onSelectedChanged(self, event)
def selectedChildren(self, obj): if not obj: return [] role = obj.getRole() isSelection = lambda x: x and 'Selection' in pyatspi.listInterfaces(x) if not isSelection(obj) and role == pyatspi.ROLE_COMBO_BOX: child = pyatspi.findDescendant(obj, isSelection) if child: return super().selectedChildren(child) # Things only seem broken for certain tables, e.g. the Paths table. # TODO - JD: File the LibreOffice bugs and reference them here. if role != pyatspi.ROLE_TABLE or self.isSpreadSheetTable(obj): return super().selectedChildren(obj) try: selection = obj.querySelection() except: return [] children = [] for i, child in enumerate(obj): if selection.isChildSelected(i): children.append(obj[i]) return children
def imagecapture(self, winName = None, resolution1 = None, resolution2 = None, x = 0, y = 0): if winName: acc = None for gui in self._list_guis(): if self._match_name_to_acc(winName, gui): if 'Component' in pyatspi.listInterfaces(gui): acc = gui break if not acc: raise LdtpServerException('No window matches %s' % winName) icomponent = acc.queryComponent() bb = icomponent.getExtents(pyatspi.DESKTOP_COORDS) x, y, resolution2, resolution1 = bb.x, bb.y, bb.height, bb.width window = gtk.gdk.get_default_root_window () size = window.get_size () pb = gtk.gdk.Pixbuf (gtk.gdk.COLORSPACE_RGB, False, 8, resolution1 or size [0], resolution2 or size [1]) pb = pb.get_from_drawable (window, window.get_colormap (), x, y, 0, 0, resolution1 or size [0], resolution2 or size [1]) if pb: tmpFile = tempfile.mktemp('.png', 'ldtpd_') pb.save(tmpFile, 'png') del pb gc.collect() rv = b64encode(open(tmpFile).read()) os.remove(tmpFile) return rv
def handleRowAndColumnSelectionChange(self, obj): interfaces = pyatspi.listInterfaces(obj) if not ("Table" in interfaces and "Selection" in interfaces): return True table = obj.queryTable() cols = set(table.getSelectedColumns()) rows = set(table.getSelectedRows()) selectedCols = sorted(cols.difference(set(self._calcSelectedColumns))) unselectedCols = sorted(set(self._calcSelectedColumns).difference(cols)) convert = lambda x: self.columnConvert(x+1) selectedCols = list(map(convert, selectedCols)) unselectedCols = list(map(convert, unselectedCols)) selectedRows = sorted(rows.difference(set(self._calcSelectedRows))) unselectedRows = sorted(set(self._calcSelectedRows).difference(rows)) convert = lambda x: x + 1 selectedRows = list(map(convert, selectedRows)) unselectedRows = list(map(convert, unselectedRows)) self._calcSelectedColumns = list(cols) self._calcSelectedRows = list(rows) if len(cols) == table.nColumns: self._script.speakMessage(messages.DOCUMENT_SELECTED_ALL) return True if not len(cols) and len(unselectedCols) == table.nColumns: self._script.speakMessage(messages.DOCUMENT_UNSELECTED_ALL) return True msgs = [] if len(unselectedCols) == 1: msgs.append(messages.TABLE_COLUMN_UNSELECTED % unselectedCols[0]) elif len(unselectedCols) > 1: msgs.append(messages.TABLE_COLUMN_RANGE_UNSELECTED % (unselectedCols[0], unselectedCols[-1])) if len(unselectedRows) == 1: msgs.append(messages.TABLE_ROW_UNSELECTED % unselectedRows[0]) elif len(unselectedRows) > 1: msgs.append(messages.TABLE_ROW_RANGE_UNSELECTED % (unselectedRows[0], unselectedRows[-1])) if len(selectedCols) == 1: msgs.append(messages.TABLE_COLUMN_SELECTED % selectedCols[0]) elif len(selectedCols) > 1: msgs.append(messages.TABLE_COLUMN_RANGE_SELECTED % (selectedCols[0], selectedCols[-1])) if len(selectedRows) == 1: msgs.append(messages.TABLE_ROW_SELECTED % selectedRows[0]) elif len(selectedRows) > 1: msgs.append(messages.TABLE_ROW_RANGE_SELECTED % (selectedRows[0], selectedRows[-1])) if msgs: self._script.presentationInterrupt() for msg in msgs: self._script.speakMessage(msg, interrupt=False) return bool(len(msgs))
def selectedChildren(self, obj): if not obj: return [] role = obj.getRole() isSelection = lambda x: x and 'Selection' in pyatspi.listInterfaces(x) if not isSelection(obj) and role == pyatspi.ROLE_COMBO_BOX: child = pyatspi.findDescendant(obj, isSelection) if child: return super().selectedChildren(child) # Things only seem broken for certain tables, e.g. the Paths table. # TODO - JD: File the LibreOffice bugs and reference them here. if role != pyatspi.ROLE_TABLE or self.isSpreadSheetCell(obj): return super().selectedChildren(obj) try: selection = obj.querySelection() except: return [] children = [] for i, child in enumerate(obj): if selection.isChildSelected(i): children.append(obj[i]) return children
def treatAsEntry(self, obj): if not obj or self.inDocumentContent(obj): return super().treatAsEntry(obj) # Firefox seems to have turned its accessible location widget into a # childless editable combobox. try: role = obj.getRole() state = obj.getState() childCount = obj.childCount except: msg = "GECKO: Exception getting role, state, and child count for %s" % obj debug.println(debug.LEVEL_INFO, msg, True) return False if role != pyatspi.ROLE_COMBO_BOX: return False if not state.contains(pyatspi.STATE_FOCUSED): return False if childCount: return False if not "EditableText" in pyatspi.listInterfaces(obj): return False msg = "GECKO: Treating %s as entry" % obj debug.println(debug.LEVEL_INFO, msg, True) return True
def _obscuringBanner(obj): document = _containingDocument(obj) if not document: msg = "EVENT SYNTHESIZER: No obscuring banner found for %s. No document." % obj debug.println(debug.LEVEL_INFO, msg, True) return None if not "Component" in pyatspi.listInterfaces(document): msg = "EVENT SYNTHESIZER: No obscuring banner found for %s. No doc iface." % obj debug.println(debug.LEVEL_INFO, msg, True) return None objX, objY, objWidth, objHeight = _objectExtents(obj) docX, docY, docWidth, docHeight = _objectExtents(document) left = _getAccessibleAtPoint(document, docX, objY) right = _getAccessibleAtPoint(document, docX + docWidth, objY) if not (left and right and left == right != document): msg = "EVENT SYNTHESIZER: No obscuring banner found for %s" % obj debug.println(debug.LEVEL_INFO, msg, True) return None msg = "EVENT SYNTHESIZER: %s believed to be obscured by banner %s" % (obj, left) debug.println(debug.LEVEL_INFO, msg, True) return left
def selectedChildren(self, obj): if not obj: return [] role = obj.getRole() isSelection = lambda x: x and 'Selection' in pyatspi.listInterfaces(x) if not isSelection(obj) and role == pyatspi.ROLE_COMBO_BOX: child = pyatspi.findDescendant(obj, isSelection) if child: return super().selectedChildren(child) # Things only seem broken for certain tables, e.g. the Paths table. # TODO - JD: File the LibreOffice bugs and reference them here. if role != pyatspi.ROLE_TABLE: return super().selectedChildren(obj) # We will need to special case this due to the possibility of there # being lots of children (which may also prove to be zombie objects). # This is why we can't have nice things. if self.isSpreadSheetTable(obj): return [] try: selection = obj.querySelection() except: return [] children = [] for i, child in enumerate(obj): if selection.isChildSelected(i): children.append(obj[i]) return children
def _treatAsSingleObject(self): interfaces = pyatspi.listInterfaces(self._obj) if "Text" not in interfaces: return True roles = [pyatspi.ROLE_ENTRY, pyatspi.ROLE_LABEL, pyatspi.ROLE_PASSWORD_TEXT] if self._obj.getRole() in roles: return True if self._obj.name and not "EditableText" in pyatspi.listInterfaces(self._obj): return True return False
def _treatAsSingleObject(self): interfaces = pyatspi.listInterfaces(self._obj) if "Text" not in interfaces: return True roles = [ pyatspi.ROLE_ENTRY, pyatspi.ROLE_LABEL, pyatspi.ROLE_PASSWORD_TEXT ] if self._obj.getRole() in roles: return True if self._obj.name and not "EditableText" in pyatspi.listInterfaces( self._obj): return True return False
def _getCoordinatesForSelectedRange(self, obj): interfaces = pyatspi.listInterfaces(obj) if not ("Table" in interfaces and "Selection" in interfaces): return (-1, -1), (-1, -1) first, last = self.firstAndLastSelectedChildren(obj) firstCoords = self.coordinatesForCell(first) lastCoords = self.coordinatesForCell(last) return firstCoords, lastCoords
def _generateLabelAndName(self, obj, **args): if self._script.utilities.isTextBlockElement(obj): return [] role = args.get('role', obj.getRole()) if role == pyatspi.ROLE_LABEL and 'Text' in pyatspi.listInterfaces(obj): return [] return super()._generateLabelAndName(obj, **args)
def _treatAsSingleObject(self): interfaces = pyatspi.listInterfaces(self._obj) if "Text" not in interfaces: return True if not self._obj.queryText().characterCount: return True return False
def onFocus(self, event): """Callback for focus: accessibility events.""" # NOTE: This event type is deprecated and Orca should no longer use it. # This callback remains just to handle bugs in applications and toolkits # that fail to reliably emit object:state-changed:focused events. if self.utilities.eventIsCanvasNoise(event): return if self.utilities.isLayoutOnly(event.source): return if event.source == mouse_review.reviewer.getCurrentItem(): msg = "GTK: Event source is current mouse review item" debug.println(debug.LEVEL_INFO, msg, True) return if self.utilities.isTypeahead(orca_state.locusOfFocus) \ and "Table" in pyatspi.listInterfaces(event.source) \ and not event.source.getState().contains(pyatspi.STATE_FOCUSED): return if "Table" in pyatspi.listInterfaces(event.source): selectedChildren = self.utilities.selectedChildren(event.source) if selectedChildren: orca.setLocusOfFocus(event, selectedChildren[0]) return ancestor = pyatspi.findAncestor(orca_state.locusOfFocus, lambda x: x == event.source) if not ancestor: orca.setLocusOfFocus(event, event.source) return if ancestor and "Table" in pyatspi.listInterfaces(ancestor): return isMenu = lambda x: x and x.getRole() == pyatspi.ROLE_MENU if isMenu(ancestor) and not pyatspi.findAncestor(ancestor, isMenu): return orca.setLocusOfFocus(event, event.source)
def _getCellFromTable(self, table, rowindex, colindex): if "Table" not in pyatspi.listInterfaces(table): return NOne if rowindex < 0 or colindex < 0: return None iface = table.queryTable() if rowindex >= iface.nRows or colindex >= iface.nColumns: return None return table.queryTable().getAccessibleAt(rowindex, colindex)
def selectedChildren(self, obj): result = super().selectedChildren(obj) if result or "Selection" in pyatspi.listInterfaces(obj): return result # HACK: Ideally, we'd use the selection interface to get the selected # children. But that interface is not implemented yet. This hackaround # is extremely non-performant. for child in obj: if child.getState().contains(pyatspi.STATE_SELECTED): result.append(child) return result
def onAccChanged(self, acc): ''' Update the UI when the selected accessible changes. @param acc: The applications-wide selected accessible. @type acc: Accessibility.Accessible ''' self.acc = acc ints = pyatspi.listInterfaces(acc) model = self.iface_combo.get_model() model.clear() for iface in ints: self.iface_combo.append_text(iface) self.iface_combo.set_active(0)
def selectedChildCount(self, obj): count = super().selectedChildCount(obj) if count or "Selection" in pyatspi.listInterfaces(obj): return count # HACK: Ideally, we'd use the selection interface to get the selected # child count. But that interface is not implemented yet. This hackaround # is extremely non-performant. for child in obj: if child.getState().contains(pyatspi.STATE_SELECTED): count += 1 msg = "CHROMIUM: NO SELECTION INTERFACE HACK: Selected children: %i" % count debug.println(debug.LEVEL_INFO, msg, True) return count
def assertInterfaces(self, control, expected_ifaces): """ Ensure that only the expected interfaces are present. Fail otherwise. """ ifaces = pyatspi.listInterfaces(control._accessible) ifaces = map(unicode.lower, ifaces) expected_ifaces = map(str.lower, expected_ifaces) for iface in ifaces: if iface in expected_ifaces: expected_ifaces.remove(iface) else: self.fail("Unexpected interface: %s" % iface) self.assertEqual(0, len(expected_ifaces), \ "Some interfaces are not implemented: %s" % expected_ifaces)
def _generateFocusedItem(self, obj, **args): result = [] role = args.get('role', obj.getRole()) if role not in [pyatspi.ROLE_LIST, pyatspi.ROLE_LIST_BOX]: return result if 'Selection' in pyatspi.listInterfaces(obj): items = self._script.utilities.selectedChildren(obj) else: items = [self._script.utilities.focusedChild(obj)] if not (items and items[0]): return result for item in map(self._generateName, items): result.extend(item) return result
def test_widget_interfaces(obj, interfaces): """ Verifies whether the interfaces implemented by the widget are consistent with those declared in the ATK-EAIL mapping. The mapping is provided in parameters.ini. :param obj: widget object in test application. :param interfaces: list of interfaces expected in ATK-EAIL mapping. """ assert obj, "No application or no ATK object has been found" implemented_interfaces = pyatspi.listInterfaces(obj) assert sorted(implemented_interfaces) == sorted(interfaces),\ "Implemented interfaces different then expected: [%s] != [%s]" % \ (str(implemented_interfaces), str(interfaces))
def _adjustPointForObj(self, obj, x, y, coordType): try: singleLine = obj.getState().contains(pyatspi.STATE_SINGLE_LINE) except: singleLine = False if not singleLine or "EditableText" not in pyatspi.listInterfaces(obj): return x, y text = self.queryNonEmptyText(obj) if not text: return x, y objBox = obj.queryComponent().getExtents(coordType) stringBox = text.getRangeExtents(0, text.characterCount, coordType) if self.intersection(objBox, stringBox) != (0, 0, 0, 0): return x, y msg = "ERROR: text bounds %s not in obj bounds %s" % (stringBox, objBox) debug.println(debug.LEVEL_INFO, msg, True) # This is where the string starts; not the widget. boxX, boxY = stringBox[0], stringBox[1] # Window Coordinates should be relative to the window; not the widget. # But broken interface is broken, and this appears to be what is being # exposed. And we need this information to get the widget's x and y. charExtents = text.getCharacterExtents(0, pyatspi.WINDOW_COORDS) if 0 < charExtents[0] < charExtents[2]: boxX -= charExtents[0] if 0 < charExtents[1] < charExtents[3]: boxY -= charExtents[1] # The point relative to the widget: relX = x - objBox[0] relY = y - objBox[1] # The point relative to our adjusted bounding box: newX = boxX + relX newY = boxY + relY msg = "INFO: Adjusted (%i, %i) to (%i, %i)" % (x, y, newX, newY) debug.println(debug.LEVEL_INFO, msg, True) return newX, newY
def isUselessPanel(self, obj): if not (obj and obj.getRole() == pyatspi.ROLE_PANEL): return False rv = self._isUselessPanel.get(hash(obj)) if rv is not None: return rv try: name = obj.name childCount = obj.childCount supportsText = "Text" in pyatspi.listInterfaces(obj) except: rv = True else: rv = not (name or childCount or supportsText) self._isUselessPanel[hash(obj)] = rv return rv
def selectedChildren(self, obj): result = super().selectedChildren(obj) if result or "Selection" in pyatspi.listInterfaces(obj): return result try: childCount = obj.childCount except: msg = "CHROMIUM: Exception getting child count of %s" % obj debug.println(debug.LEVEL_INFO, msg, True) return result # HACK: Ideally, we'd use the selection interface to get the selected # children. But that interface is not implemented yet. This hackaround # is extremely non-performant. for i in range(childCount): child = obj[i] if child and child.getState().contains(pyatspi.STATE_SELECTED): result.append(child) return result
def _getStringContext(self): """Returns the _StringContext associated with the specified point.""" if not (self._script and self._obj): return _StringContext(self._obj) interfaces = pyatspi.listInterfaces(self._obj) if "Text" not in interfaces: return _StringContext(self._obj, self._script) state = self._obj.getState() if not state.contains(pyatspi.STATE_SELECTABLE): boundary = pyatspi.TEXT_BOUNDARY_WORD_START else: boundary = pyatspi.TEXT_BOUNDARY_LINE_START string, start, end = self._script.utilities.textAtPoint( self._obj, self._x, self._y, boundary=boundary) if not string and self._script.utilities.isTextArea(self._obj): string = self._script.speechGenerator.getRoleName(self._obj) return _StringContext(self._obj, self._script, string, start, end)
def imagecapture(self, window_name = None, x = 0, y = 0, width = None, height = None): """ Captures screenshot of the whole desktop or given window @param window_name: Window name to look for, either full name, LDTP's name convention, or a Unix glob. @type window_name: string @param x: x co-ordinate value @type x: int @param y: y co-ordinate value @type y: int @param width: width co-ordinate value @type width: int @param height: height co-ordinate value @type height: int @return: screenshot with base64 encoded for the client @rtype: string """ if window_name: acc = None for gui in self._list_guis(): if self._match_name_to_acc(window_name, gui): if 'Component' in pyatspi.listInterfaces(gui): acc = gui for obj in self._list_objects(gui): role = obj.getRole() if role == pyatspi.ROLE_CHECK_BOX or \ role == pyatspi.ROLE_PUSH_BUTTON or \ role == pyatspi.ROLE_RADIO_BUTTON: try: # Try to grab focus self._grab_focus(obj) except: pass # Inner for loop break # Outer for loop break if not acc: raise LdtpServerException('No window matches %s' % window_name) icomponent = acc.queryComponent() bb = icomponent.getExtents(pyatspi.DESKTOP_COORDS) x, y, height, width = bb.x, bb.y, bb.height, bb.width tmpFile = tempfile.mktemp('.png', 'ldtpd_') if gtk3: window = gdk.get_default_root_window() tmp_size = window.get_geometry() size = [] # Width size.append(tmp_size[2]) # Height size.append(tmp_size[3]) pb = gdk.pixbuf_get_from_window(window, x, y, width or size[0], height or size[1]) pb.savev(tmpFile, 'png', [], []) del pb gc.collect() else: window = gtk.gdk.get_default_root_window() size = window.get_size() pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width or size [0], height or size [1]) pb = pb.get_from_drawable(window, window.get_colormap(), x, y, 0, 0, width or size [0], height or size [1]) if pb: pb.save(tmpFile, 'png') del pb gc.collect() rv = b64encode(open(tmpFile).read()) os.remove(tmpFile) return rv
def handleRowAndColumnSelectionChange(self, obj): interfaces = pyatspi.listInterfaces(obj) if not ("Table" in interfaces and "Selection" in interfaces): return True table = obj.queryTable() cols = set(table.getSelectedColumns()) rows = set(table.getSelectedRows()) selectedCols = sorted(cols.difference(set(self._calcSelectedColumns))) unselectedCols = sorted( set(self._calcSelectedColumns).difference(cols)) convert = lambda x: self.columnConvert(x + 1) selectedCols = list(map(convert, selectedCols)) unselectedCols = list(map(convert, unselectedCols)) selectedRows = sorted(rows.difference(set(self._calcSelectedRows))) unselectedRows = sorted(set(self._calcSelectedRows).difference(rows)) convert = lambda x: x + 1 selectedRows = list(map(convert, selectedRows)) unselectedRows = list(map(convert, unselectedRows)) self._calcSelectedColumns = list(cols) self._calcSelectedRows = list(rows) if len(cols) == table.nColumns: self._script.speakMessage(messages.DOCUMENT_SELECTED_ALL) return True if not len(cols) and len(unselectedCols) == table.nColumns: self._script.speakMessage(messages.DOCUMENT_UNSELECTED_ALL) return True msgs = [] if len(unselectedCols) == 1: msgs.append(messages.TABLE_COLUMN_UNSELECTED % unselectedCols[0]) elif len(unselectedCols) > 1: msgs.append(messages.TABLE_COLUMN_RANGE_UNSELECTED % (unselectedCols[0], unselectedCols[-1])) if len(unselectedRows) == 1: msgs.append(messages.TABLE_ROW_UNSELECTED % unselectedRows[0]) elif len(unselectedRows) > 1: msgs.append(messages.TABLE_ROW_RANGE_UNSELECTED % (unselectedRows[0], unselectedRows[-1])) if len(selectedCols) == 1: msgs.append(messages.TABLE_COLUMN_SELECTED % selectedCols[0]) elif len(selectedCols) > 1: msgs.append(messages.TABLE_COLUMN_RANGE_SELECTED % (selectedCols[0], selectedCols[-1])) if len(selectedRows) == 1: msgs.append(messages.TABLE_ROW_SELECTED % selectedRows[0]) elif len(selectedRows) > 1: msgs.append(messages.TABLE_ROW_RANGE_SELECTED % (selectedRows[0], selectedRows[-1])) if msgs: self._script.presentationInterrupt() for msg in msgs: self._script.speakMessage(msg, interrupt=False) return bool(len(msgs))
def imagecapture(self, window_name=None, x=0, y=0, width=None, height=None): """ Captures screenshot of the whole desktop or given window @param window_name: Window name to look for, either full name, LDTP's name convention, or a Unix glob. @type window_name: string @param x: x co-ordinate value @type x: int @param y: y co-ordinate value @type y: int @param width: width co-ordinate value @type width: int @param height: height co-ordinate value @type height: int @return: screenshot with base64 encoded for the client @rtype: string """ # Validate the parameters # x and y offsets cannot be nagative x = max(0, x) y = max(0, y) # height and width cannot be less than 1 # set to None if nagative value is given if width < 1: width = None if height < 1: height = None if window_name: acc = None for gui in self._list_guis(): if self._match_name_to_acc(window_name, gui): if 'Component' in pyatspi.listInterfaces(gui): acc = gui for obj in self._list_objects(gui): role = obj.getRole() if role == pyatspi.ROLE_CHECK_BOX or \ role == pyatspi.ROLE_PUSH_BUTTON or \ role == pyatspi.ROLE_RADIO_BUTTON: try: # Try to grab focus self._grab_focus(obj) except: pass # Inner for loop break # Outer for loop break if not acc: raise LdtpServerException('No window matches %s' % window_name) icomponent = acc.queryComponent() bb = icomponent.getExtents(pyatspi.DESKTOP_COORDS) # If co-ordinates are provided, use it # offsets cannot be greater than or equal to the window size # we want to capture at least one pixel x = min(x, bb.width - 1) y = min(y, bb.height - 1) # adjust the width and height parameters # so that the captured image is contained # within the visible window area # Take into account that window may be only # partially on the screen then the reported # width and height are not the same as the area of the window # that can actually be captured. # if bb.x is negative then the actual width # is smaller than the bb.width leftClippedWidth = min(bb.width, bb.width + bb.x) # if bb.y is negative then the actual height # is smaller than the bb.height topClippedHeight = min(bb.height, bb.height + bb.y) # Clipping from the right and bottom is done later # when the desktop size is known if width == None: width = leftClippedWidth - x else: width = min(width, leftClippedWidth - x) if height == None: height = topClippedHeight - y else: height = min(height, topClippedHeight - y) # take the window position into account # use 0 as the window co-oridinate # if it is negative x = x + max(0, bb.x) y = y + max(0, bb.y) tmpFile = tempfile.mktemp('.png', 'ldtpd_') if gtk3: window = gdk.get_default_root_window() tmp_size = window.get_geometry() size = [] # Width size.append(tmp_size[2]) # Height size.append(tmp_size[3]) # offsets cannot be greater than or equal to the desktop size # we want to capture at least one pixel x = min(x, size[0] - 1) y = min(y, size[1] - 1) # adjust the width and height parameters # so that the captured image is contained # within the desktop area if width == None: width = size[0] - x else: width = min(width, size[0] - x) if height == None: height = size[1] - y else: height = min(height, size[1] - y) pb = gdk.pixbuf_get_from_window(window, x, y, width, height) pb.savev(tmpFile, 'png', [], []) del pb gc.collect() else: window = gtk.gdk.get_default_root_window() size = window.get_size() # offsets cannot be greater than or equal to the desktop size # we want to capture at least one pixel x = min(x, size[0] - 1) y = min(y, size[1] - 1) # adjust the width and height parameters # so that the captured image is contained # within the desktop area if width == None: width = size[0] - x else: width = min(width, size[0] - x) if height == None: height = size[1] - y else: height = min(height, size[1] - y) pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height) pb = pb.get_from_drawable(window, window.get_colormap(), x, y, 0, 0, width, height) if pb: pb.save(tmpFile, 'png') del pb gc.collect() rv = b64encode(open(tmpFile).read()) os.remove(tmpFile) return rv
def imagecapture(self, window_name = None, x = 0, y = 0, width = None, height = None): """ Captures screenshot of the whole desktop or given window @param window_name: Window name to look for, either full name, LDTP's name convention, or a Unix glob. @type window_name: string @param x: x co-ordinate value @type x: int @param y: y co-ordinate value @type y: int @param width: width co-ordinate value @type width: int @param height: height co-ordinate value @type height: int @return: screenshot with base64 encoded for the client @rtype: string """ # Validate the parameters # x and y offsets cannot be nagative x = max(0, x) y = max(0, y) # height and width cannot be less than 1 # set to None if nagative value is given if width < 1: width = None if height < 1: height = None if window_name: acc = None for gui in self._list_guis(): if self._match_name_to_acc(window_name, gui): if 'Component' in pyatspi.listInterfaces(gui): acc = gui for obj in self._list_objects(gui): role = obj.getRole() if role == pyatspi.ROLE_CHECK_BOX or \ role == pyatspi.ROLE_PUSH_BUTTON or \ role == pyatspi.ROLE_RADIO_BUTTON: try: # Try to grab focus self._grab_focus(obj) except: pass # Inner for loop break # Outer for loop break if not acc: raise LdtpServerException('No window matches %s' % window_name) icomponent = acc.queryComponent() bb = icomponent.getExtents(pyatspi.DESKTOP_COORDS) # If co-ordinates are provided, use it # offsets cannot be greater than or equal to the window size # we want to capture at least one pixel x = min(x, bb.width - 1) y = min(y, bb.height - 1) # adjust the width and height parameters # so that the captured image is contained # within the visible window area # Take into account that window may be only # partially on the screen then the reported # width and height are not the same as the area of the window # that can actually be captured. # if bb.x is negative then the actual width # is smaller than the bb.width leftClippedWidth = min(bb.width, bb.width + bb.x) # if bb.y is negative then the actual height # is smaller than the bb.height topClippedHeight = min(bb.height, bb.height + bb.y) # Clipping from the right and bottom is done later # when the desktop size is known if width == None: width = leftClippedWidth - x else: width = min(width, leftClippedWidth - x) if height == None: height = topClippedHeight - y else: height = min(height, topClippedHeight - y) # take the window position into account # use 0 as the window co-oridinate # if it is negative x = x + max(0, bb.x) y = y + max(0, bb.y) tmpFile = tempfile.mktemp('.png', 'ldtpd_') if gtk3: window = gdk.get_default_root_window() tmp_size = window.get_geometry() size = [] # Width size.append(tmp_size[2]) # Height size.append(tmp_size[3]) # offsets cannot be greater than or equal to the desktop size # we want to capture at least one pixel x = min(x, size[0] - 1) y = min(y, size[1] - 1) # adjust the width and height parameters # so that the captured image is contained # within the desktop area if width == None: width = size[0] - x else: width = min(width, size[0] - x) if height == None: height = size[1] - y else: height = min(height, size[1] - y) pb = gdk.pixbuf_get_from_window(window, x, y, width, height) pb.savev(tmpFile, 'png', [], []) del pb gc.collect() else: window = gtk.gdk.get_default_root_window() size = window.get_size() # offsets cannot be greater than or equal to the desktop size # we want to capture at least one pixel x = min(x, size[0] - 1) y = min(y, size[1] - 1) # adjust the width and height parameters # so that the captured image is contained # within the desktop area if width == None: width = size[0] - x else: width = min(width, size[0] - x) if height == None: height = size[1] - y else: height = min(height, size[1] - y) pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, width, height) pb = pb.get_from_drawable(window, window.get_colormap(), x, y, 0, 0, width, height) if pb: pb.save(tmpFile, 'png') del pb gc.collect() rv = b64encode(open(tmpFile).read()) os.remove(tmpFile) return rv
def getZonesFromText(self, accessible, cliprect): """Gets a list of Zones from an object that implements the AccessibleText specialization. Arguments: - accessible: the accessible - cliprect: the extents that the Zones must fit inside. Returns a list of Zones. """ if not self.script.utilities.hasPresentableText(accessible): return [] zones = [] text = accessible.queryText() # TODO - JD: This is here temporarily whilst I sort out the rest # of the text-related mess. if "EditableText" in pyatspi.listInterfaces(accessible) \ and accessible.getState().contains(pyatspi.STATE_SINGLE_LINE): extents = accessible.queryComponent().getExtents(0) return [TextZone(accessible, 0, text.getText(0, -1), *extents)] offset = 0 lastEndOffset = -1 upperMax = lowerMax = text.characterCount upperMid = lowerMid = int(upperMax / 2) upperMin = lowerMin = 0 upperY = lowerY = 0 oldMid = 0 # performing binary search to locate first line inside clipped area while oldMid != upperMid: oldMid = upperMid [x, y, width, height] = text.getRangeExtents(upperMid, upperMid + 1, 0) upperY = y if y > cliprect.y: upperMax = upperMid else: upperMin = upperMid upperMid = int((upperMax - upperMin) / 2) + upperMin # performing binary search to locate last line inside clipped area oldMid = 0 limit = cliprect.y + cliprect.height while oldMid != lowerMid: oldMid = lowerMid [x, y, width, height] = text.getRangeExtents(lowerMid, lowerMid + 1, 0) lowerY = y if y > limit: lowerMax = lowerMid else: lowerMin = lowerMid lowerMid = int((lowerMax - lowerMin) / 2) + lowerMin msg = "FLAT REVIEW: Getting lines for %s offsets %i-%i" % ( accessible, upperMin, lowerMax) debug.println(debug.LEVEL_INFO, msg, True) lines = self._getLines(accessible, upperMin, lowerMax) msg = "FLAT REVIEW: %i lines found for %s" % (len(lines), accessible) debug.println(debug.LEVEL_INFO, msg, True) for string, startOffset, endOffset in lines: zones.extend( self.splitTextIntoZones(accessible, string, startOffset, cliprect)) return zones