def findChildren(self, pred, recursive=True, isLambda=False): """ Find all children/descendents satisfying the predicate. You can also use lambdas in place of pred that will enable search also against pure dogtail Node properties (like showing). I.e: "lambda x: x.roleName == 'menu item' and x.showing is True". isLambda does not have to be set, it's kept only for api compatibility. """ # always use lambda search, but we keep isLambda param for api compatibility compare_func = None if isLambda is True or isinstance(pred, LambdaType): compare_func = pred else: assert isinstance(pred, predicate.Predicate) compare_func = pred.satisfiedByNode results = [] numAttempts = 0 while numAttempts < config.searchCutoffCount: if numAttempts >= config.searchWarningThreshold or config.debugSearching: logger.log("a11y errors caught, making attempt %i" % numAttempts) try: if recursive: results = pyatspi.utils.findAllDescendants(self, compare_func) else: results = list(filter(compare_func, self.children)) break except (GLib.GError, TypeError): numAttempts += 1 if numAttempts == config.searchCutoffCount: logger.log("warning: errors caught from the a11y tree, giving up search") else: sleep(config.searchBackoffDuration) continue return results
def selecttab(root, tabname): try: find_pattern(root, tabname, "page tab").click() except: vmmlogger.log( "Error: failed to select tab : %s in node [%s]" % tabname, root) raise
def satisfiedByNode(node): # labelled nodes are handled specially: if self.label: # this reverses the search; we're looking for a node with LABELLED_BY # and then checking the label, rather than looking for a label and # then returning whatever LABEL_FOR targets if node.labeller: return stringMatches(self.label, node.labeller.name) else: return False else: # Ensure the node matches any criteria that were set: try: if self.name: if not stringMatches(self.name, node.name): return False if self.roleName: if self.roleName != node.roleName: return False if self.description: if self.description != node.description: return False except GLib.GError as e: if re.match(r"name :[0-9]+\.[0-9]+ was not provided", e.message): logger.log("Dogtail: warning: omiting possibly broken at-spi application record") return False else: raise e return True
def checkForA11yInteractively(): # pragma: no cover """ Checks if accessibility is enabled, and presents a dialog prompting the user if it should be enabled if it is not already, then halts execution. """ if isA11yEnabled(): return dialog = Gtk.Dialog('Enable Assistive Technology Support?', None, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, (Gtk.STOCK_QUIT, Gtk.ResponseType.CLOSE, "_Enable", Gtk.ResponseType.ACCEPT)) question = """Dogtail requires that Assistive Technology Support be enabled for it to function. Would you like to enable Assistive Technology support now? Note that you will have to log out for the change to fully take effect. """.strip() dialog.set_default_response(Gtk.ResponseType.ACCEPT) questionLabel = Gtk.Label(label=question) questionLabel.set_line_wrap(True) dialog.vbox.pack_start(questionLabel, True, True, 0) dialog.show_all() result = dialog.run() if result == Gtk.ResponseType.ACCEPT: logger.log("Enabling accessibility...") enableA11y() elif result == Gtk.ResponseType.CLOSE: bailBecauseA11yIsDisabled() dialog.destroy()
def checkForA11yInteractively(): # pragma: no cover """ Checks if accessibility is enabled, and presents a dialog prompting the user if it should be enabled if it is not already, then halts execution. """ if isA11yEnabled(): return dialog = Gtk.Dialog( 'Enable Assistive Technology Support?', None, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, (Gtk.STOCK_QUIT, Gtk.ResponseType.CLOSE, "_Enable", Gtk.ResponseType.ACCEPT)) question = """Dogtail requires that Assistive Technology Support be enabled for it to function. Would you like to enable Assistive Technology support now? Note that you will have to log out for the change to fully take effect. """.strip() dialog.set_default_response(Gtk.ResponseType.ACCEPT) questionLabel = Gtk.Label(label=question) questionLabel.set_line_wrap(True) dialog.vbox.pack_start(questionLabel, True, True, 0) dialog.show_all() result = dialog.run() if result == Gtk.ResponseType.ACCEPT: logger.log("Enabling accessibility...") enableA11y() elif result == Gtk.ResponseType.CLOSE: bailBecauseA11yIsDisabled() dialog.destroy()
def detectDistro(): logger.log("Detecting distribution:", newline=False) if os.environ.get("CERTIFIED_GNOMIE", "no") == "yes": distro = JHBuild() # pragma: no cover elif os.path.exists("/etc/SuSE-release"): distro = Suse() # pragma: no cover elif os.path.exists("/etc/fedora-release"): distro = Fedora() # pragma: no cover elif os.path.exists("/etc/redhat-release"): distro = RHEL() # pragma: no cover elif os.path.exists("/usr/share/doc/ubuntu-minimal"): distro = Ubuntu() elif os.path.exists("/etc/debian_version"): # pragma: no cover distro = Debian() # pragma: no cover elif os.path.exists("/etc/gentoo-release"): # pragma: no cover distro = Gentoo() # pragma: no cover elif os.path.exists("/etc/slackware-version"): # pragma: no cover raise DistributionNotSupportedError("Slackware") # pragma: no cover elif os.path.exists("/var/lib/conarydb/conarydb"): # pragma: no cover distro = Conary() # pragma: no cover elif os.path.exists("/etc/release") and \ re.match(".*Solaris", open("/etc/release").readline()): # pragma: no cover distro = Solaris() # pragma: no cover elif os.path.exists("/etc/os-release") and \ re.match(".*GNOME-Continuous", open("/etc/os-release").readline()): # pragma: no cover distro = GnomeContinuous() # pragma: no cover else: raise DistributionNotSupportedError("Unknown") # pragma: no cover logger.log(distro.__class__.__name__) return distro
def satisfiedByNode(node): try: if self.description == None: return node.roleName == 'application' and stringMatches( self.appName, node.name) else: return node.roleName == 'application' and stringMatches( self.appName, node.name) and stringMatches( self.description, node.description) except GLib.GError as e: if re.match(r"name :[0-9]+\.[0-9]+ was not provided", e.message): logger.log( "Dogtail: warning: omiting possibly broken at-spi application record" ) return False else: try: sleep(config.defaults['searchWarningThreshold']) return node.roleName == 'application' and stringMatches( self.appName, node.name) except GLib.GError: logger.log( "Dogtail: warning: application may be hanging") return False
def absoluteMotionWithTrajectory(source_x, source_y, dest_x, dest_y, mouseDelay=None, check=True): """ Synthetize mouse absolute motion with trajectory. The 'trajectory' means that the whole motion is divided into several atomic movements which are synthetized separately. """ if check: checkCoordinates(source_x, source_y) checkCoordinates(dest_x, dest_y) logger.log("Mouse absolute motion with trajectory to (%s,%s)" % (dest_x, dest_y)) dx = float(dest_x - source_x) dy = float(dest_y - source_y) max_len = max(abs(dx), abs(dy)) if max_len == 0: # actually, no motion requested return dx /= max_len dy /= max_len act_x = float(source_x) act_y = float(source_y) for _ in range(0, int(max_len)): act_x += dx act_y += dy if mouseDelay: doDelay(mouseDelay) registry.generateMouseEvent(int(act_x), int(act_y), name='abs') if mouseDelay: doDelay(mouseDelay) else: doDelay()
def satisfiedByNode(node): # labelled nodes are handled specially: if self.label: # this reverses the search; we're looking for a node with LABELLED_BY # and then checking the label, rather than looking for a label and # then returning whatever LABEL_FOR targets if node.labeller: return stringMatches(self.label, node.labeller.name) else: return False else: # Ensure the node matches any criteria that were set: try: if self.name: if not stringMatches(self.name, node.name): return False if self.roleName: if self.roleName != node.roleName: return False if self.description: if self.description != node.description: return False except GLib.GError as e: if re.match(r"name :[0-9]+\.[0-9]+ was not provided", e.message): logger.log( "Dogtail: warning: omiting possibly broken at-spi application record" ) return False else: raise e return True
def doubleClick(self, button=1): """ Generates a raw mouse double-click event, using the specified button. """ clickX = self.position[0] + self.size[0] / 2 clickY = self.position[1] + self.size[1] / 2 if config.debugSearching: logger.log("raw click on %s %s at (%s,%s)" % (self.name, self.getLogString(), str(clickX), str(clickY))) rawinput.doubleClick(clickX, clickY, button)
def findChild(self, pred, recursive=True, debugName=None, retry=True, requireResult=True, showingOnly=None): """ Search for a node satisyfing the predicate, returning a Node. If retry is True (the default), it makes multiple attempts, backing off and retrying on failure, and eventually raises a descriptive exception if the search fails. If retry is False, it gives up after one attempt. If requireResult is True (the default), an exception is raised after all attempts have failed. If it is false, the function simply returns None. """ def describeSearch(parent, pred, recursive, debugName): """ Internal helper function """ if recursive: noun = "descendent" else: noun = "child" if debugName is None: debugName = pred.describeSearchResult() return str("%s of %s: %s") % (str(noun), parent.getLogString(), str(debugName)) compare_func = None if isinstance(pred, LambdaType): compare_func = pred if debugName is None: debugName = "child satisyfing a custom lambda function" else: assert isinstance(pred, predicate.Predicate) compare_func = pred.satisfiedByNode numAttempts = 0 while numAttempts < config.searchCutoffCount: if numAttempts >= config.searchWarningThreshold or config.debugSearching: logger.log(str("searching for %s (attempt %i)") % (describeSearch(self, pred, recursive, debugName), numAttempts)) result = self._fastFindChild(compare_func, recursive, showingOnly=showingOnly) if result: assert isinstance(result, Node) if debugName: result.debugName = debugName else: result.debugName = pred.describeSearchResult() return result else: if not retry: break numAttempts += 1 if config.debugSearching or config.debugSleep: logger.log("sleeping for %f" % config.searchBackoffDuration) sleep(config.searchBackoffDuration) if requireResult: raise SearchError(describeSearch(self, pred, recursive, debugName))
def release(x, y, button=1, check=True): """ Synthesize a mouse button release at (x,y) """ if check: checkCoordinates(x, y) logger.log("Mouse button %s release at (%s,%s)" % (button, x, y)) registry.generateMouseEvent(x, y, name='b%sr' % button) doDelay()
def doubleClick(x, y, button=1, check=True): """ Synthesize a mouse button double-click at (x,y) """ if check: checkCoordinates(x, y) logger.log("Mouse button %s doubleclick at (%s,%s)" % (button, x, y)) registry.generateMouseEvent(x, y, name='b%sd' % button) doDelay()
def click(x, y, button=1, check=True): """ Synthesize a mouse button click at (x,y) """ if check: checkCoordinates(x, y) logger.log("Mouse button %s click at (%s,%s)" % (button, x, y)) registry.generateMouseEvent(x, y, name='b%sc' % button) doDelay(config.actionDelay)
def rootwindow(self): if self._rootwindow is None: try: self._rootwindow = find_pattern(self.root, "New VM", "frame") except: vmmlogger.log("Failed to set property: %s in vmmAddNewVM" % "rootwindow") raise return self._rootwindow
def doDelay(delay=None): """ Utility function to insert a delay (with logging and a configurable default delay) """ if delay is None: delay = config.defaultDelay if config.debugSleep: logger.log("sleeping for %f" % delay) sleep(delay)
def rootwindow(self): if self._rootwindow is None: try: self._rootwindow = find_pattern(self.root, "Preferences", "frame") except: # will replace all print with logging functions vmmlogger.log("Failed to set property: %s" % "rootwindow") raise return self._rootwindow
def warn(message, caller=True): """ Generate a warning, and pass it to the debug logger. """ frameRec = inspect.stack()[-1] message = "Warning: %s:%s: %s" % (frameRec[1], frameRec[2], message) if caller and frameRec[1] != '<stdin>' and frameRec[1] != '<string>': message = message + ':\n ' + frameRec[4][0] del frameRec logger.log(message)
def doubleClick(self, button=1): """ Generates a raw mouse double-click event, using the specified button. """ clickX = self.position[0] + self.size[0] / 2 clickY = self.position[1] + self.size[1] / 2 if config.debugSearching: logger.log(str("raw click on %s %s at (%s,%s)") % (str(self.name), self.getLogString(), str(clickX), str(clickY))) rawinput.doubleClick(clickX, clickY, button)
def relativeMotion(x, y, mouseDelay=None): """ Synthetize a relative motion from actual position. Note: Does not check if the end coordinates are positive. """ logger.log("Mouse relative motion of (%s,%s)" % (x, y)) registry.generateMouseEvent(x, y, name='rel') if mouseDelay: doDelay(mouseDelay) else: doDelay()
def point(self, mouseDelay=None): """ Move mouse cursor to the center of the widget. """ pointX = self.position[0] + self.size[0] / 2 pointY = self.position[1] + self.size[1] / 2 logger.log("Pointing on %s %s at (%s,%s)" % (self.name, self.getLogString(), str(pointX), str(pointY))) rawinput.registry.generateMouseEvent(pointX, pointY, "abs") if mouseDelay: doDelay(mouseDelay) else: doDelay()
def absoluteMotion(x, y, mouseDelay=None, check=True): """ Synthesize mouse absolute motion to (x,y) """ if check: checkCoordinates(x, y) logger.log("Mouse absolute motion to (%s,%s)" % (x, y)) registry.generateMouseEvent(x, y, name='abs') if mouseDelay: doDelay(mouseDelay) else: doDelay()
def rootwindow(self): if self._rootwindow is None: try: #print_nodes(self.root) self._rootwindow = find_pattern(self.root, ".* Connection Details", "frame") except: vmmlogger.log( "Failed to set property: %s in vmmConnectionDetails" % "rootwindow") raise return self._rootwindow
def click(self, button=1): """ Generates a raw mouse click event, using the specified button. - 1 is left, - 2 is middle, - 3 is right. """ logger.log("Clicking on %s" % self.getLogString()) clickX = self.position[0] + self.size[0] / 2 clickY = self.position[1] + self.size[1] / 2 if config.debugSearching: logger.log("raw click on %s %s at (%s,%s)" % (self.name, self.getLogString(), str(clickX), str(clickY))) rawinput.click(clickX, clickY, button)
def point(self, mouseDelay=None): """ Move mouse cursor to the center of the widget. """ pointX = self.position[0] + self.size[0] / 2 pointY = self.position[1] + self.size[1] / 2 logger.log(str("Pointing on %s %s at (%s,%s)") % (str(self.name), self.getLogString(), str(pointX), str(pointY))) rawinput.registry.generateMouseEvent(pointX, pointY, 'abs') if mouseDelay: doDelay(mouseDelay) else: doDelay()
def screenshot(file='screenshot.png', timeStamp=True): """ This function wraps the ImageMagick import command to take a screenshot. The file argument may be specified as 'foo', 'foo.png', or using any other extension that ImageMagick supports. PNG is the default. By default, screenshot filenames are in the format of foo_YYYYMMDD-hhmmss.png . The timeStamp argument may be set to False to name the file foo.png. """ if not isinstance(timeStamp, bool): raise TypeError("timeStampt must be True or False") # config is supposed to create this for us. If it's not there, bail. assert os.path.isdir(config.scratchDir) baseName = ''.join(file.split('.')[0:-1]) fileExt = file.split('.')[-1].lower() if not baseName: baseName = file fileExt = 'png' if timeStamp: ts = TimeStamp() newFile = ts.fileStamp(baseName) + '.' + fileExt path = config.scratchDir + newFile else: newFile = baseName + '.' + fileExt path = config.scratchDir + newFile from gi.repository import Gdk from gi.repository import GdkPixbuf rootWindow = Gdk.get_default_root_window() geometry = rootWindow.get_geometry() pixbuf = GdkPixbuf.Pixbuf(colorspace=GdkPixbuf.Colorspace.RGB, has_alpha=False, bits_per_sample=8, width=geometry[2], height=geometry[3]) pixbuf = Gdk.pixbuf_get_from_window(rootWindow, 0, 0, geometry[2], geometry[3]) # GdkPixbuf.Pixbuf.save() needs 'jpeg' and not 'jpg' if fileExt == 'jpg': fileExt = 'jpeg' try: pixbuf.savev(path, fileExt, [], []) except GLib.GError: raise ValueError("Failed to save screenshot in %s format" % fileExt) assert os.path.exists(path) logger.log("Screenshot taken: " + path) return path
def satisfiedByNode(node): try: return node.roleName == 'application' and stringMatches(self.appName, node.name) except GLib.GError as e: if re.match(r"name :[0-9]+\.[0-9]+ was not provided", e.message): logger.log("Dogtail: warning: omiting possibly broken at-spi application record") return False else: try: sleep(config.defaults['searchWarningThreshold']) return node.roleName == 'application' and stringMatches(self.appName, node.name) except GLib.GError: logger.log("Dogtail: warning: application may be hanging") return False
def text(self, text): try: if config.debugSearching: msg = "Setting text of %s to %s" # Let's not get too crazy if 'text' is really large... # FIXME: Sometimes the next line screws up Unicode strings. if len(text) > 140: txt = text[:134] + " [...]" else: txt = text logger.log(str(msg) % (self.getLogString(), str("'%s'") % str(txt))) self.queryEditableText().setTextContents(text) except NotImplementedError: raise AttributeError("can't set attribute")
def load(packageName, language='', getDependencies=True): for moFile in getMoFilesForPackage(packageName, language, getDependencies): # Searching the popt mo-files for translations makes gettext bail out, # so we ignore them here. This is # https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=172155 . if not('popt.mo' in moFile or moFile in moFiles): try: translationDbs.append(GettextTranslationDb(moFile)) moFiles[moFile] = None except (AttributeError, IndexError): if config.config.debugTranslation: #import traceback # logger.log(traceback.format_exc()) logger.log("Warning: Failed to load mo-file for translation: " + moFile)
def click(self, button=1): """ Generates a raw mouse click event, using the specified button. - 1 is left, - 2 is middle, - 3 is right. """ logger.log(str("Clicking on %s") % self.getLogString()) clickX = self.position[0] + self.size[0] / 2 clickY = self.position[1] + self.size[1] / 2 if config.debugSearching: logger.log(str("raw click on %s %s at (%s,%s)") % (str(self.name), self.getLogString(), str(clickX), str(clickY))) rawinput.click(clickX, clickY, button)
def children(self): """ A list of this Accessible's children """ if self.parent and self.parent.roleName == 'hyper link': print(self.parent.role) return [] children = [] childCount = self.childCount if childCount > config.childrenLimit: global haveWarnedAboutChildrenLimit if not haveWarnedAboutChildrenLimit: logger.log("Only returning %s children. You may change " "config.childrenLimit if you wish. This message will only" " be printed once." % str(config.childrenLimit)) haveWarnedAboutChildrenLimit = True childCount = config.childrenLimit for i in range(childCount): # Workaround for GNOME bug #465103 # also solution for GNOME bug #321273 try: child = self[i] except LookupError: child = None if child: children.append(child) invalidChildren = childCount - len(children) if invalidChildren and config.debugSearching: logger.log(str("Skipped %s invalid children of %s") % (invalidChildren, str(self))) try: ht = self.queryHypertext() for li in range(ht.getNLinks()): link = ht.getLink(li) for ai in range(link.nAnchors): child = link.getObject(ai) if child == self: continue child.__setupUserData() child.user_data['linkAnchor'] = \ LinkAnchor(node=child, hypertext=ht, linkIndex=li, anchorIndex=ai) children.append(child) except (NotImplementedError, AttributeError): pass return children
def drag(fromXY, toXY, button=1, check=True): """ Synthesize a mouse press, drag, and release on the screen. """ logger.log("Mouse button %s drag from %s to %s" % (button, fromXY, toXY)) (x, y) = fromXY press(x, y, button, check) (x, y) = toXY absoluteMotion(x, y, check=check) doDelay() release(x, y, button, check) doDelay()
def dragWithTrajectory(fromXY, toXY, button=1, check=True): """ Synthetize a mouse press, drag (including move events), and release on the screen """ logger.log("Mouse button %s drag with trajectory from %s to %s" % (button, fromXY, toXY)) (x, y) = fromXY press(x, y, button, check) (x, y) = toXY absoluteMotionWithTrajectory(fromXY[0], fromXY[1], x, y, check=check) doDelay() release(x, y, button, check) doDelay()
def bailBecauseA11yIsDisabled(): if sys.argv[0].endswith("pydoc"): return try: with open("/proc/%s/cmdline" % os.getpid(), 'r') as f: content = f.read() if content.find('epydoc') != -1: return # pragma: no cover if content.find('sphinx') != -1: return # pragma: no cover except: # pragma: no cover pass # pragma: no cover logger.log("Dogtail requires that Assistive Technology support be enabled." "\nYou can enable accessibility with sniff or by running:\n" "'gsettings set org.gnome.desktop.interface toolkit-accessibility true'\nAborting...") sys.exit(1)
def clickForward(self): """ Click on the 'Forward' button to advance to next page of wizard. It will log the title of the new page that is reached. FIXME: what if it's Next rather than Forward ??? This will only work if your libgnomeui has accessible buttons; see above. """ fwd = self.child("Forward") fwd.click() # Log the new wizard page; it's helpful when debugging scripts logger.log(str("%s is now on '%s' page") % (self, str(self.getPageTitle())))
def do(self): """ Performs the given tree.Action, with appropriate delays and logging. """ logger.log(str("%s on %s") % (str(self.name), self.node.getLogString())) if not self.node.sensitive: if config.ensureSensitivity: raise NotSensitiveError(self) else: nSE = NotSensitiveError(self) logger.log("Warning: " + str(nSE)) if config.blinkOnActions: self.node.blink() result = self.__action.doAction(self.__index) doDelay(config.actionDelay) return result
def translate(srcString): """ Look up srcString in the various translation databases (if any), returning a list of all matches found (potentially the empty list) """ # Use a dict to get uniqueness: results = {} # Try to translate the string: for translationDb in translationDbs: for result in translationDb.getTranslationsOf(srcString): results[result] = True # No translations found: if len(results) == 0: if config.config.debugTranslation: logger.log('Translation not found for "%s"' % srcString) return list(results.keys())
def bailBecauseA11yIsDisabled(): if sys.argv[0].endswith("pydoc"): return try: with open("/proc/%s/cmdline" % os.getpid(), 'r') as f: content = f.read() if content.find('epydoc') != -1: return # pragma: no cover if content.find('sphinx') != -1: return # pragma: no cover except: # pragma: no cover pass # pragma: no cover logger.log( "Dogtail requires that Assistive Technology support be enabled." "\nYou can enable accessibility with sniff or by running:\n" "'gsettings set org.gnome.desktop.interface toolkit-accessibility true'\nAborting..." ) sys.exit(1)
def getRelativeSearch(self): """ Get a (ancestorNode, predicate, isRecursive) triple that identifies the best way to find this Node uniquely. FIXME: or None if no such search exists? FIXME: may need to make this more robust FIXME: should this be private? """ if config.debugSearchPaths: logger.log("getRelativeSearchPath(%s)" % self) assert self assert self.parent isRecursive = False ancestor = self.parent # iterate up ancestors until you reach an identifiable one, # setting the search to be isRecursive if need be: while not self.__nodeIsIdentifiable(ancestor): ancestor = ancestor.parent isRecursive = True # Pick the most appropriate predicate for finding this node: if self.labellee: if self.labellee.name: return (ancestor, predicate.IsLabelledAs(self.labellee.name), isRecursive) if self.roleName == 'menu': return (ancestor, predicate.IsAMenuNamed(self.name), isRecursive) elif self.roleName == 'menu item' or self.roleName == 'check menu item': return (ancestor, predicate.IsAMenuItemNamed(self.name), isRecursive) elif self.roleName == 'text': return (ancestor, predicate.IsATextEntryNamed(self.name), isRecursive) elif self.roleName == 'push button': return (ancestor, predicate.IsAButtonNamed(self.name), isRecursive) elif self.roleName == 'frame': return (ancestor, predicate.IsAWindowNamed(self.name), isRecursive) elif self.roleName == 'dialog': return (ancestor, predicate.IsADialogNamed(self.name), isRecursive) else: pred = predicate.GenericPredicate( name=self.name, roleName=self.roleName) return (ancestor, pred, isRecursive)
def getAbsoluteSearchPath(self): """ FIXME: this needs rewriting... Generate a SearchPath instance giving the 'best' way to find the Accessible wrapped by this node again, starting at the root and applying each search in turn. This is somewhat analagous to an absolute path in a filesystem, except that some of searches may be recursive, rather than just searching direct children. Used by the recording framework for identifying nodes in a persistent way, independent of the style of script being written. FIXME: try to ensure uniqueness FIXME: need some heuristics to get 'good' searches, whatever that means """ if config.debugSearchPaths: logger.log("getAbsoluteSearchPath(%s)" % self) if self.roleName == 'application': result = path.SearchPath() result.append(predicate.IsAnApplicationNamed(self.name), False) return result else: if self.parent: (ancestor, pred, isRecursive) = self.getRelativeSearch() if config.debugSearchPaths: logger.log("got ancestor: %s" % ancestor) ancestorPath = ancestor.getAbsoluteSearchPath() ancestorPath.append(pred, isRecursive) return ancestorPath else: # This should be the root node: return path.SearchPath()
def keyCombo(self, comboString): if config.debugSearching: logger.log("Pressing keys '%s' into %s" % (comboString, self.getLogString())) if self.focusable: if not self.focused: try: self.grabFocus() except Exception: logger.log("Node is focusable but I can't grabFocus!") else: logger.log("Node is not focusable; trying key combo anyway") rawinput.keyCombo(comboString)