def altF4(self, assertClosed=True): 'Press <Alt>F4' procedurelogger.action('Press <Alt>F4.', self) self.keyCombo('<Alt>F4') if assertClosed: self.assertClosed()
def select(self, log=True): 'Select the table cell' if log: procedurelogger.action('Select %s.' % self, self) self.grabFocus()
def select(self, name, logName=None, log=True): 'Select a tab' # we don't use self.findPageTab or self.__getattr__('findPageTab') # here because findPageTab tries to promote tabs to specific classes # which may have constructors that look for widgets that are # lazy-loaded, causing bogus searchErrors. tab = utils.findDescendant(self, lambda x: x.role == pyatspi.ROLE_PAGE_TAB and utils.equalsOrMatches(x.name, name) and x.showing, \ recursive=False) # do the work of actually selecting the tab. this should cause # lazy-loaded widgets to be loaded. self.selectChild(tab.getIndexInParent()) # now search for the tab as if we haven't done any of the above, but # don't do any logging tab = self.findPageTab(name, logName=logName) # now that we have the (possibly promoted) tab, do the logging if log: # we need to log after the find() because the tab might be promoted and have a different logName procedurelogger.action('Select the %s.' % tab, self) sleep(config.MEDIUM_DELAY) return tab
def enterText(self, text, log=True): 'Enter text' if log: procedurelogger.action('Enter "%s" into the %s.' % (text, self), self) self.text = text # since we don't absolutely need to use typeText here, lets do it this way since its a lot faster
def click(self, log=True): 'Click the button' if log: procedurelogger.action('Click the %s.' % self, self) super(Button, self).click()
def click(self, log=True): 'Click the button' if log: procedurelogger.action('Click the %s.' % self, self) self.__getattr__('click')()
def mouseMove(self, xOffset=0, yOffset=0, log=True): 'Move the mouse cursor to the Accessible' if log: procedurelogger.action('Move the mouse cursor to the %s.' % (self), self) x, y = self._getAccessibleCenter() pyatspi.Registry.generateMouseEvent(x + xOffset, y + yOffset, 'abs')
def activate(self, log=True): 'Activate (double-click) the table cell' if log: procedurelogger.action('Double-click %s.' % self, self) self.grabFocus() super(TableCell, self).activate()
def activate(self, log=True): 'Activate (double-click) the table cell' if log: procedurelogger.action('Double-click %s.' % self, self) self.grabFocus() self.__getattr__('activate')()
def selectColorRGB(self, rgbValues): if not self._isDialog(): raise NotImplementedError procedurelogger.action('Select the color with RGB value of: (%s, %s, %s).' % (rgbValues[0], rgbValues[1], rgbValues[2])) self.red.value = rgbValues[0] self.green.value = rgbValues[1] self.blue.value = rgbValues[2] procedurelogger.expectedResult('The color with RGB value of (%s, %s, %s) is selected.' % (rgbValues[0], rgbValues[1], rgbValues[2]))
def selectColorHSV(self, hsvValues): if not self._isDialog(): raise NotImplementedError procedurelogger.action('Select the color with HSV value of: (%s, %s, %s).' % (hsvValues[0], hsvValues[1], hsvValues[2])) self.hue.value = hsvValues[0] self.saturation.value = hsvValues[1] self.val.value = hsvValues[2] procedurelogger.expectedResult('The color with HSV value of (%s, %s, %s) is selected.' % (hsvValues[0], hsvValues[1], hsvValues[2]))
def open(self, path): ''' Open a menu Path must be an array of strings; regular expressions are not supported. ''' procedurelogger.action('Open the %s menu.' % ' => '.join(path), self) return self._open(path)
def doActionMethod(log=False): def sensitive(): return self.sensitive if not utils.retryUntilTrue(sensitive): raise errors.NotSensitiveError if log: procedurelogger.action('Perform "%s" action for %s.' % (iaction.getName(i), self)) iaction.doAction(i)
def typeText(self, text, log=True): 'Type text into the table cell' self.mouseClick() if log: procedurelogger.action('Enter "%s" into %s.' % (text, self), self) sleep(config.SHORT_DELAY) super(TableCell, self).typeText(text) pyatspi.Registry.generateKeyboardEvent(self._charToKeySym('Return'), None, pyatspi.KEY_SYM)
def typeText(self, text, log=True): 'Turns text (a string) into a series of keyboard events' if log: procedurelogger.action('Type "%s".' % text, self) text_syms = map(self._charToKeySym, text) for key in text_syms: sleep(config.KEYCOMBO_DELAY) pyatspi.Registry.generateKeyboardEvent(key, None, pyatspi.KEY_SYM)
def select(self, name, logName=None, log=True): 'Select an item' item = self.findMenuItem(name, logName=logName, checkShowing=False) if log: procedurelogger.action('Select %s.' % str(item).replace(' menu option', ''), self) item.click() return item
def select(self, name, logName=None, log=True): 'Select an item' item = self.findMenuItem(name, logName=logName, checkShowing=False) if log: procedurelogger.action( 'Select %s.' % str(item).replace(' menu option', ''), self) item.click() return item
def mouseClick(self, button=1, xOffset=0, yOffset=0, log=True): ''' Click the table cell If the table cell is editable, this should trigger the "edit mode". If you just want to select the table cell, use select() instead. ''' if log: procedurelogger.action('Click %s.' % self, self) super(TableCell, self).mouseClick(button=button, xOffset=xOffset, yOffset=yOffset)
def open(self, path): ''' Open a menu Path must be an array of strings; regular expressions are not supported. ''' if type(path) != type(['l']): raise TypeError, 'path argument must be a list' procedurelogger.action('Open the %s menu.' % ' => '.join(path), self) return self._open(path)
def typeText(self, text, log=True): 'Type text into the table cell' # Click the table cell. If the table cell is editable, this should trigger the # "edit mode". If you just want to select the table cell, use select() instead. self.mouseClick(log=log) if log: procedurelogger.action('Enter "%s" into %s.' % (text, self), self) sleep(config.SHORT_DELAY) super(TableCell, self).typeText(text, False) pyatspi.Registry.generateKeyboardEvent(self._charToKeySym('Return'), None, pyatspi.KEY_SYM)
def insertText(self, text, offset=0, log=True): ''' Insert the specified text into an editable text accessible using an optional offset from the first index of the accessible. This method uses the IAccessibleEditableText insertText method to insert the specified text. ''' if log: procedurelogger.action('Enter "%s" into %s.' % (text, self), self) ieditable = self._accessible.queryEditableText() ieditable.insertText(offset, text, len(text))
def select(self, log=True): ''' Select the page tab This method should not be used to initially select a tab; PageTabList.select() should be used instead. Using PageTabList.findPageTab() may cause bogus search errors if the tab's class's constructor looks for sub-widgets and that are lazy-loaded. ''' if log: procedurelogger.action('Select the %s.' % self, self.parent) self.parent.selectChild(self.getIndexInParent())
def select(self, path, log=True): ''' Select a menu item Path must be an array of strings; regular expressions are not supported. ''' if log: procedurelogger.action('Under the %s menu, select %s.' % (' => '.join(path[0:-1]), path[-1].replace('...', '')), self) parent = self._open(path[0:-1]) # the last item in the path is excluded because we're going to click that item item = utils.findDescendant(parent, lambda x: (x.role == pyatspi.ROLE_MENU_ITEM or x.role == pyatspi.ROLE_CHECK_MENU_ITEM) \ and utils.equalsOrMatches(x.name, path[-1]) and x.showing, recursive=False) item.click() return item
def mouseClick(self, button=1, xOffset=0, yOffset=0, log=True): 'Synthesize a left, middle, or right mouse click on this Accessible' if log: button_name = "Click" if button == 1: button_name = "Left click" elif button == 2: button_name = "Middle click" elif button == 3: button_name = "Right click" procedurelogger.action('%s the %s.' % (button_name, self), self) x, y = self._getAccessibleCenter() pyatspi.Registry.generateMouseEvent(x + xOffset, y + yOffset, 'b%dc' % button)
def deleteText(self, start=0, end=None, log=True): ''' Delete the text of an editable text accessible. By default all text is deleted. Optionally, a start and end index can be specified to delete a range of text. This method uses the IAccessibleEditableText deleteText method to delete the text. ''' ieditable = self._accessible.queryEditableText() if end is None: end = ieditable.characterCount text = ieditable.getText(start, end) if log: procedurelogger.action('Delete "%s" from %s.' % (text, self), self) ieditable.deleteText(start, end)
def select(self, path, log=True): ''' Select a menu item Path must be an array of strings; regular expressions are not supported. ''' if log: procedurelogger.action( 'Under the %s menu, select %s.' % (' => '.join(path[0:-1]), path[-1].replace('...', '')), self) parent = self._open( path[0:-1] ) # the last item in the path is excluded because we're going to click that item item = utils.findDescendant(parent, lambda x: (x.role == pyatspi.ROLE_MENU_ITEM or x.role == pyatspi.ROLE_CHECK_MENU_ITEM) \ and utils.equalsOrMatches(x.name, path[-1]) and x.showing, recursive=False) item.click() return item
def keyCombo(self, combo, grabFocus=True, log=True): ''' Optionally focus this Accessible and press a single key or a combination of keys simultaneously. ''' if log: procedurelogger.action('Press %s.' % combo, self) import gtk.gdk _keymap = gtk.gdk.keymap_get_default() keySymAliases = { 'enter' : 'Return', 'esc' : 'Escape', 'alt' : 'Alt_L', 'control' : 'Control_L', 'ctrl' : 'Control_L', 'shift' : 'Shift_L', 'del' : 'Delete', 'ins' : 'Insert', 'pageup' : 'Page_Up', 'pagedown' : 'Page_Down', ' ' : 'space', '\t' : 'Tab', '\n' : 'Return' } ModifierKeyCodes = { 'Control_L' : _keymap.get_entries_for_keyval(gtk.keysyms.Control_L)[0][0], 'Alt_L' : _keymap.get_entries_for_keyval(gtk.keysyms.Alt_L)[0][0], 'Shift_L' : _keymap.get_entries_for_keyval(gtk.keysyms.Shift_L)[0][0] } keys = [] for key in re.split('[<>]', combo): if key: key = keySymAliases.get(key.lower(), key) keys.append(key) modifiers = map(ModifierKeyCodes.get, keys[:-1]) if grabFocus: try: self.grabFocus() except: pass sleep(config.SHORT_DELAY) for key_code in modifiers: sleep(config.KEYCOMBO_DELAY) pyatspi.Registry.generateKeyboardEvent(key_code, None, pyatspi.KEY_PRESS) sleep(config.KEYCOMBO_DELAY) pyatspi.Registry.generateKeyboardEvent(self._charToKeySym(keys[-1]), None, pyatspi.KEY_SYM) for key_code in modifiers: sleep(config.KEYCOMBO_DELAY) pyatspi.Registry.generateKeyboardEvent(key_code, None, pyatspi.KEY_RELEASE)
def selectFont(self, family, style, size): procedurelogger.action("Select font '%s' with style '%s' on size '%s'." % (family, style, size)) self.families.select(family, log=False) self.styles.select(style, log=False) self.sizes.select(size, log=False) procedurelogger.expectedResult("The font '%s' with style '%s' on size '%s' is selected." % (family, style, size))
def launchApplication(args=[], name=None, find=None, cwd=None, env=None, wait=config.MEDIUM_DELAY, cache=True, logString=None): ''' Launch an application with accessibility enabled args, cwd, and env are passed to subprocess.Popen. If cwd is not specified, it defaults to os.cwd(). If env is not specified, it defaults to os.environ, plus GTK_MODULES='gail:atk-bridge' After launching the application, a reference to the strongwind.accessibles.Application is cached. The "name" argument to this method is used to find the accessible that should be promoted to a strongwind.accessibles.Application. The name is also used to refer to the application in the test procedures log. If name is not specified, it defaults to the basename of args[0] with any file extension stripped. If the accessible name of the application is not fixed, the "find" argument can be used to search for a pattern. If find is not specified, it defaults to re.compile('^' + name) Returns a tuple containing a strongwind.accessibles.Application object and a Popen object. ''' # if a name for the application is not specified, try to guess it if name is None: name = utils.getBasenameWithoutExtension(args[0]) if logString is None: logString = 'Launch %s.' % name procedurelogger.action(logString) if env is None: env = os.environ # enable accessibility for this application if not env.has_key('GTK_MODULES'): env['GTK_MODULES'] = 'gail:atk-bridge' if find is None: find = re.compile('^' + name) if cwd is None: cwd = os.getcwd() def findAppWithLargestId(desktop, find): ''' Find the application with the largest id whose name matches find If ids are not recycled (i.e., ids always increment and never start over again at 1), the application with the highest id will be the last launched. We're making this assumption. ''' appWithLargestId = None apps = utils.findAllDescendants( desktop, lambda x: pyatspi.ROLE_APPLICATION == x.role and find. search(x.name), False) if len(apps) > 0: appWithLargestId = apps[0] for a in apps: if a._accessible.id > appWithLargestId._accessible.id: appWithLargestId = a return appWithLargestId # before we launch the application, check to see if there is another # instance of the application already open existingApp = findAppWithLargestId(_desktop, find) # launch the application subproc = subprocess.Popen(args, cwd=cwd, env=env) # wait for the application to launch and for the applications list to # settle. if we try to list the desktop's applications too soon, we get # crashes sometimes. sleep(wait) def findNewApplication(): ''' Find the application we just launched If there is an existing application, make sure the app we find here has an id larger than the existing application. If no application is found, wait and retry a number of times before returning None. ''' for i in xrange(config.RETRY_TIMES): app = findAppWithLargestId(_desktop, find) try: if existingApp is None or existingApp.id < app.id: return app except (LookupError, pyatspi.ORBit.CORBA.COMM_FAILURE): return app sleep(config.RETRY_INTERVAL) raise errors.SearchError app = findNewApplication() if cache: addApplication(app) return (app, subproc)
def launchApplication( args=[], name=None, find=None, cwd=None, env=None, wait=config.MEDIUM_DELAY, cache=True, logString=None ): """ Launch an application with accessibility enabled args, cwd, and env are passed to subprocess.Popen. If cwd is not specified, it defaults to os.cwd(). If env is not specified, it defaults to os.environ, plus GTK_MODULES='gail:atk-bridge' After launching the application, a reference to the strongwind.accessibles.Application is cached. The "name" argument to this method is used to find the accessible that should be promoted to a strongwind.accessibles.Application. The name is also used to refer to the application in the test procedures log. If name is not specified, it defaults to the basename of args[0] with any file extension stripped. If the accessible name of the application is not fixed, the "find" argument can be used to search for a pattern. If find is not specified, it defaults to re.compile('^' + name) Returns a tuple containing a strongwind.accessibles.Application object and a Popen object. """ # if a name for the application is not specified, try to guess it if name is None: name = utils.getBasenameWithoutExtension(args[0]) if logString is None: logString = "Launch %s." % name procedurelogger.action(logString) if env is None: env = os.environ # enable accessibility for this application if not env.has_key("GTK_MODULES"): env["GTK_MODULES"] = "gail:atk-bridge" if find is None: find = re.compile("^" + name) if cwd is None: cwd = os.getcwd() def findAppWithLargestId(desktop, find): """ Find the application with the largest id whose name matches find If ids are not recycled (i.e., ids always increment and never start over again at 1), the application with the highest id will be the last launched. We're making this assumption. """ appWithLargestId = None apps = utils.findAllDescendants( desktop, lambda x: pyatspi.ROLE_APPLICATION == x.role and find.search(x.name), False ) if len(apps) > 0: appWithLargestId = apps[0] for a in apps: if a._accessible.id > appWithLargestId._accessible.id: appWithLargestId = a return appWithLargestId # before we launch the application, check to see if there is another # instance of the application already open existingApp = findAppWithLargestId(_desktop, find) # launch the application subproc = subprocess.Popen(args, cwd=cwd, env=env) # wait for the application to launch and for the applications list to # settle. if we try to list the desktop's applications too soon, we get # crashes sometimes. sleep(wait) def findNewApplication(): """ Find the application we just launched If there is an existing application, make sure the app we find here has an id larger than the existing application. If no application is found, wait and retry a number of times before returning None. """ for i in xrange(config.RETRY_TIMES): app = findAppWithLargestId(_desktop, find) try: if existingApp is None or existingApp.id < app.id: return app except (LookupError, pyatspi.ORBit.CORBA.COMM_FAILURE): return app sleep(config.RETRY_INTERVAL) raise errors.SearchError app = findNewApplication() if cache: addApplication(app) return (app, subproc)