Best Python code snippet using fMBT_python
fmbtgti.py
Source:fmbtgti.py  
...267    into fmbtgti.GUITestInterface instances and Screenshots.268    To implement an OCR engine, you need to override _findText() at269    minimum. See _findText documentation in this class for270    requirements.271    If possible in your OCR engine, you can provide _dumpOcr() to272    reveal what is recognized in screenshots.273    For efficient caching of results and freeing cached results, you274    can override _addScreenshot() and _removeScreenshot(). Every275    screenshot is added before findText() or dumpOcr().276    A typical usage of OcrEngine instance:277    - oe.addScreenshot(ss)278    - oe.findText(ss, text1, <engine/screenshot/find-specific-args>)279    - oe.findText(ss, text2, <engine/screenshot/find-specific-args>)280    - oe.removeScreenshot(ss)281    Note that there may be several screenshots added before they are282    removed.283    """284    def __init__(self, *args, **kwargs):285        super(OcrEngine, self).__init__(*args, **kwargs)286        self._ssFindTextDefaults = {}287        self._findTextDefaults = {}288        ocrFindArgs, _ = _takeOcrArgs(self, kwargs)289        self._setFindTextDefaults(ocrFindArgs)290    def dumpOcr(self, screenshot, **kwargs):291        """292        Returns what is recognized in the screenshot. For debugging293        purposes.294        """295        ocrArgs = self.__ocrArgs(screenshot, **kwargs)296        return self._dumpOcr(screenshot, **ocrArgs)297    def _dumpOcr(self, screenshot, **kwargs):298        return None299    def addScreenshot(self, screenshot, **findTextDefaults):300        """301        Prepare for finding text from the screenshot.302        Parameters:303          screenshot (fmbtgti.Screenshot)304                  screenshot object to be searched from.305          other parameters (optional)306                  findText defaults for this screenshot.307        Notice that there may be many screenshots simultaneously.308        Do not keep reference to the screenshot object.309        """310        self.setScreenshotFindTextDefaults(screenshot, **findTextDefaults)311        return self._addScreenshot(screenshot, **findTextDefaults)312    def _addScreenshot(self, screenshot, **findTextDefaults):313        pass314    def removeScreenshot(self, screenshot):315        """316        OCR queries on the screenshot will not be made anymore.317        """318        self._removeScreenshot(screenshot)319        try:320            del self._ssFindTextDefaults[id(screenshot)]321        except KeyError:322            raise KeyError('screenshot "%s" does not have findTextDefaults. '323                           'If OcrEngine.addScreenshot() is overridden, it '324                           '*must* call parent\'s addScreenshot.' % (screenshot.filename(),))325    def _removeScreenshot(self, screenshot):326        pass327    def setFindTextDefaults(self, **findTextDefaults):328        return self._setFindTextDefaults(findTextDefaults, screenshot=None)329    def setScreenshotFindTextDefaults(self, screenshot, **findTextDefaults):330        return self._setFindTextDefaults(findTextDefaults, screenshot=screenshot)331    def _setFindTextDefaults(self, defaults, screenshot=None):332        """333        Set default values for optional arguments for findText().334        Parameters:335          defaults (dictionary)336                  Default keyword arguments and their values.337          screenshot (optional, fmbtgti.Screenshot instance)338                  Use the defaults for findText on this screenshot. If339                  the defaults are None, make them default for all340                  screenshots. Screenshot-specific defaults override341                  engine default.342        """343        if screenshot == None:344            self._findTextDefaults.update(defaults)345        else:346            ssid = id(screenshot)347            if not ssid in self._ssFindTextDefaults:348                self._ssFindTextDefaults[ssid] = self._findTextDefaults.copy()349            self._ssFindTextDefaults[ssid].update(defaults)350    def findTextDefaults(self, screenshot=None):351        if screenshot == None:352            return self._findTextDefaults353        elif id(screenshot) in self._ssFindTextDefaults:354            return self._ssFindTextDefaults[id(screenshot)]355        else:356            return None357    def _findTextArgNames(self):358        """359        Returns names of optional findText arguments.360        """361        return inspect.getargspec(self._findText).args[3:]362    def __ocrArgs(self, screenshot, **priorityArgs):363        ocrArgs = {}364        ocrArgs.update(self._findTextDefaults)365        ssId = id(screenshot)366        if ssId in self._ssFindTextDefaults:367            ocrArgs.update(self._ssFindTextDefaults[ssId])368        ocrArgs.update(priorityArgs)369        return ocrArgs370    def findText(self, screenshot, text, **kwargs):371        """372        Return list of fmbtgti.GUIItems that match to text.373        """374        ocrArgs = self.__ocrArgs(screenshot, **kwargs)375        return self._findText(screenshot, text, **ocrArgs)376    def _findText(self, screenshot, text, **kwargs):377        """378        Find appearances of text from the screenshot.379        Parameters:380          screenshot (fmbtgti.Screenshot)381                  Screenshot from which text is to be searched382                  for. Use Screenshot.filename() to get the filename.383          text (string)384                  text to be searched for.385          other arguments (engine specific)386                  kwargs contain keyword arguments given to387                  findText(screenshot, text, ...), already extended388                  first with screenshot-specific findTextDefaults, then389                  with engine-specific findTextDefaults.390                  _findText *must* define all engine parameters as391                  explicit keyword arguments:392                  def _findText(self, screenshot, text, engArg1=42):393                      ...394        Return list of fmbtgti.GUIItems.395        """396        raise NotImplementedError("_findText needed but not implemented.")397class _EyenfingerOcrEngine(OcrEngine):398    """399    OCR engine parameters that can be used in all400    ...OcrText() methods (swipeOcrText, tapOcrText, findItemsByOcrText, ...):401      match (float, optional):402              minimum match score in range [0.0, 1.0].  The default is403              1.0 (exact match).404      area ((left, top, right, bottom), optional):405              search from the given area only. Left, top, right and406              bottom are either absolute coordinates (integers) or407              floats in range [0.0, 1.0]. In the latter case they are408              scaled to screenshot dimensions. The default is (0.0,409              0.0, 1.0, 1.0), that is, search everywhere in the410              screenshot.411      pagesegmodes (list of integers, optional):412              try all integers as tesseract -pagesegmode413              arguments. The default is [3], another good option could414              be [3, 6].415      preprocess (string, optional):416              preprocess filter to be used in OCR for better417              result. Refer to eyenfinger.autoconfigure to search for418              a good one.419    """420    class _OcrResults(object):421        __slots__ = ("filename", "screenSize", "pagesegmodes", "preprocess", "area", "words")422        def __init__(self, filename, screenSize):423            self.filename = filename424            self.screenSize = screenSize425            self.pagesegmodes = None426            self.preprocess = None427            self.area = None428            self.words = None429    def __init__(self, *args, **engineDefaults):430        engineDefaults["area"] = engineDefaults.get("area", (0.0, 0.0, 1.0, 1.0))431        engineDefaults["match"] = engineDefaults.get("match", 1.0)432        engineDefaults["pagesegmodes"] = engineDefaults.get("pagesegmodes", _OCRPAGESEGMODES)433        engineDefaults["preprocess"] = engineDefaults.get("preprocess", _OCRPREPROCESS)434        super(_EyenfingerOcrEngine, self).__init__(*args, **engineDefaults)435        self._ss = {} # OCR results for screenshots436    def _addScreenshot(self, screenshot, **findTextDefaults):437        ssId = id(screenshot)438        self._ss[ssId] = _EyenfingerOcrEngine._OcrResults(screenshot.filename(), screenshot.size())439    def _removeScreenshot(self, screenshot):440        ssId = id(screenshot)441        if ssId in self._ss:442            del self._ss[ssId]443    def _findText(self, screenshot, text, match=None, preprocess=None, area=None, pagesegmodes=None):444        ssId = id(screenshot)445        self._assumeOcrResults(screenshot, preprocess, area, pagesegmodes)446        for ppfilter in self._ss[ssId].words.keys():447            try:448                score_text_bbox_list = eyenfinger.findText(449                    text, self._ss[ssId].words[ppfilter], match=match)450                if not score_text_bbox_list:451                    continue452                else:453                    break454            except eyenfinger.BadMatch:455                continue456        else:457            return []458        retval = [GUIItem("OCR text (match %.2f)" % (score,),459                          bbox, self._ss[ssId].filename,460                          ocrFind=text, ocrFound=matching_text)461                  for score, matching_text, bbox in score_text_bbox_list]462        return retval463    def _dumpOcr(self, screenshot, match=None, preprocess=None, area=None, pagesegmodes=None):464        ssId = id(screenshot)465        if self._ss[ssId].words == None:466            self._assumeOcrResults(screenshot, preprocess, area, pagesegmodes)467        w = []468        for ppfilter in self._ss[ssId].preprocess:469            for word in self._ss[ssId].words[ppfilter]:470                for appearance, (wid, middle, bbox) in enumerate(self._ss[ssId].words[ppfilter][word]):471                    (x1, y1, x2, y2) = bbox472                    w.append((word, (x1, y1, x2, y2)))473        return sorted(set(w), key=lambda i:(i[1][1]/8, i[1][0]))474    def _assumeOcrResults(self, screenshot, preprocess, area, pagesegmodes):475        ssId = id(screenshot)476        if not type(preprocess) in (list, tuple):477            preprocess = [preprocess]478        if self._ss[ssId].words == None or self._ss[ssId].preprocess != preprocess or self._ss[ssId].area != area:479            self._ss[ssId].words = {}480            self._ss[ssId].preprocess = preprocess481            self._ss[ssId].area = area482            for ppfilter in preprocess:483                pp = ppfilter % { "zoom": "-resize %sx" % (self._ss[ssId].screenSize[0] * 2) }484                eyenfinger.iRead(source=self._ss[ssId].filename, ocr=True, preprocess=pp, ocrArea=area, ocrPageSegModes=pagesegmodes)485                self._ss[ssId].words[ppfilter] = eyenfinger._g_words486def _defaultOcrEngine():487    if _g_defaultOcrEngine:488        return _g_defaultOcrEngine489    else:490        _EyenfingerOcrEngine().register(defaultOcr=True)491        return _g_defaultOcrEngine492class OirEngine(OrEngine):493    """494    This is an abstract interface for OIR (optical image recognition)495    engines that can be plugged into fmbtgti.GUITestInterface496    instances and Screenshots.497    To implement an OIR engine, you need to override _findBitmap() at498    minimum. See _findBitmap documentation in this class for499    requirements.500    This base class provides full set of OIR parameters to501    _findBitmap. The parameters are combined from502    - OirEngine find defaults, specified when OirEngine is503      instantiated.504    - Screenshot instance find defaults.505    - bitmap / bitmap directory find defaults (read from the506      .fmbtoirrc that is in the same directory as the bitmap).507    - ...Bitmap() method parameters.508    The latter in the list override the former.509    For efficient caching of results and freeing cached results, you510    can override _addScreenshot() and _removeScreenshot(). Every511    screenshot is added before findBitmap().512    A typical usage of OirEngine instance:513    - oe.addScreenshot(ss)514    - oe.findBitmap(ss, bmpFilename1, <engine/screenshot/find-specific-args>)515    - oe.findBitmap(ss, bmpFilename2, <engine/screenshot/find-specific-args>)516    - oe.removeScreenshot(ss)517    Note that there may be several screenshots added before they are518    removed. ss is a Screenshot instance. Do not keep references to519    Screenshot intances, otherwise garbage collector will not remove520    them.521    """522    def __init__(self, *args, **kwargs):523        super(OirEngine, self).__init__(*args, **kwargs)524        self._ssFindBitmapDefaults = {}525        self._findBitmapDefaults = {}526        oirArgs, _ = _takeOirArgs(self, kwargs)527        self._setFindBitmapDefaults(oirArgs)528    def addScreenshot(self, screenshot, **findBitmapDefaults):529        """530        Prepare for finding bitmap from the screenshot.531        Parameters:532          screenshot (fmbtgti.Screenshot)533                  screenshot object to be searched from.534          other parameters (optional)535                  findBitmap defaults for this screenshot.536        Notice that there may be many screenshots simultaneously.537        Do not keep reference to the screenshot object.538        """539        self.setScreenshotFindBitmapDefaults(screenshot, **findBitmapDefaults)540        return self._addScreenshot(screenshot, **findBitmapDefaults)541    def _addScreenshot(self, screenshot, **findBitmapDefaults):542        pass543    def removeScreenshot(self, screenshot):544        """545        OIR queries on the screenshot will not be made anymore.546        """547        self._removeScreenshot(screenshot)548        try:549            del self._ssFindBitmapDefaults[id(screenshot)]550        except KeyError:551            raise KeyError('screenshot "%s" does not have findBitmapDefaults. '552                           'If OirEngine.addScreenshot() is overridden, it '553                           '*must* call parent\'s addScreenshot.' % (screenshot.filename(),))554    def _removeScreenshot(self, screenshot):555        pass556    def setFindBitmapDefaults(self, **findBitmapDefaults):557        return self._setFindBitmapDefaults(findBitmapDefaults, screenshot=None)558    def setScreenshotFindBitmapDefaults(self, screenshot, **findBitmapDefaults):559        return self._setFindBitmapDefaults(findBitmapDefaults, screenshot=screenshot)560    def _setFindBitmapDefaults(self, defaults, screenshot=None):561        """562        Set default values for optional arguments for findBitmap().563        Parameters:564          defaults (dictionary)565                  Default keyword arguments and their values.566          screenshot (optional, fmbtgti.Screenshot instance)567                  Use the defaults for findBitmap on this screenshot. If568                  the defaults are None, make them default for all569                  screenshots. Screenshot-specific defaults override570                  engine default.571        """572        if screenshot == None:573            self._findBitmapDefaults.update(defaults)574        else:575            ssid = id(screenshot)576            if not ssid in self._ssFindBitmapDefaults:577                self._ssFindBitmapDefaults[ssid] = self._findBitmapDefaults.copy()578            self._ssFindBitmapDefaults[ssid].update(defaults)579    def findBitmapDefaults(self, screenshot=None):580        if screenshot == None:581            return self._findBitmapDefaults582        elif id(screenshot) in self._ssFindBitmapDefaults:583            return self._ssFindBitmapDefaults[id(screenshot)]584        else:585            return None586    def _findBitmapArgNames(self):587        """588        Returns names of optional findBitmap arguments.589        """590        return inspect.getargspec(self._findBitmap).args[3:]591    def __oirArgs(self, screenshot, bitmap, **priorityArgs):592        oirArgs = {}593        oirArgs.update(self._findBitmapDefaults)594        ssId = id(screenshot)595        if ssId in self._ssFindBitmapDefaults:596            oirArgs.update(self._ssFindBitmapDefaults[ssId])597        oirArgs.update(priorityArgs)598        return oirArgs599    def findBitmap(self, screenshot, bitmap, **kwargs):600        """601        Return list of fmbtgti.GUIItems that match to bitmap.602        """603        oirArgs = self.__oirArgs(screenshot, bitmap, **kwargs)604        return self._findBitmap(screenshot, bitmap, **oirArgs)605    def _findBitmap(self, screenshot, bitmap, **kwargs):606        """607        Find appearances of bitmap from the screenshot.608        Parameters:609          screenshot (fmbtgti.Screenshot)610                  Screenshot from which bitmap is to be searched611                  for. Use Screenshot.filename() to get the filename.612          bitmap (string)613                  bitmap to be searched for.614          other arguments (engine specific)615                  kwargs contain keyword arguments given to616                  findBitmap(screenshot, bitmap, ...), already extended617                  first with screenshot-specific findBitmapDefaults, then618                  with engine-specific findBitmapDefaults.619                  _findBitmap *must* define all engine parameters as620                  explicit keyword arguments:621                  def _findBitmap(self, screenshot, bitmap, engArg1=42):622                      ...623        Returns list of fmbtgti.GUIItems.624        """625        raise NotImplementedError("_findBitmap needed but not implemented.")626class _Eye4GraphicsOirEngine(OirEngine):627    """OIR engine parameters that can be used in all628    ...Bitmap() methods (swipeBitmap, tapBitmap, findItemsByBitmap, ...):629      colorMatch (float, optional):630              required color matching accuracy. The default is 1.0631              (exact match). For instance, 0.75 requires that every632              pixel's every RGB component value on the bitmap is at633              least 75 % match with the value of corresponding pixel's634              RGB component in the screenshot.635      opacityLimit (float, optional):636              threshold for comparing pixels with non-zero alpha637              channel. Pixels less opaque than the given threshold are638              skipped in match comparison. The default is 0, that is,639              alpha channel is ignored.640      area ((left, top, right, bottom), optional):641              search bitmap from the given area only. Left, top right642              and bottom are either absolute coordinates (integers) or643              floats in range [0.0, 1.0]. In the latter case they are644              scaled to screenshot dimensions. The default is (0.0,645              0.0, 1.0, 1.0), that is, search everywhere in the646              screenshot.647      limit (integer, optional):648              number of returned matches is limited to the limit. The649              default is -1: all matches are returned. Applicable in650              findItemsByBitmap.651      allowOverlap (boolean, optional):652              allow returned icons to overlap. If False, returned list653              contains only non-overlapping bounding boxes. The654              default is False.655      scale (float or pair of floats, optional):656              scale to be applied to the bitmap before657              matching. Single float is a factor for both X and Y658              axis, pair of floats is (xScale, yScale). The default is659              1.0.660      bitmapPixelSize (integer, optional):661              size of pixel rectangle on bitmap for which there must662              be same color on corresponding screenshot rectangle.  If663              scale is 1.0, default is 1 (rectangle is 1x1). If scale664              != 1.0, the default is 2 (rectangle is 2x2).665      screenshotPixelSize (integer, optional):666              size of pixel rectangle on screenshot in which there667              must be a same color pixel as in the corresponding668              rectangle on bitmap. The default is scale *669              bitmapPixelSize.670    If unsure about parameters, but you have a bitmap that should be671    detected in a screenshot, try obj.oirEngine().adjustParameters().672    Example:673    d.enableVisualLog("params.html")674    screenshot = d.refreshScreenshot()675    results = d.oirEngine().adjustParameters(screenshot, "mybitmap.png")676    if results:677        item, params = results[0]678        print "found %s with parameters:" % (item,)679        print "\n".join(sorted(["  %s = %s" % (k, params[k]) for k in params]))680        print "verify:", d.verifyBitmap("mybitmap.png", **params)681    Notice, that you can force refreshScreenshot to load old screenshot:682    d.refreshScreenshot("old.png")683    """684    def __init__(self, *args, **engineDefaults):685        engineDefaults["colorMatch"] = engineDefaults.get("colorMatch", 1.0)686        engineDefaults["opacityLimit"] = engineDefaults.get("opacityLimit", 0.0)687        engineDefaults["area"] = engineDefaults.get("area", (0.0, 0.0, 1.0, 1.0))688        engineDefaults["limit"] = engineDefaults.get("limit", -1)689        engineDefaults["allowOverlap"] = engineDefaults.get("allowOverlap", False)690        engineDefaults["scale"] = engineDefaults.get("scale", 1.0)691        engineDefaults["bitmapPixelSize"] = engineDefaults.get("bitmapPixelSize", 0)692        engineDefaults["screenshotPixelSize"] = engineDefaults.get("screenshotPixelSize", 0)693        OirEngine.__init__(self, *args, **engineDefaults)694        self._openedImages = {}695        self._findBitmapCache = {}696    def _addScreenshot(self, screenshot, **findBitmapDefaults):697        filename = screenshot.filename()698        self._openedImages[filename] = eye4graphics.openImage(filename)699        # make sure size() is available, this can save an extra700        # opening of the screenshot file.701        if screenshot.size(allowReadingFile=False) == None:702            screenshot.setSize(_e4gImageDimensions(self._openedImages[filename]))703        self._findBitmapCache[filename] = {}704    def _removeScreenshot(self, screenshot):705        filename = screenshot.filename()706        eye4graphics.closeImage(self._openedImages[filename])707        del self._openedImages[filename]708        del self._findBitmapCache[filename]709    def adjustParameters(self, screenshot, bitmap,710                         scaleRange = [p/100.0 for p in range(110,210,10)],711                         colorMatchRange = [p/100.0 for p in range(100,60,-10)],712                         pixelSizeRange = range(2,5),713                         resultCount = 1,714                         **oirArgs):715        """716        Search for scale, colorMatch, bitmapPixelSize and717        screenshotPixelSize parameters that find the bitmap in the718        screenshot.719        Parameters:720          screenshot (Screenshot instance):721                  screenshot that contains the bitmap.722          bitmap (string):723                  bitmap filename.724          scaleRange (list of floats, optional):725                  scales to go through.726                  The default is: 1.1, 1.2, ... 2.0.727          colorMatchRange (list of floats, optional):728                  colorMatches to try out.729                  The default is: 1.0, 0.9, ... 0.7.730          pixelSizeRange (list of integers, optional):731                  values for bitmapPixelSize and screenshotPixelSize.732                  The default is: [2, 3]733          resultCount (integer, optional):734                  number of parameter combinations to be found.735                  The default is 1. 0 is unlimited.736          other OIR parameters: as usual, refer to engine documentation.737        Returns list of pairs: (GUIItem, findParams), where738        GUIItem is the detected item (GUIItem.bbox() is the box around it),739        and findParams is a dictionary containing the parameters.740        """741        if not screenshot.filename() in self._findBitmapCache:742            self.addScreenshot(screenshot)743            ssAdded = True744        else:745            ssAdded = False746        retval = []747        for colorMatch in colorMatchRange:748            for pixelSize in pixelSizeRange:749                for scale in scaleRange:750                    findParams = oirArgs.copy()751                    findParams.update({"colorMatch": colorMatch,752                                       "limit": 1,753                                       "scale": scale,754                                       "bitmapPixelSize": pixelSize,755                                       "screenshotPixelSize": pixelSize})756                    results = self.findBitmap(screenshot, bitmap,757                                               **findParams)758                    if results:759                        retval.append((results[0], findParams))760                        if len(retval) == resultCount:761                            return retval762        if ssAdded:763            self.removeScreenshot(screenshot)764        return retval765    def _findBitmap(self, screenshot, bitmap, colorMatch=None,766                    opacityLimit=None, area=None, limit=None,767                    allowOverlap=None, scale=None,768                    bitmapPixelSize=None, screenshotPixelSize=None):769        """770        Find items on the screenshot that match to bitmap.771        """772        ssFilename = screenshot.filename()773        ssSize = screenshot.size()774        cacheKey = (bitmap, colorMatch, opacityLimit, area, limit,775                    scale, bitmapPixelSize, screenshotPixelSize)776        if cacheKey in self._findBitmapCache[ssFilename]:777            return self._findBitmapCache[ssFilename][cacheKey]778        self._findBitmapCache[ssFilename][cacheKey] = []779        e4gIcon = eye4graphics.openImage(bitmap)780        if e4gIcon == 0:781            raise IOError('Cannot open bitmap "%s".' % (bitmap,))782        matchCount = 0783        leftTopRightBottomZero = (_intCoords((area[0], area[1]), ssSize) +784                                  _intCoords((area[2], area[3]), ssSize) +785                                  (0,))786        struct_area_bbox = _Bbox(*leftTopRightBottomZero)787        struct_bbox = _Bbox(0, 0, 0, 0, 0)788        contOpts = 0 # search for the first hit789        try:790            xscale, yscale = scale791        except TypeError:792            xscale = yscale = float(scale)793        while True:794            if matchCount == limit: break795            result = eye4graphics.findNextIcon(796                ctypes.byref(struct_bbox),797                ctypes.c_void_p(self._openedImages[ssFilename]),798                ctypes.c_void_p(e4gIcon),799                0, # no fuzzy matching800                ctypes.c_double(colorMatch),801                ctypes.c_double(opacityLimit),802                ctypes.byref(struct_area_bbox),803                ctypes.c_int(contOpts),804                ctypes.c_float(xscale),805                ctypes.c_float(yscale),806                ctypes.c_int(bitmapPixelSize),807                ctypes.c_int(screenshotPixelSize))808            contOpts = 1 # search for the next hit809            if result < 0: break810            bbox = (int(struct_bbox.left), int(struct_bbox.top),811                    int(struct_bbox.right), int(struct_bbox.bottom))812            addToFoundItems = True813            if allowOverlap == False:814                for guiItem in self._findBitmapCache[ssFilename][cacheKey]:815                    itemLeft, itemTop, itemRight, itemBottom = guiItem.bbox()816                    if ((itemLeft <= bbox[0] <= itemRight or itemLeft <= bbox[2] <= itemRight) and817                        (itemTop <= bbox[1] <= itemBottom or itemTop <= bbox[3] <= itemBottom)):818                        if ((itemLeft < bbox[0] < itemRight or itemLeft < bbox[2] < itemRight) or819                            (itemTop < bbox[1] < itemBottom or itemTop < bbox[3] < itemBottom)):820                            addToFoundItems = False821                            break822            if addToFoundItems:823                self._findBitmapCache[ssFilename][cacheKey].append(824                    GUIItem("bitmap", bbox, ssFilename, bitmap=bitmap))825                matchCount += 1826        eye4graphics.closeImage(e4gIcon)827        return self._findBitmapCache[ssFilename][cacheKey]828def _defaultOirEngine():829    if _g_defaultOirEngine:830        return _g_defaultOirEngine831    else:832        _Eye4GraphicsOirEngine().register(defaultOir=True)833        return _g_defaultOirEngine834class _OirRc(object):835    """Optical image recognition settings for a directory.836    Currently loaded from file .fmbtoirc in the directory.837    There is once _OirRc instance per directory.838    """839    _filename = ".fmbtoirrc"840    _cache = {}841    @classmethod842    def load(cls, directory):843        if directory in cls._cache:844            pass845        elif os.access(os.path.join(directory, cls._filename), os.R_OK):846            cls._cache[directory] = cls(directory)847        else:848            cls._cache[directory] = None849        return cls._cache[directory]850    def __init__(self, directory):851        self._key2value = {}852        curdir = "."853        self._dir2oirArgsList = {curdir: [{}]}854        for line in file(os.path.join(directory, _OirRc._filename)):855            line = line.strip()856            if line == "" or line[0] in "#;":857                continue858            elif line == "alternative":859                self._dir2oirArgsList[curdir].append({}) # new set of args860                self._key2value = {}861            elif "=" in line:862                key, value_str = line.split("=", 1)863                value_str = value_str.strip()864                if key.strip().lower() == "includedir":865                    curdir = value_str866                    self._dir2oirArgsList[curdir] = [{}]867                else:868                    try: value = int(value_str)869                    except ValueError:870                        try: value = float(value_str)871                        except ValueError:872                            if value_str[0] in "([\"'": # tuple, list, string873                                value = eval(value_str)874                            else:875                                value = value_str876                    self._dir2oirArgsList[curdir][-1][key.strip()] = value877    def searchDirs(self):878        return self._dir2oirArgsList.keys()879    def oirArgsList(self, searchDir):880        return self._dir2oirArgsList[searchDir]881class _Paths(object):882    def __init__(self, bitmapPath, relativeRoot):883        self.bitmapPath = bitmapPath884        self.relativeRoot = relativeRoot885        self._oirAL = {} # OIR parameters for bitmaps886        self._abspath = {} # bitmap to abspath887    def abspath(self, bitmap, checkReadable=True):888        if bitmap in self._abspath:889            return self._abspath[bitmap]890        if bitmap.startswith("/"):891            path = [os.path.dirname(bitmap)]892            bitmap = os.path.basename(bitmap)893        else:894            path = []895            for singleDir in self.bitmapPath.split(":"):896                if not singleDir.startswith("/"):897                    path.append(os.path.join(self.relativeRoot, singleDir))898                else:899                    path.append(singleDir)900        for singleDir in path:901            retval = os.path.join(singleDir, bitmap)902            if not checkReadable or os.access(retval, os.R_OK):903                oirRc = _OirRc.load(singleDir)904                if oirRc:905                    self._oirAL[retval] = oirRc.oirArgsList(".")906                else:907                    self._oirAL[retval] = [{}]908                self._oirAL[bitmap] = self._oirAL[retval]909                break910            else:911                # bitmap is not in singleDir, but there might be .fmbtoirrc912                oirRc = _OirRc.load(singleDir)913                if oirRc:914                    for d in oirRc.searchDirs():915                        if d.startswith("/"):916                            candidate = os.path.join(d, bitmap)917                        else:918                            candidate = os.path.join(singleDir, d, bitmap)919                        if os.access(candidate, os.R_OK):920                            self._oirAL[candidate] = oirRc.oirArgsList(d)921                            self._oirAL[bitmap] = self._oirAL[candidate]922                            retval = candidate923                            break924        if checkReadable and not os.access(retval, os.R_OK):925            raise ValueError('Bitmap "%s" not readable in bitmapPath %s' % (bitmap, ':'.join(path)))926        self._abspath[bitmap] = retval927        return retval928    def oirArgsList(self, bitmap):929        """Returns list of alternative OIR parameters associated to the bitmap930        by appropriate .fmbtoirrc file931        """932        if bitmap in self._oirAL:933            return self._oirAL[bitmap]934        else:935            absBitmap = self.abspath(bitmap)936            if absBitmap in self._oirAL:937                return self._oirAL[absBitmap]938            else:939                return None940class GUITestInterface(object):941    def __init__(self, ocrEngine=None, oirEngine=None, rotateScreenshot=None):942        self._paths = _Paths("", "")943        self._conn = None944        self._lastScreenshot = None945        self._longPressHoldTime = 2.0946        self._longTapHoldTime = 2.0947        self._ocrEngine = None948        self._oirEngine = None949        self._rotateScreenshot = rotateScreenshot950        self._screenshotLimit = None951        self._screenshotRefCount = {} # filename -> Screenshot object ref count952        self._screenshotArchiveMethod = "resize"953        if ocrEngine == None:954            self.setOcrEngine(_defaultOcrEngine())955        else:956            if type(ocrEngine) == int:957                self.setOcrEngine(_g_ocrEngines[ocrEngine])958            else:959                self.setOcrEngine(ocrEngine)960        if oirEngine == None:961            self.setOirEngine(_defaultOirEngine())962        else:963            if type(oirEngine) == int:964                self.setOirEngine(_g_oirEngines[oirEngine])965            else:966                self.setOirEngine(oirEngine)967        self._screenshotDir = None968        self._screenshotDirDefault = "screenshots"969        self._screenshotSubdir = None970        self._screenshotSubdirDefault = ""971        self._screenSize = None972        self._visualLog = None973        self._visualLogFileObj = None974        self._visualLogFilenames = set()975    def bitmapPath(self):976        """977        Returns bitmapPath from which bitmaps are searched for.978        """979        return self._paths.bitmapPath980    def bitmapPathRoot(self):981        """982        Returns the path that prefixes all relative directories in983        bitmapPath.984        """985        return self._paths.relativeRoot986    def close(self):987        self._lastScreenshot = None988        if self._visualLog:989            if hasattr(self._visualLog._outFileObj, "name"):990                self._visualLogFilenames.remove(self._visualLog._outFileObj.name)991            self._visualLog.close()992            if self._visualLogFileObj:993                self._visualLogFileObj.close()994            self._visualLog = None995    def connection(self):996        """997        Returns GUITestConnection instance.998        """999        return self._conn1000    def drag(self, (x1, y1), (x2, y2), delayBetweenMoves=0.01, delayBeforeMoves=0, delayAfterMoves=0, movePoints=20):1001        """1002        Touch the screen on coordinates (x1, y1), drag along straight1003        line to coordinates (x2, y2), and raise fingertip.1004        coordinates (floats in range [0.0, 1.0] or integers):1005                floating point coordinates in range [0.0, 1.0] are1006                scaled to full screen width and height, others are1007                handled as absolute coordinate values.1008        delayBeforeMoves (float, optional):1009                seconds to wait after touching and before dragging.1010                If negative, starting touch event is not sent.1011        delayBetweenMoves (float, optional):1012                seconds to wait when moving between points when1013                dragging.1014        delayAfterMoves (float, optional):1015                seconds to wait after dragging, before raising1016                fingertip.1017                If negative, fingertip is not raised.1018        movePoints (integer, optional):1019                the number of intermediate move points between end1020                points of the line.1021        Returns True on success, False if sending input failed.1022        """1023        x1, y1 = self.intCoords((x1, y1))1024        x2, y2 = self.intCoords((x2, y2))1025        if delayBeforeMoves >= 0:1026            if not self._conn.sendTouchDown(x1, y1):1027                return False1028        if delayBeforeMoves > 0:1029            time.sleep(delayBeforeMoves)1030        else:1031            time.sleep(delayBetweenMoves)1032        for i in xrange(0, movePoints):1033            nx = x1 + int(round(((x2 - x1) / float(movePoints+1)) * (i+1)))1034            ny = y1 + int(round(((y2 - y1) / float(movePoints+1)) * (i+1)))1035            if not self._conn.sendTouchMove(nx, ny): return False1036            time.sleep(delayBetweenMoves)1037        if delayAfterMoves > 0:1038            self._conn.sendTouchMove(x2, y2)1039            time.sleep(delayAfterMoves)1040        if delayAfterMoves >= 0:1041            if self._conn.sendTouchUp(x2, y2):1042                return True1043            else:1044                return False1045        else:1046            return True1047    def enableVisualLog(self, filenameOrObj,1048                        screenshotWidth="240", thumbnailWidth="",1049                        timeFormat="%s.%f", delayedDrawing=False,1050                        copyBitmapsToScreenshotDir=False):1051        """1052        Start writing visual HTML log on this device object.1053        Parameters:1054          filenameOrObj (string or a file object)1055                  The file to which the log is written.1056          screenshotWidth (string, optional)1057                  Width of screenshot images in HTML.1058                  The default is "240".1059          thumbnailWidth (string, optional)1060                  Width of thumbnail images in HTML.1061                  The default is "", that is, original size.1062          timeFormat (string, optional)1063                  Timestamp format. The default is "%s.%f".1064                  Refer to strftime documentation.1065          delayedDrawing (boolean, optional)1066                  If True, screenshots with highlighted icons, words1067                  and gestures are not created during the1068                  test. Instead, only shell commands are stored for1069                  later execution. The value True can significantly1070                  save test execution time and disk space. The default1071                  is False.1072          copyBitmapsToScreenshotDir (boolean, optional)1073                  If True, every logged bitmap file will be copied to1074                  bitmaps directory in screenshotDir. The default is1075                  False.1076        """1077        if type(filenameOrObj) == str:1078            try:1079                outFileObj = file(filenameOrObj, "w")1080                self._visualLogFileObj = outFileObj1081            except Exception, e:1082                _fmbtLog('Failed to open file "%s" for logging.' % (filenameOrObj,))1083                raise1084        else:1085            outFileObj = filenameOrObj1086            # someone else opened the file => someone else will close it1087            self._visualLogFileObj = None1088        if hasattr(outFileObj, "name"):1089            if outFileObj.name in self._visualLogFilenames:1090                raise ValueError('Visual logging on file "%s" is already enabled' % (outFileObj.name,))1091            else:1092                self._visualLogFilenames.add(outFileObj.name)1093        self._visualLog = _VisualLog(self, outFileObj, screenshotWidth,1094                                     thumbnailWidth, timeFormat, delayedDrawing,1095                                     copyBitmapsToScreenshotDir)1096    def visualLog(self, *args):1097        """Writes parameters to the visual log, given that visual logging is1098        enabled.1099        """1100        pass1101    def intCoords(self, (x, y)):1102        """1103        Convert floating point coordinate values in range [0.0, 1.0] to1104        screen coordinates.1105        """1106        width, height = self.screenSize()1107        return _intCoords((x, y), (width, height))1108    def ocrEngine(self):1109        """1110        Returns the OCR engine that is used by default for new1111        screenshots.1112        """1113        return self._ocrEngine1114    def oirEngine(self):1115        """1116        Returns the OIR engine that is used by default for new1117        screenshots.1118        """1119        return self._oirEngine1120    def pressKey(self, keyName, long=False, hold=0.0):1121        """1122        Press a key.1123        Parameters:1124          keyName (string):1125                  the name of the key, like KEYCODE_HOME.1126          long (boolean, optional):1127                  if True, press the key for long time.1128          hold (float, optional):1129                  time in seconds to hold the key down.1130        """1131        if long and hold == 0.0:1132            hold = self._longPressHoldTime1133        if hold > 0.0:1134            try:1135                assert self._conn.sendKeyDown(keyName)1136                time.sleep(hold)1137                assert self._conn.sendKeyUp(keyName)1138            except AssertionError:1139                return False1140            return True1141        return self._conn.sendPress(keyName)1142    def _newScreenshotFilepath(self):1143        """1144        Returns path and filename for next screenshot file.1145        Makes sure the file can be written (necessary directory1146        structure exists).1147        """1148        t = datetime.datetime.now()1149        filename = _filenameTimestamp(t) + "-" + self._conn.target() + ".png"1150        filepath = os.path.join(self.screenshotDir(),1151                                t.strftime(self.screenshotSubdir()),1152                                filename)1153        necessaryDirs = os.path.dirname(filepath)1154        if necessaryDirs and not os.path.isdir(necessaryDirs):1155            try:1156                os.makedirs(necessaryDirs)1157            except Exception, e:1158                _fmbtLog('creating directory "%s" for screenshots failed: %s' %1159                         (necessaryDirs, e))1160                raise1161        return filepath1162    def _archiveScreenshot(self, filepath):1163        if self._screenshotArchiveMethod == "remove":1164            try:1165                os.remove(filepath)1166            except IOError:1167                pass1168        elif self._screenshotArchiveMethod.startswith("resize"):1169            if self._screenshotArchiveMethod == "resize":1170                convertArgs = ["-resize",1171                               "%sx" % (int(self.screenSize()[0]) / 4,)]1172            else:1173                widthHeight = self._screenshotArchiveMethod.split()[1]1174                convertArgs = ["-resize", widthHeight]1175            subprocess.call(["convert", filepath] + convertArgs + [filepath])1176    def _archiveScreenshots(self):1177        """1178        Archive screenshot files if screenshotLimit has been exceeded.1179        """1180        freeScreenshots = [filename1181                           for (filename, refCount) in self._screenshotRefCount.iteritems()1182                           if refCount == 0]1183        archiveCount = len(freeScreenshots) - self._screenshotLimit1184        if archiveCount > 0:1185            freeScreenshots.sort(reverse=True) # archive oldest1186            while archiveCount > 0:1187                toBeArchived = freeScreenshots.pop()1188                try:1189                    self._archiveScreenshot(toBeArchived)1190                except IOError:1191                    pass1192                del self._screenshotRefCount[toBeArchived]1193                archiveCount -= 11194    def refreshScreenshot(self, forcedScreenshot=None, rotate=None):1195        """1196        Takes new screenshot and updates the latest screenshot object.1197        Parameters:1198          forcedScreenshot (Screenshot or string, optional):1199                  use given screenshot object or image file, do not1200                  take new screenshot.1201          rotate (integer, optional):1202                  rotate screenshot by given number of degrees. This1203                  overrides constructor rotateScreenshot parameter1204                  value. The default is None (no override).1205        Returns Screenshot object, and makes the same object "the1206        latest screenshot" that is used by all *Bitmap and *OcrText1207        methods.1208        """1209        if forcedScreenshot != None:1210            if type(forcedScreenshot) == str:1211                self._lastScreenshot = Screenshot(1212                    screenshotFile=forcedScreenshot,1213                    paths = self._paths,1214                    ocrEngine=self._ocrEngine,1215                    oirEngine=self._oirEngine,1216                    screenshotRefCount=self._screenshotRefCount)1217            else:1218                self._lastScreenshot = forcedScreenshot1219        else:1220            if self.screenshotDir() == None:1221                self.setScreenshotDir(self._screenshotDirDefault)1222            if self.screenshotSubdir() == None:1223                self.setScreenshotSubdir(self._screenshotSubdirDefault)1224            screenshotFile = self._newScreenshotFilepath()1225            if self._conn.recvScreenshot(screenshotFile):1226                # New screenshot successfully received from device1227                if rotate == None:1228                    rotate = self._rotateScreenshot1229                if rotate != None and rotate != 0:1230                    subprocess.call(["convert", screenshotFile, "-rotate", str(rotate), screenshotFile])1231                self._lastScreenshot = Screenshot(1232                    screenshotFile=screenshotFile,1233                    paths = self._paths,1234                    ocrEngine=self._ocrEngine,1235                    oirEngine=self._oirEngine,1236                    screenshotRefCount=self._screenshotRefCount)1237            else:1238                self._lastScreenshot = None1239        # Make sure unreachable Screenshot instances are released from1240        # memory.1241        gc.collect()1242        for obj in gc.garbage:1243            if isinstance(obj, Screenshot):1244                if hasattr(obj, "_logCallReturnValue"):1245                    # Some methods have been wrapped by visual1246                    # log. Break reference cycles to let gc collect1247                    # them.1248                    del obj.findItemsByBitmap1249                    del obj.findItemsByOcr1250        del gc.garbage[:]1251        gc.collect()1252        # If screenshotLimit has been set, archive old screenshot1253        # stored on the disk.1254        if self._screenshotLimit != None and self._screenshotLimit >= 0:1255            self._archiveScreenshots()1256        return self._lastScreenshot1257    def screenshot(self):1258        """1259        Returns the latest Screenshot object.1260        Use refreshScreenshot() to get a new screenshot.1261        """1262        return self._lastScreenshot1263    def screenshotArchiveMethod(self):1264        """1265        Returns how screenshots exceeding screenshotLimit are archived.1266        """1267        return self._screenshotArchiveMethod1268    def screenshotDir(self):1269        """1270        Returns the directory under which new screenshots are saved.1271        """1272        return self._screenshotDir1273    def screenshotLimit(self):1274        """1275        Returns the limit after which unused screenshots are archived.1276        """1277        return self._screenshotLimit1278    def screenshotSubdir(self):1279        """1280        Returns the subdirectory in screenshotDir under which new1281        screenshots are stored.1282        """1283        return self._screenshotSubdir1284    def screenSize(self):1285        """1286        Returns screen size in pixels in tuple (width, height).1287        """1288        if self._screenSize == None:1289            if self._lastScreenshot == None:1290                self.refreshScreenshot()1291                self._screenSize = self._lastScreenshot.size()1292                self._lastScreenshot = None1293            else:1294                self._screenSize = self._lastScreenshot.size()1295        return self._screenSize1296    def setBitmapPath(self, bitmapPath, rootForRelativePaths=None):1297        """1298        Set new path for finding bitmaps.1299        Parameters:1300          bitmapPath (string)1301                  colon-separated list of directories from which1302                  bitmap methods look for bitmap files.1303          rootForRelativePaths (string, optional)1304                  path that will prefix all relative paths in1305                  bitmapPath.1306        Example:1307          gui.setBitmapPath("bitmaps:icons:/tmp", "/home/X")1308          gui.tapBitmap("start.png")1309          will look for /home/X/bitmaps/start.png,1310          /home/X/icons/start.png and /tmp/start.png, in this order.1311        """1312        self._paths.bitmapPath = bitmapPath1313        if rootForRelativePaths != None:1314            self._paths.relativeRoot = rootForRelativePaths1315    def setConnection(self, conn):1316        """1317        Set the connection object that performs actions on real target.1318        Parameters:1319          conn (GUITestConnection instance):1320                  The connection to be used.1321        """1322        self._conn = conn1323    def setOcrEngine(self, ocrEngine):1324        """1325        Set OCR (optical character recognition) engine that will be1326        used by default in new screenshots.1327        Returns previous default.1328        """1329        prevDefault = self._ocrEngine1330        self._ocrEngine = ocrEngine1331        return prevDefault1332    def setOirEngine(self, oirEngine):1333        """1334        Set OIR (optical image recognition) engine that will be used1335        by default in new screenshots.1336        Returns previous default.1337        """1338        prevDefault = self._oirEngine1339        self._oirEngine = oirEngine1340        return prevDefault1341    def setScreenshotArchiveMethod(self, screenshotArchiveMethod):1342        """1343        Set method for archiving screenshots when screenshotLimit is exceeded.1344        Parameters:1345          screenshotArchiveMethod (string)1346                  Supported methods are "resize [WxH]" and "remove"1347                  where W and H are integers that define maximum width and1348                  height for an archived screenshot.1349                  The default method is "resize".1350        """1351        if screenshotArchiveMethod == "remove":1352            pass1353        elif screenshotArchiveMethod == "resize":1354            pass1355        elif screenshotArchiveMethod.startswith("resize"):1356            try:1357                w, h = screenshotArchiveMethod.split(" ")[1].split("x")1358            except:1359                raise ValueError("Invalid resize syntax")1360            try:1361                w, h = int(w), int(h)1362            except:1363                raise ValueError(1364                    "Invalid resize width or height, integer expected")1365        else:1366            raise ValueError('Unknown archive method "%s"' %1367                             (screenshotArchiveMethod,))1368        self._screenshotArchiveMethod = screenshotArchiveMethod1369    def setScreenshotDir(self, screenshotDir):1370        self._screenshotDir = screenshotDir1371        if not os.path.isdir(self.screenshotDir()):1372            try:1373                os.makedirs(self.screenshotDir())1374            except Exception, e:1375                _fmbtLog('creating directory "%s" for screenshots failed: %s' % (self.screenshotDir(), e))1376                raise1377    def setScreenshotLimit(self, screenshotLimit):1378        """1379        Set maximum number for unarchived screenshots.1380        Parameters:1381          screenshotLimit (integer)1382                  Maximum number of unarchived screenshots that are1383                  free for archiving (that is, not referenced by test code).1384                  The default is None, that is, there is no limit and1385                  screenshots are never archived.1386        See also:1387          setScreenshotArchiveMethod()1388        """1389        self._screenshotLimit = screenshotLimit1390    def setScreenshotSubdir(self, screenshotSubdir):1391        """1392        Define a subdirectory under screenshotDir() for screenshot files.1393        Parameters:1394          screenshotSubdir (string)1395                  Name of a subdirectory. The name should contain1396                  conversion specifiers supported by strftime.1397        Example:1398          sut.setScreenshotSubdir("%m-%d-%H")1399                  A screenshot taken on June 20th at 4.30pm will1400                  be stored to screenshotDir/01-20-16. That is,1401                  screenshots taken on different hours will be1402                  stored to different subdirectories.1403        By default, all screenshots are stored directly to screenshotDir().1404        """1405        self._screenshotSubdir = screenshotSubdir1406    def swipe(self, (x, y), direction, distance=1.0, **dragArgs):1407        """1408        swipe starting from coordinates (x, y) to given direction.1409        Parameters:1410          coordinates (floats in range [0.0, 1.0] or integers):1411                  floating point coordinates in range [0.0, 1.0] are1412                  scaled to full screen width and height, others are1413                  handled as absolute coordinate values.1414          direction (string or integer):1415                  Angle (0..360 degrees), or "north", "south", "east"1416                  and "west" (corresponding to angles 90, 270, 0 and1417                  180).1418          distance (float, optional):1419                  Swipe distance. Values in range [0.0, 1.0] are1420                  scaled to the distance from the coordinates to the1421                  edge of the screen. The default is 1.0: swipe to the1422                  edge of the screen.1423          rest of the parameters: refer to drag documentation.1424        Returns True on success, False if sending input failed.1425        """1426        if type(direction) == str:1427            d = direction.lower()1428            if d in ["n", "north"]: direction = 901429            elif d in ["s", "south"]: direction = 2701430            elif d in ["e", "east"]: direction = 01431            elif d in ["w", "west"]: direction = 1801432            else: raise ValueError('Illegal direction "%s"' % (direction,))1433        direction = direction % 3601434        x, y = self.intCoords((x, y))1435        dirRad = math.radians(direction)1436        distToEdge = _edgeDistanceInDirection((x, y), self.screenSize(), direction)1437        if distance > 1.0: distance = float(distance) / distToEdge1438        x2 = int(x + math.cos(dirRad) * distToEdge * distance)1439        y2 = int(y - math.sin(dirRad) * distToEdge * distance)1440        return self.drag((x, y), (x2, y2), **dragArgs)1441    def swipeBitmap(self, bitmap, direction, distance=1.0, **dragAndOirArgs):1442        """1443        swipe starting from bitmap to given direction.1444        Parameters:1445          bitmap (string)1446                  bitmap from which swipe starts1447          direction, distance1448                  refer to swipe documentation.1449          startPos1450                  refer to swipeItem documentation.1451          optical image recognition arguments (optional)1452                  refer to help(obj.oirEngine()).1453          delayBeforeMoves, delayBetweenMoves, delayAfterMoves,1454          movePoints1455                  refer to drag documentation.1456        Returns True on success, False if sending input failed.1457        """1458        assert self._lastScreenshot != None, "Screenshot required."1459        dragArgs, rest = _takeDragArgs(dragAndOirArgs)1460        oirArgs, _ = _takeOirArgs(self._lastScreenshot, rest, thatsAll=True)1461        oirArgs["limit"] = 11462        items = self._lastScreenshot.findItemsByBitmap(bitmap, **oirArgs)1463        if len(items) == 0:1464            return False1465        return self.swipeItem(items[0], direction, distance, **dragArgs)1466    def swipeItem(self, viewItem, direction, distance=1.0, **dragArgs):1467        """1468        swipe starting from viewItem to given direction.1469        Parameters:1470          viewItem (ViewItem)1471                  item from which swipe starts1472          direction, distance1473                  refer to swipe documentation.1474          startPos (pair of floats (x,y)):1475                  position of starting swipe, relative to the item.1476                  (0.0, 0.0) is the top-left corner,1477                  (1.0, 0.0) is the top-right corner,1478                  (1.0, 1.0) is the lower-right corner.1479                  Values < 0.0 and > 1.0 start swiping from coordinates1480                  outside the item.1481          delayBeforeMoves, delayBetweenMoves, delayAfterMoves,1482          movePoints1483                  refer to drag documentation.1484        Returns True on success, False if sending input failed.1485        """1486        if "startPos" in dragArgs:1487            posX, posY = dragArgs["startPos"]1488            del dragArgs["startPos"]1489            x1, y1, x2, y2 = viewItem.bbox()1490            swipeCoords = (x1 + (x2-x1) * posX,1491                           y1 + (y2-y1) * posY)1492        else:1493            swipeCoords = viewItem.coords()1494        return self.swipe(swipeCoords, direction, distance, **dragArgs)1495    def swipeOcrText(self, text, direction, distance=1.0, **dragAndOcrArgs):1496        """1497        Find text from the latest screenshot using OCR, and swipe it.1498        Parameters:1499          text (string):1500                  the text to be swiped.1501          direction, distance1502                  refer to swipe documentation.1503          startPos1504                  refer to swipeItem documentation.1505          delayBeforeMoves, delayBetweenMoves, delayAfterMoves,1506          movePoints1507                  refer to drag documentation.1508          OCR engine specific arguments1509                  refer to help(obj.ocrEngine())1510        Returns True on success, False otherwise.1511        """1512        assert self._lastScreenshot != None, "Screenshot required."1513        dragArgs, rest = _takeDragArgs(dragAndOcrArgs)1514        ocrArgs, _ = _takeOcrArgs(self._lastScreenshot, rest, thatsAll=True)1515        items = self._lastScreenshot.findItemsByOcr(text, **ocrArgs)1516        if len(items) == 0:1517            return False1518        return self.swipeItem(items[0], direction, distance, **dragArgs)1519    def tap(self, (x, y), long=False, hold=0.0, count=1, delayBetweenTaps=0.175, button=None):1520        """1521        Tap screen on coordinates (x, y).1522        Parameters:1523          coordinates (floats in range [0.0, 1.0] or integers):1524                  floating point coordinates in range [0.0, 1.0] are1525                  scaled to full screen width and height, others are1526                  handled as absolute coordinate values.1527          count (integer, optional):1528                  number of taps to the coordinates. The default is 1.1529          delayBetweenTaps (float, optional):1530                  time (seconds) between taps when count > 1.1531                  The default is 0.175 (175 ms).1532          long (boolean, optional):1533                  if True, touch the screen for a long time.1534          hold (float, optional):1535                  time in seconds to touch the screen.1536          button (integer, optional):1537                  send tap using given mouse button. The default is1538                  None: button parameter is not passed to the1539                  underlying connection layer (sendTouchDown etc.),1540                  the default in the underlying layer will be used.1541                  Note that all connection layers may not support1542                  this parameter.1543        Returns True if successful, otherwise False.1544        """1545        x, y = self.intCoords((x, y))1546        count = int(count)1547        if long and hold == 0.0:1548            hold = self._longTapHoldTime1549        extraParams = {}1550        if button != None:1551            extraParams['button'] = button1552        if count == 0:1553            self._conn.sendTouchMove(x, y)1554        while count > 0:1555            if hold > 0.0:1556                try:1557                    assert self._conn.sendTouchDown(x, y, **extraParams)1558                    time.sleep(hold)1559                    assert self._conn.sendTouchUp(x, y, **extraParams)1560                except AssertionError:1561                    return False1562            else:1563                if not self._conn.sendTap(x, y, **extraParams):1564                    return False1565            count = int(count) - 11566        return True1567    def tapBitmap(self, bitmap, **tapAndOirArgs):1568        """1569        Find a bitmap from the latest screenshot, and tap it.1570        Parameters:1571          bitmap (string):1572                  filename of the bitmap to be tapped.1573          optical image recognition arguments (optional)1574                  refer to help(obj.oirEngine()).1575          tapPos (pair of floats (x,y)):1576                  refer to tapItem documentation.1577          long, hold, count, delayBetweenTaps, button (optional):1578                  refer to tap documentation.1579        Returns True if successful, otherwise False.1580        """1581        assert self._lastScreenshot != None, "Screenshot required."1582        tapArgs, rest = _takeTapArgs(tapAndOirArgs)1583        oirArgs, _ = _takeOirArgs(self._lastScreenshot, rest, thatsAll=True)1584        oirArgs["limit"] = 11585        items = self._lastScreenshot.findItemsByBitmap(bitmap, **oirArgs)1586        if len(items) == 0:1587            return False1588        return self.tapItem(items[0], **tapArgs)1589    def tapItem(self, viewItem, **tapArgs):1590        """1591        Tap the center point of viewItem.1592        Parameters:1593          viewItem (GUIItem object):1594                  item to be tapped, possibly returned by1595                  findItemsBy... methods in Screenshot or View.1596          tapPos (pair of floats (x,y)):1597                  position to tap, relative to the item.1598                  (0.0, 0.0) is the top-left corner,1599                  (1.0, 0.0) is the top-right corner,1600                  (1.0, 1.0) is the lower-right corner.1601                  Values < 0 and > 1 tap coordinates outside the item.1602          long, hold, count, delayBetweenTaps, button (optional):1603                  refer to tap documentation.1604        """1605        if "tapPos" in tapArgs:1606            posX, posY = tapArgs["tapPos"]1607            del tapArgs["tapPos"]1608            x1, y1, x2, y2 = viewItem.bbox()1609            tapCoords = (x1 + (x2-x1) * posX,1610                         y1 + (y2-y1) * posY)1611        else:1612            tapCoords = viewItem.coords()1613        return self.tap(tapCoords, **tapArgs)1614    def tapOcrText(self, text, appearance=0, **tapAndOcrArgs):1615        """1616        Find text from the latest screenshot using OCR, and tap it.1617        Parameters:1618          text (string):1619                  the text to be tapped.1620          long, hold, count, delayBetweenTaps, button (optional):1621                  refer to tap documentation.1622          OCR engine specific arguments1623                  refer to help(obj.ocrEngine())1624          Returns True if successful, otherwise False.1625        """1626        assert self._lastScreenshot != None, "Screenshot required."1627        tapArgs, rest = _takeTapArgs(tapAndOcrArgs)1628        ocrArgs, _ = _takeOcrArgs(self._lastScreenshot, rest, thatsAll=True)1629        items = self._lastScreenshot.findItemsByOcr(text, **ocrArgs)1630        if len(items) <= appearance:1631            return False1632        return self.tapItem(items[appearance], **tapArgs)1633    def type(self, text):1634        """1635        Type text.1636        """1637        return self._conn.sendType(text)1638    def verifyOcrText(self, text, **ocrArgs):1639        """1640        Verify using OCR that the last screenshot contains the text.1641        Parameters:1642          text (string):1643                  text to be verified.1644          OCR engine specific arguments1645                  refer to help(obj.ocrEngine())1646          Returns True if successful, otherwise False.1647        """1648        assert self._lastScreenshot != None, "Screenshot required."1649        ocrArgs, _ = _takeOcrArgs(self._lastScreenshot, ocrArgs, thatsAll=True)1650        return self._lastScreenshot.findItemsByOcr(text, **ocrArgs) != []1651    def verifyBitmap(self, bitmap, **oirArgs):1652        """1653        Verify that bitmap is present in the last screenshot.1654        Parameters:1655          bitmap (string):1656                  filename of the bitmap file to be searched for.1657          optical image recognition arguments (optional)1658                  refer to help(obj.oirEngine()).1659        """1660        assert self._lastScreenshot != None, "Screenshot required."1661        oirArgs, _ = _takeOirArgs(self._lastScreenshot, oirArgs, thatsAll=True)1662        oirArgs["limit"] = 11663        return self._lastScreenshot.findItemsByBitmap(bitmap, **oirArgs) != []1664    def wait(self, refreshFunc, waitFunc, waitFuncArgs=(), waitFuncKwargs={}, waitTime = 5.0, pollDelay = 1.0):1665        """1666        Wait until waitFunc returns True or waitTime has expired.1667        Parameters:1668          refreshFunc (function):1669                  this function is called before re-evaluating1670                  waitFunc. For instance, refreshScreenshot.1671          waitFunc, waitFuncArgs, waitFuncKwargs (function, tuple,1672          dictionary):1673                  wait for waitFunc(waitFuncArgs, waitFuncKwargs) to1674                  return True1675          waitTime (float, optional):1676                  max. time in seconds to wait for. The default is1677                  5.0.1678          pollDelay (float, optional):1679                  time in seconds to sleep between refreshs. The1680                  default is 1.0.1681        Returns True if waitFunc returns True - either immediately or1682        before waitTime has expired - otherwise False.1683        refreshFunc will not be called if waitFunc returns immediately1684        True.1685        """1686        if waitFunc(*waitFuncArgs, **waitFuncKwargs):1687            return True1688        startTime = time.time()1689        endTime = startTime + waitTime1690        now = startTime1691        while now < endTime:1692            time.sleep(min(pollDelay, (endTime - now)))1693            now = time.time()1694            refreshFunc()1695            if waitFunc(*waitFuncArgs, **waitFuncKwargs):1696                return True1697        return False1698    def waitAnyBitmap(self, listOfBitmaps, **waitAndOirArgs):1699        """1700        Wait until any of given bitmaps appears on screen.1701        Parameters:1702          listOfBitmaps (list of strings):1703                  list of bitmaps (filenames) to be waited for.1704          optical image recognition arguments (optional)1705                  refer to help(obj.oirEngine()).1706          waitTime, pollDelay (float, optional):1707                  refer to wait documentation.1708        Returns list of bitmaps appearing in the first screenshot that1709        contains at least one of the bitmaps. If none of the bitmaps1710        appear within the time limit, returns empty list.1711        If the bitmap is not found from most recently refreshed1712        screenshot, waitAnyBitmap updates the screenshot.1713        """1714        if listOfBitmaps == []: return []1715        if not self._lastScreenshot: self.refreshScreenshot()1716        waitArgs, rest = _takeWaitArgs(waitAndOirArgs)1717        oirArgs, _ = _takeOirArgs(self._lastScreenshot, rest, thatsAll=True)1718        foundBitmaps = []1719        def observe():1720            for bitmap in listOfBitmaps:1721                if self._lastScreenshot.findItemsByBitmap(bitmap, **oirArgs):1722                    foundBitmaps.append(bitmap)1723            return foundBitmaps != []1724        self.wait(self.refreshScreenshot, observe, **waitArgs)1725        return foundBitmaps1726    def waitAnyOcrText(self, listOfTexts, **waitAndOcrArgs):1727        """1728        Wait until OCR recognizes any of texts on the screen.1729        Parameters:1730          listOfTexts (list of string):1731                  texts to be waited for.1732          waitTime, pollDelay (float, optional):1733                  refer to wait documentation.1734          OCR engine specific arguments1735                  refer to help(obj.ocrEngine())1736        Returns list of texts that appeared in the first screenshot1737        that contains at least one of the texts. If none of the texts1738        appear within the time limit, returns empty list.1739        If any of texts is not found from most recently refreshed1740        screenshot, waitAnyOcrText updates the screenshot.1741        """1742        if listOfTexts == []: return []1743        if not self._lastScreenshot: self.refreshScreenshot()1744        waitArgs, rest = _takeWaitArgs(waitAndOcrArgs)1745        ocrArgs, _ = _takeOcrArgs(self._lastScreenshot, rest, thatsAll=True)1746        foundTexts = []1747        def observe():1748            for text in listOfTexts:1749                if self.verifyOcrText(text, **ocrArgs):1750                    foundTexts.append(text)1751            return foundTexts != []1752        self.wait(self.refreshScreenshot, observe, **waitArgs)1753        return foundTexts1754    def waitBitmap(self, bitmap, **waitAndOirArgs):1755        """1756        Wait until bitmap appears on screen.1757        Parameters:1758          bitmap (string):1759                  filename of the bitmap to be waited for.1760          optical image recognition arguments (optional)1761                  refer to help(obj.oirEngine()).1762          waitTime, pollDelay (float, optional):1763                  refer to wait documentation.1764        Returns True if bitmap appeared within given time limit,1765        otherwise False.1766        If the bitmap is not found from most recently refreshed1767        screenshot, waitBitmap updates the screenshot.1768        """1769        return self.waitAnyBitmap([bitmap], **waitAndOirArgs) != []1770    def waitOcrText(self, text, **waitAndOcrArgs):1771        """1772        Wait until OCR detects text on the screen.1773        Parameters:1774          text (string):1775                  text to be waited for.1776          waitTime, pollDelay (float, optional):1777                  refer to wait documentation.1778          OCR engine specific arguments1779                  refer to help(obj.ocrEngine())1780        Returns True if the text appeared within given time limit,1781        otherwise False.1782        If the text is not found from most recently refreshed1783        screenshot, waitOcrText updates the screenshot.1784        """1785        return self.waitAnyOcrText([text], **waitAndOcrArgs) != []1786class Screenshot(object):1787    """1788    Screenshot class takes and holds a screenshot (bitmap) of device1789    display, or a forced bitmap file if device connection is not given.1790    """1791    def __init__(self, screenshotFile=None, paths=None,1792                 ocrEngine=None, oirEngine=None, screenshotRefCount=None):1793        self._filename = screenshotFile1794        self._ocrEngine = ocrEngine1795        self._ocrEngineNotified = False1796        self._oirEngine = oirEngine1797        self._oirEngineNotified = False1798        self._screenshotRefCount = screenshotRefCount1799        if (type(self._screenshotRefCount) == dict and self._filename):1800            self._screenshotRefCount[self._filename] = (1 +1801                self._screenshotRefCount.get(self._filename, 0))1802        self._screenSize = None1803        self._paths = paths1804    def __del__(self):1805        if self._ocrEngine and self._ocrEngineNotified:1806            self._ocrEngine.removeScreenshot(self)1807        if self._oirEngine and self._oirEngineNotified:1808            if (self._ocrEngineNotified == False or1809                id(self._oirEngine) != id(self._ocrEngine)):1810                self._oirEngine.removeScreenshot(self)1811        if (type(self._screenshotRefCount) == dict and self._filename):1812            self._screenshotRefCount[self._filename] -= 11813    def isBlank(self):1814        """1815        Returns True if screenshot is blank, otherwise False.1816        """1817        return _e4gImageIsBlank(self._filename)1818    def setSize(self, screenSize):1819        self._screenSize = screenSize1820    def size(self, allowReadingFile=True):1821        """1822        Returns screenshot size in pixels, as pair (width, height).1823        """1824        if self._screenSize == None and allowReadingFile:1825            e4gImage = eye4graphics.openImage(self._filename)1826            self._screenSize = _e4gImageDimensions(e4gImage)1827            eye4graphics.closeImage(e4gImage)1828        return self._screenSize1829    def _notifyOcrEngine(self):1830        if self._ocrEngine and not self._ocrEngineNotified:1831            self._ocrEngine.addScreenshot(self)1832            self._ocrEngineNotified = True1833            if id(self._ocrEngine) == id(self._oirEngine):1834                self._oirEngineNotified = True1835    def _notifyOirEngine(self):1836        if self._oirEngine and not self._oirEngineNotified:1837            self._oirEngine.addScreenshot(self)1838            self._oirEngineNotified = True1839            if id(self._oirEngine) == id(self._ocrEngine):1840                self._ocrEngineNotified = True1841    def dumpOcr(self, **kwargs):1842        """1843        Return what OCR engine recognizes on this screenshot.1844        Not all OCR engines provide this functionality.1845        """1846        self._notifyOcrEngine()1847        return self._ocrEngine.dumpOcr(self, **kwargs)1848    def dumpOcrWords(self, **kwargs):1849        """1850        Deprecated, use dumpOcr().1851        """1852        return self.dumpOcr(**kwargs)1853    def filename(self):1854        return self._filename1855    def findItemsByBitmap(self, bitmap, **oirFindArgs):1856        if self._oirEngine != None:1857            self._notifyOirEngine()1858            oirArgsList = self._paths.oirArgsList(bitmap)1859            results = []1860            if oirArgsList:1861                for oirArgs in oirArgsList:1862                    oirArgs, _ = _takeOirArgs(self._oirEngine, oirArgs.copy())1863                    oirArgs.update(oirFindArgs)1864                    results.extend(self._oirEngine.findBitmap(1865                        self, self._paths.abspath(bitmap), **oirArgs))1866                    if results: break...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
