How to use dumpOcr method in fMBT

Best Python code snippet using fMBT_python

fmbtgti.py

Source:fmbtgti.py Github

copy

Full Screen

...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...

Full Screen

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run fMBT automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful