How to use iUseImageAsWindow method in fMBT

Best Python code snippet using fMBT_python

eyenfinger.py

Source:eyenfinger.py Github

copy

Full Screen

...449 _g_windowOffsets[_g_lastWindow][0], _g_windowOffsets[_g_lastWindow][1],450 SCREENSHOT_FILENAME))451 source = SCREENSHOT_FILENAME452 else:453 iUseImageAsWindow(source)454 _g_origImage = source455 orig_width, orig_height = _g_windowSizes[_g_lastWindow][0], _g_windowSizes[_g_lastWindow][1]456 x1, y1 = _coordsToInt(ocrArea[:2], (orig_width, orig_height))457 x2, y2 = _coordsToInt(ocrArea[2:], (orig_width, orig_height))458 if x2 <= x1 or y2 <= y1:459 raise EyenfingerError("Invalid area size: %s => %s" % (ocrArea, (x1, y1, x2, y2)))460 if orig_width <= 0 or orig_height <= 0:461 raise EyenfingerError("Invalid image size: %sx%s" % (orig_width, orig_height))462 if not ocr:463 if capture:464 drawWords(_g_origImage, capture, [], [])465 return []466 if preprocess == None:467 preprocess = _g_preprocess468 # convert to text469 _g_readImage = _g_origImage + "-pp.png"470 if ocrArea == (0, 0, 1.0, 1.0):471 croparea = []472 wordXOffset = 0473 wordYOffset = 0474 else:475 croparea = ["-crop", "%sx%s+%s+%s" % (x2-x1, y2-y1, x1, y1), "+repage"]476 wordXOffset = x1477 wordYOffset = y1478 # rescale possible resize preprocessing parameter479 resize_m = re.search('-resize ([0-9]+)x([0-9]*)', preprocess)480 if resize_m:481 origXResize = int(resize_m.group(1))482 newXResize = int(origXResize/float(orig_width) * (x2-x1))483 preprocess = (preprocess[:resize_m.start()] +484 ("-resize %sx" % (newXResize,)) +485 preprocess[resize_m.end():])486 _g_words = {}487 for psm in ocrPageSegModes:488 convert_cmd = ([fmbt_config.imagemagick_convert, _g_origImage] +489 croparea +490 shlex.split(preprocess) +491 [_g_readImage])492 tesseract_cmd = ["tesseract", _g_readImage, SCREENSHOT_FILENAME,493 "-l", lang, "-psm", str(psm), "hocr"]494 if isinstance(configfile, basestring):495 tesseract_cmd += [configfile]496 elif isinstance(configfile, list) or isinstance(configfile, tuple):497 tesseract_cmd += configfile498 exit_status, output = _runcmd(convert_cmd)499 if exit_status != 0:500 raise NoOCRResults("Convert returned exit status (%s): %s"501 % (exit_status, _g_last_runcmd_error))502 exit_status, output = _runcmd(tesseract_cmd)503 if exit_status != 0:504 raise NoOCRResults("Tesseract returned exit status (%s): %s"505 % (exit_status, _g_last_runcmd_error))506 hocr_filename = SCREENSHOT_FILENAME + ".html" # Tesseract 3.02507 if not os.access(hocr_filename, os.R_OK):508 hocr_filename = SCREENSHOT_FILENAME + ".hocr" # Tesseract 3.03509 if not os.access(hocr_filename, os.R_OK):510 raise NoOCRResults("HOCR output missing. Tesseract OCR 3.02 or greater required.\n")511 # store every word and its coordinates512 _g_words.update(_hocr2words(file(hocr_filename).read()))513 # convert word coordinates to the unscaled pixmap514 try:515 ocr_page_line = [line for line in file(hocr_filename).readlines() if "class='ocr_page'" in line][0]516 except IndexError:517 raise NoOCRResults("Could not read ocr_page class information from %s" % (hocr_filename,))518 scaled_width, scaled_height = re.findall('bbox 0 0 ([0-9]+)\s*([0-9]+)', ocr_page_line)[0]519 scaled_width, scaled_height = float(scaled_width) / (float(x2-x1)/orig_width), float(scaled_height) / (float(y2-y1)/orig_height)520 for word in sorted(_g_words.keys()):521 for appearance, (wordid, middle, bbox) in enumerate(_g_words[word]):522 _g_words[word][appearance] = \523 (wordid,524 (int(middle[0]/scaled_width * orig_width) + wordXOffset,525 int(middle[1]/scaled_height * orig_height) + wordYOffset),526 (int(bbox[0]/scaled_width * orig_width) + wordXOffset,527 int(bbox[1]/scaled_height * orig_height) + wordYOffset,528 int(bbox[2]/scaled_width * orig_width) + wordXOffset,529 int(bbox[3]/scaled_height * orig_height) + wordYOffset))530 _log('found "' + word + '": (' + str(bbox[0]) + ', ' + str(bbox[1]) + ')')531 if capture:532 drawWords(_g_origImage, capture, _g_words, _g_words)533 return sorted(_g_words.keys())534def iVerifyWord(word, match=0.33, appearance=1, capture=None):535 """536 DEPRECATED - use fmbtx11.Screen.verifyOcrText instead.537 Verify that word can be found from previously iRead() image.538 Parameters:539 word word that should be checked540 appearance if word appears many times, appearance to541 be clicked. Defaults to the first one.542 match minimum matching score543 capture save image with verified word highlighted544 to this file. Default: None (nothing is saved).545 Returns pair: ((score, matchingWord), (left, top, right, bottom)), where546 score score of found match (1.0 for perfect match)547 matchingWord corresponding word detected by OCR548 (left, top, right, bottom)549 bounding box of the word in read image550 Throws BadMatch error if word is not found.551 Throws NoOCRResults error if there are OCR results available552 on the current screen.553 """554 if _g_words == None:555 raise NoOCRResults('iRead has not been called with ocr=True')556 score, matching_word = findWord(word)557 if capture:558 drawWords(_g_origImage, capture, [word], _g_words)559 if score < match:560 raise BadMatch('No matching word for "%s". The best candidate "%s" with score %.2f, required %.2f' %561 (word, matching_word, score, match))562 return ((score, matching_word), _g_words[matching_word][appearance-1][2])563def iVerifyText(text, match=0.33, capture=None):564 """565 DEPRECATED - use fmbtx11.Screen.verifyOcrText instead.566 Verify that text can be found from previously iRead() image.567 Parameters:568 text multiple words that should be checked569 match minimum matching score570 capture save image with verified text highlighted571 to this file. Default: None (nothing is saved).572 Returns pair:573 ((score, matchingText), (left, top, right, bottom)), where574 score score of found match (1.0 for perfect match)575 matchingText corresponding text detected by OCR576 (left, top, right, bottom)577 bounding box of the text in read image578 Throws BadMatch error if text is not found.579 Throws NoOCRResults error if there are OCR results available580 on the current screen.581 """582 if _g_words == None:583 raise NoOCRResults('iRead has not been called with ocr=True')584 score_text_bbox_list = findText(text, match)585 if len(score_text_bbox_list) == 0:586 raise BadMatch('No match >= %s for text "%s"' % (score, text))587 score, text, bbox = score_text_box_list[0]588 if capture:589 drawBbox(_g_origImage, capture, bbox, "%.2f %s" % (score, text))590 return ((score, matching_text), bbox)591def iVerifyIcon(iconFilename, match=None, colorMatch=None, opacityLimit=None, capture=None, area=(0.0, 0.0, 1.0, 1.0), _origin="iVerifyIcon"):592 """593 DEPRECATED - use fmbtx11.Screen.verifyBitmap instead.594 Verify that icon can be found from previously iRead() image.595 Parameters:596 iconFilename name of the icon file to be searched for597 match minimum matching score between 0 and 1.0,598 1.0 is perfect match (default)599 colorMatch 1.0 (default) requires exact color match. Value600 below 1.0 defines maximum allowed color601 difference. See iSetDefaultIconColorMatch.602 opacityLimit 0.0 (default) requires exact color values603 independently of opacity. If lower than 1.0,604 pixel less opaque than given value are skipped605 in pixel perfect comparisons.606 capture save image with verified icon highlighted607 to this file. Default: None (nothing is saved).608 area rectangle (left, top, right, bottom). Search609 icon inside this rectangle only. Values can be610 absolute coordinates, or floats in range [0.0,611 1.0] that will be scaled to image dimensions.612 The default is (0.0, 0.0, 1.0, 1.0), that is613 full rectangle.614 Returns pair: (score, (left, top, right, bottom)), where615 score score of found match (1.0 for perfect match)616 (left, top, right, bottom)617 bounding box of found icon618 Throws BadMatch error if icon is not found.619 """620 if not eye4graphics:621 _log('ERROR: %s("%s") called, but eye4graphics not loaded.' % (_origin, iconFilename))622 raise EyenfingerError("eye4graphics not available")623 if not _g_origImage:624 _log('ERROR %s("%s") called, but source not defined (iRead not called).' % (_origin, iconFilename))625 raise BadSourceImage("Source image not defined, cannot search for an icon.")626 if not (os.path.isfile(iconFilename) and os.access(iconFilename, os.R_OK)):627 _log('ERROR %s("%s") called, but the icon file is not readable.' % (_origin, iconFilename))628 raise BadIconImage('Icon "%s" is not readable.' % (iconFilename,))629 if match == None:630 match = _g_defaultIconMatch631 if match > 1.0:632 _log('ERROR %s("%s"): invalid match value, must be below 1.0. ' % (_origin, iconFilename,))633 raise ValueError("invalid match value: %s, should be 0 <= match <= 1.0" % (match,))634 if colorMatch == None:635 colorMatch = _g_defaultIconColorMatch636 if not 0.0 <= colorMatch <= 1.0:637 _log('ERROR %s("%s"): invalid colorMatch value, must be between 0 and 1. ' % (_origin, iconFilename,))638 raise ValueError("invalid colorMatch value: %s, should be 0 <= colorMatch <= 1.0" % (colorMatch,))639 if opacityLimit == None:640 opacityLimit = _g_defaultIconOpacityLimit641 if not 0.0 <= opacityLimit <= 1.0:642 _log('ERROR %s("%s"): invalid opacityLimit value, must be between 0 and 1. ' % (_origin, iconFilename,))643 raise ValueError("invalid opacityLimit value: %s, should be 0 <= opacityLimit <= 1.0" % (opacityLimit,))644 if area[0] > area[2] or area[1] >= area[3]:645 raise ValueError("invalid area: %s, should be rectangle (left, top, right, bottom)" % (area,))646 leftTopRightBottomZero = (_coordsToInt((area[0], area[1]), windowSize()) +647 _coordsToInt((area[2], area[3]), windowSize()) +648 (0,))649 struct_area_bbox = Bbox(*leftTopRightBottomZero)650 struct_bbox = Bbox(0,0,0,0,0)651 threshold = int((1.0-match)*20)652 err = eye4graphics.findSingleIcon(ctypes.byref(struct_bbox),653 _g_origImage, iconFilename, threshold,654 ctypes.c_double(colorMatch),655 ctypes.c_double(opacityLimit),656 ctypes.byref(struct_area_bbox))657 bbox = (int(struct_bbox.left), int(struct_bbox.top),658 int(struct_bbox.right), int(struct_bbox.bottom))659 if err == -1 or err == -2:660 msg = '%s: "%s" not found, match=%.2f, threshold=%s, closest threshold %s.' % (661 _origin, iconFilename, match, threshold, int(struct_bbox.error))662 if capture:663 drawIcon(_g_origImage, capture, iconFilename, bbox, 'red')664 _log(msg)665 raise BadMatch(msg)666 elif err != 0:667 _log("%s: findSingleIcon returned %s" % (_origin, err,))668 raise BadMatch("%s not found, findSingleIcon returned %s." % (iconFilename, err))669 if threshold > 0:670 score = (threshold - int(struct_bbox.error)) / float(threshold)671 else:672 score = 1.0673 if capture:674 drawIcon(_g_origImage, capture, iconFilename, bbox, area=leftTopRightBottomZero[:4])675 return (score, bbox)676def iClickIcon(iconFilename, clickPos=(0.5,0.5), match=None,677 colorMatch=None, opacityLimit=None,678 mouseButton=1, mouseEvent=MOUSEEVENT_CLICK, dryRun=None, capture=None):679 """680 DEPRECATED - use fmbtx11.Screen.tapBitmap instead.681 Click coordinates relative to the given icon in previously iRead() image.682 Parameters:683 iconFilename read icon from this file684 clickPos position to be clicked,685 relative to word top-left corner of the bounding686 box around the word. X and Y units are relative687 to width and height of the box. (0,0) is the688 top-left corner, (1,1) is bottom-right corner,689 (0.5, 0.5) is the middle point (default).690 Values below 0 or greater than 1 click outside691 the bounding box.692 match 1.0 (default) requires exact match. Value below 1.0693 defines minimum required score for fuzzy matching694 (EXPERIMENTAL). See iSetDefaultIconMatch.695 colorMatch 1.0 (default) requires exact color match. Value696 below 1.0 defines maximum allowed color697 difference. See iSetDefaultIconColorMatch.698 opacityLimit 0.0 (default) requires exact color values699 independently of opacity. If lower than 1.0,700 pixel less opaque than given value are skipped701 in pixel perfect comparisons.702 mouseButton mouse button to be synthesized on the event, default is 1.703 mouseEvent event to be synthesized, the default is MOUSEEVENT_CLICK,704 others: MOUSEEVENT_MOVE, MOUSEEVENT_DOWN, MOUSEEVENT_UP.705 dryRun if True, does not synthesize events. Still returns706 coordinates of the clicked position and illustrates707 the clicked position on the capture image if708 given.709 capture name of file where image of highlighted icon and710 clicked point are saved.711 Returns pair (score, (clickedX, clickedY)), where712 score score of found match (1.0 for perfect match)713 (clickedX, clickedY)714 X and Y coordinates of clicked position on the715 screen.716 Throws BadMatch error if could not find a matching word.717 """718 _DEPRECATED()719 score, bbox = iVerifyIcon(iconFilename, match=match,720 colorMatch=colorMatch, opacityLimit=opacityLimit,721 capture=capture, _origin="iClickIcon")722 clickedXY = iClickBox(bbox, clickPos, mouseButton, mouseEvent, dryRun,723 capture, _captureText = iconFilename)724 return (score, clickedXY)725def iClickWord(word, appearance=1, clickPos=(0.5,0.5), match=0.33,726 mouseButton=1, mouseEvent=1, dryRun=None, capture=None):727 """728 DEPRECATED - use fmbtx11.Screen.tapOcrText instead.729 Click coordinates relative to the given word in previously iRead() image.730 Parameters:731 word word that should be clicked732 appearance if word appears many times, appearance to733 be clicked. Defaults to the first one.734 clickPos position to be clicked,735 relative to word top-left corner of the bounding736 box around the word. X and Y units are relative737 to width and height of the box. (0,0) is the738 top-left corner, (1,1) is bottom-right corner,739 (0.5, 0.5) is the middle point (default).740 Values below 0 or greater than 1 click outside741 the bounding box.742 capture name of file where image of highlighted word and743 clicked point are saved.744 Returns pair: ((score, matchingWord), (clickedX, clickedY)), where745 score score of found match (1.0 for perfect match)746 matchingWord corresponding word detected by OCR747 (clickedX, clickedY)748 X and Y coordinates of clicked position on the749 screen.750 Throws BadMatch error if could not find a matching word.751 Throws NoOCRResults error if there are OCR results available752 on the current screen.753 """754 _DEPRECATED()755 (score, matching_word), bbox = iVerifyWord(word, appearance=appearance, match=match, capture=False)756 clickedX, clickedY = iClickBox(bbox, clickPos, mouseButton, mouseEvent, dryRun, capture=False)757 windowId = _g_lastWindow758 _log('iClickWord("%s"): word "%s", match %.2f, bbox %s, window offset %s, click %s' %759 (word, matching_word, score,760 bbox, _g_windowOffsets[windowId],761 (clickedX, clickedY)))762 if capture:763 drawWords(_g_origImage, capture, [word], _g_words)764 drawClickedPoint(capture, capture, (clickedX, clickedY))765 return ((score, matching_word), (clickedX, clickedY))766def iClickBox((left, top, right, bottom), clickPos=(0.5, 0.5),767 mouseButton=1, mouseEvent=1, dryRun=None,768 capture=None, _captureText=None):769 """770 DEPRECATED - use fmbtx11.Screen.tapItem instead.771 Click coordinates relative to the given bounding box, default is772 in the middle of the box.773 Parameters:774 (left, top, right, bottom)775 coordinates of the box inside the window.776 (0, 0) is the top-left corner of the window.777 clickPos (offsetX, offsetY) position to be clicked,778 relative to the given box. (0, 0) is the779 top-left, and (1.0, 1.0) is the lower-right780 corner of the box. The default is (0.5, 0.5),781 that is, the middle point of the box. Values782 smaller than 0 and bigger than 1 are allowed,783 too.784 mouseButton mouse button to be synthesized on the event, default is 1.785 mouseEvent event to be synthesized, the default is MOUSEEVENT_CLICK,786 others: MOUSEEVENT_MOVE, MOUSEEVENT_DOWN, MOUSEEVENT_UP.787 dryRun if True, does not synthesize events. Still returns788 coordinates of the clicked position and illustrates789 the clicked position on the capture image if790 given.791 capture name of file where the last screenshot with792 clicked point highlighted is saved. The default793 is None (nothing is saved).794 Returns pair (clickedX, clickedY)795 X and Y coordinates of clicked position on the796 screen.797 """798 clickWinX = int(left + clickPos[0]*(right-left))799 clickWinY = int(top + clickPos[1]*(bottom-top))800 (clickedX, clickedY) = iClickWindow((clickWinX, clickWinY),801 mouseButton, mouseEvent,802 dryRun, capture=False)803 if capture:804 if _captureText == None:805 _captureText = "Box: %s, %s, %s, %s" % (left, top, right, bottom)806 drawIcon(_g_origImage, capture, _captureText, (left, top, right, bottom))807 drawClickedPoint(capture, capture, (clickedX, clickedY))808 return (clickedX, clickedY)809def iClickWindow((clickX, clickY), mouseButton=1, mouseEvent=1, dryRun=None, capture=None):810 """811 DEPRECATED - use fmbtx11.Screen.tap instead.812 Click given coordinates in the window.813 Parameters:814 (clickX, clickY)815 coordinates to be clicked inside the window.816 (0, 0) is the top-left corner of the window.817 Integer values are window coordinates. Floating818 point values from 0.0 to 1.0 are scaled to window819 coordinates: (0.5, 0.5) is the middle of the820 window, and (1.0, 1.0) the bottom-right corner of821 the window.822 mouseButton mouse button to be synthesized on the event, default is 1.823 mouseEvent event to be synthesized, the default is MOUSEEVENT_CLICK,824 others: MOUSEEVENT_MOVE, MOUSEEVENT_DOWN, MOUSEEVENT_UP.825 dryRun if True, does not synthesize events. Still826 illustrates the clicked position on the capture827 image if given.828 capture name of file where the last screenshot with829 clicked point highlighted is saved. The default830 is None (nothing is saved).831 Returns pair (clickedX, clickedY)832 X and Y coordinates of clicked position on the833 screen.834 """835 # Get the size of the window836 wndSize = windowSize()837 (clickX, clickY) = _coordsToInt((clickX, clickY), wndSize)838 # Get the position of the window839 wndPos = windowXY()840 # If coordinates are given as percentages, convert to window coordinates841 clickScrX = clickX + wndPos[0]842 clickScrY = clickY + wndPos[1]843 iClickScreen((clickScrX, clickScrY), mouseButton, mouseEvent, dryRun, capture)844 return (clickScrX, clickScrY)845def iClickScreen((clickX, clickY), mouseButton=1, mouseEvent=1, dryRun=None, capture=None):846 """847 DEPRECATED - use fmbtx11.Screen.tap instead.848 Click given absolute coordinates on the screen.849 Parameters:850 (clickX, clickY)851 coordinates to be clicked on the screen. (0, 0)852 is the top-left corner of the screen. Integer853 values are screen coordinates. Floating point854 values from 0.0 to 1.0 are scaled to screen855 coordinates: (0.5, 0.5) is the middle of the856 screen, and (1.0, 1.0) the bottom-right corner of857 the screen.858 mouseButton mouse button to be synthesized on the event, default is 1.859 mouseEvent event to be synthesized, the default is MOUSEEVENT_CLICK,860 others: MOUSEEVENT_MOVE, MOUSEEVENT_DOWN, MOUSEEVENT_UP.861 dryRun if True, does not synthesize events. Still862 illustrates the clicked position on the capture863 image if given.864 capture name of file where the last screenshot with865 clicked point highlighted is saved. The default866 is None (nothing is saved).867 """868 _DEPRECATED()869 if mouseEvent == MOUSEEVENT_CLICK:870 params = "'mouseclick %s'" % (mouseButton,)871 elif mouseEvent == MOUSEEVENT_DOWN:872 params = "'mousedown %s'" % (mouseButton,)873 elif mouseEvent == MOUSEEVENT_UP:874 params = "'mouseup %s'" % (mouseButton,)875 else:876 params = ""877 clickX, clickY = _coordsToInt((clickX, clickY))878 if capture:879 drawClickedPoint(_g_origImage, capture, (clickX, clickY))880 if dryRun == None:881 dryRun = _g_defaultClickDryRun882 if not dryRun:883 # use xte from the xautomation package884 _runcmd("xte 'mousemove %s %s' %s" % (clickX, clickY, params))885def iGestureScreen(listOfCoordinates, duration=0.5, holdBeforeGesture=0.0, holdAfterGesture=0.0, intermediatePoints=0, capture=None, dryRun=None):886 """887 DEPRECATED - use fmbtx11.Screen.drag instead.888 Synthesizes a gesture on the screen.889 Parameters:890 listOfCoordinates891 The coordinates through which the cursor moves.892 Integer values are screen coordinates. Floating893 point values from 0.0 to 1.0 are scaled to screen894 coordinates: (0.5, 0.5) is the middle of the895 screen, and (1.0, 1.0) the bottom-right corner of896 the screen.897 duration gesture time in seconds, excluding898 holdBeforeGesture and holdAfterGesture times.899 holdBeforeGesture900 time in seconds to keep mouse down before the901 gesture.902 holdAfterGesture903 time in seconds to keep mouse down after the904 gesture.905 intermediatePoints906 the number of intermediate points to be added907 between each of the coordinates. Intermediate908 points are added to straight lines between start909 and end points.910 capture name of file where the last screenshot with911 the points through which the cursors passes is912 saved. The default is None (nothing is saved).913 dryRun if True, does not synthesize events. Still914 illustrates the coordinates through which the cursor915 goes.916 """917 _DEPRECATED()918 # The params list to be fed to xte919 params = []920 # The list of coordinates through which the cursor has to go921 goThroughCoordinates = []922 for pos in xrange(len(listOfCoordinates)):923 x, y = _coordsToInt(listOfCoordinates[pos])924 goThroughCoordinates.append((x,y))925 if pos == len(listOfCoordinates) - 1:926 break # last coordinate added927 nextX, nextY = _coordsToInt(listOfCoordinates[pos+1])928 (x,y), (nextX, nextY) = (x, y), (nextX, nextY)929 for ip in range(intermediatePoints):930 goThroughCoordinates.append(931 (int(round(x + (nextX-x)*(ip+1)/float(intermediatePoints+1))),932 int(round(y + (nextY-y)*(ip+1)/float(intermediatePoints+1)))))933 # Calculate the time (in micro seconds) to sleep between moves.934 if len(goThroughCoordinates) > 1:935 moveDelay = 1000000 * float(duration) / (len(goThroughCoordinates)-1)936 else:937 moveDelay = 0938 if not dryRun:939 # Build the params list.940 params.append("'mousemove %d %d'" % goThroughCoordinates[0])941 params.append("'mousedown 1 '")942 if holdBeforeGesture > 0:943 params.append("'usleep %d'" % (holdBeforeGesture * 1000000,))944 for i in xrange(1, len(goThroughCoordinates)):945 params.append("'usleep %d'" % (moveDelay,))946 params.append("'mousemove %d %d'" % goThroughCoordinates[i])947 if holdAfterGesture > 0:948 params.append("'usleep %d'" % (holdAfterGesture * 1000000,))949 params.append("'mouseup 1'")950 # Perform the gesture951 _runcmd("xte %s" % (" ".join(params),))952 if capture:953 intCoordinates = [ _coordsToInt(point) for point in listOfCoordinates ]954 drawLines(_g_origImage, capture, intCoordinates, goThroughCoordinates)955 return goThroughCoordinates956def iGestureWindow(listOfCoordinates, duration=0.5, holdBeforeGesture=0.0, holdAfterGesture=0.0, intermediatePoints=0, capture=None, dryRun=None):957 """958 DEPRECATED - use fmbtx11.Screen.drag instead.959 Synthesizes a gesture on the window.960 Parameters:961 listOfCoordinates962 The coordinates through which the cursor moves.963 Integer values are window coordinates. Floating964 point values from 0.0 to 1.0 are scaled to window965 coordinates: (0.5, 0.5) is the middle of the966 window, and (1.0, 1.0) the bottom-right corner of967 the window.968 duration gesture time in seconds, excluding969 holdBeforeGesture and holdAfterGesture times.970 holdBeforeGesture971 time in seconds to keep mouse down before the972 gesture.973 holdAfterGesture974 time in seconds to keep mouse down after the975 gesture.976 intermediatePoints977 the number of intermediate points to be added978 between each of the coordinates. Intermediate979 points are added to straight lines between start980 and end points.981 capture name of file where the last screenshot with982 the points through which the cursors passes is983 saved. The default is None (nothing is saved).984 dryRun if True, does not synthesize events. Still985 illustrates the coordinates through which the cursor986 goes.987 """988 screenCoordinates = [ _windowToScreen(*_coordsToInt((x,y),windowSize())) for (x,y) in listOfCoordinates ]989 return iGestureScreen(screenCoordinates, duration, holdBeforeGesture, holdAfterGesture, intermediatePoints, capture, dryRun)990def iType(word, delay=0.0):991 """992 DEPRECATED - use fmbtx11.Screen.type instead.993 Send keypress events.994 Parameters:995 word is either996 - a string containing letters and numbers.997 Each letter/number is using press and release events.998 - a list that contains999 - keys: each key is sent using press and release events.1000 - (key, event)-pairs: the event (either "press" or "release")1001 is sent.1002 - (key1, key2, ..., keyn)-tuples. 2n events is sent:1003 key1 press, key2 press, ..., keyn press,1004 keyn release, ..., key2 release, key1 release.1005 Keys are defined in eyenfinger.Xkeys, for complete list1006 see keysymdef.h.1007 delay is given as seconds between sent events1008 Examples:1009 iType('hello')1010 iType([('Shift_L', 'press'), 'h', 'e', ('Shift_L', 'release'), 'l', 'l', 'o'])1011 iType([('Control_L', 'Alt_L', 'Delete')])1012 """1013 _DEPRECATED()1014 args = []1015 for char in word:1016 if type(char) == tuple:1017 if char[1].lower() == 'press':1018 args.append("'keydown %s'" % (char[0],))1019 elif char[1].lower() == 'release':1020 args.append("'keyup %s'" % (char[0],))1021 else:1022 rest = []1023 for key in char:1024 args.append("'keydown %s'" % (key,))1025 rest.insert(0, "'keyup %s'" % (key,))1026 args = args + rest1027 else:1028 # char is keyname or single letter/number1029 args.append("'key %s'" % (char,))1030 usdelay = " 'usleep %s' " % (int(delay*1000000),)1031 _runcmd("xte %s" % (usdelay.join(args),))1032def iInputKey(*args, **kwargs):1033 """1034 DEPRECATED - use fmbtx11.Screen.pressKey instead.1035 Send keypresses using Linux evdev interface1036 (/dev/input/eventXX).1037 iInputKey(keySpec[, keySpec...], hold=<float>, delay=<float>, device=<str>)1038 Parameters:1039 keySpec is one of the following:1040 - a string of one-character-long key names:1041 "aesc" will send four keypresses: A, E, S and C.1042 - a list of key names:1043 ["a", "esc"] will send two keypresses: A and ESC.1044 Key names are listed in eyenfinger.InputKeys.1045 - an integer:1046 116 will press the POWER key.1047 - "_" or "^":1048 only press or release event will be generated1049 for the next key, respectively.1050 If a key name inside keySpec is prefixed by "_"1051 or "^", only press or release event is generated1052 for that key.1053 hold time (in seconds) to hold the key before1054 releasing. The default is 0.1.1055 delay delay (in seconds) after key release. The default1056 is 0.1.1057 device name of the input device or input event file to1058 which all key presses are sent. The default can1059 be set with iSetDefaultInputKeyDevice(). For1060 instance, "/dev/input/event0" or a name of a1061 device in /proc/bus/input/devices.1062 """1063 _DEPRECATED()1064 hold = kwargs.get("hold", 0.1)1065 delay = kwargs.get("delay", 0.1)1066 device = kwargs.get("device", _g_defaultInputKeyDevice)1067 inputKeySeq = []1068 press, release = 1, 11069 for a in args:1070 if a == "_": press, release = 1, 01071 elif a == "^": press, release = 0, 11072 elif type(a) == str:1073 for char in a:1074 if char == "_": press, release = 1, 01075 elif char == "^": press, release = 0, 11076 else:1077 inputKeySeq.append((press, release, _inputKeyNameToCode(char.upper())))1078 press, release = 1, 11079 elif type(a) in (tuple, list):1080 for keySpec in a:1081 if type(keySpec) == int:1082 inputKeySeq.append((press, release, keySpec))1083 press, release = 1, 11084 else:1085 if keySpec.startswith("_"):1086 press, release = 1, 01087 keySpec = keySpec[1:]1088 elif keySpec.startswith("^"):1089 press, release = 0, 11090 keySpec = keySpec[1:]1091 if keySpec:1092 inputKeySeq.append((press, release, _inputKeyNameToCode(keySpec.upper())))1093 press, release = 1, 11094 elif type(a) == int:1095 inputKeySeq.append((press, release, a))1096 press, release = 1, 11097 else:1098 raise ValueError('Invalid keySpec "%s"' % (a,))1099 if inputKeySeq:1100 _writeInputKeySeq(_deviceFilename(device), inputKeySeq, hold=hold, delay=delay)1101def _deviceFilename(deviceName):1102 if not _deviceFilename.deviceCache:1103 _deviceFilename.deviceCache = dict(_listInputDevices())1104 if not deviceName in _deviceFilename.deviceCache:1105 return deviceName1106 else:1107 return _deviceFilename.deviceCache[deviceName]1108_deviceFilename.deviceCache = {}1109def _listInputDevices():1110 nameAndFile = []1111 for l in file("/proc/bus/input/devices"):1112 if l.startswith("N: Name="):1113 nameAndFile.append([l.split('"')[1]])1114 elif l.startswith("H: Handlers=") and "event" in l:1115 try:1116 eventFilename = re.findall("(event[0-9]+)", l)[0]1117 nameAndFile[-1].append("/dev/input/%s" % (eventFilename,))1118 except:1119 _log('WARNING: Could not recognise event[0-9] filename from row "%s".' % (l.strip(),))1120 return nameAndFile1121def _writeInputKeySeq(filename, keyCodeSeq, hold=0.1, delay=0.1):1122 if type(filename) != str or len(filename) == 0:1123 raise ValueError('Invalid input device "%s"' % (filename,))1124 fd = os.open(filename, os.O_WRONLY | os.O_NONBLOCK)1125 for press, release, keyCode in keyCodeSeq:1126 if press:1127 bytes = os.write(fd, struct.pack(_InputEventStructSpec,1128 int(time.time()), 0, _EV_KEY, keyCode, 1))1129 if bytes > 0:1130 bytes += os.write(fd, struct.pack(_InputEventStructSpec,1131 0, 0, 0, 0, 0))1132 time.sleep(hold)1133 if release:1134 bytes += os.write(fd, struct.pack(_InputEventStructSpec,1135 int(time.time()), 0, _EV_KEY, keyCode, 0))1136 if bytes > 0:1137 bytes += os.write(fd, struct.pack(_InputEventStructSpec,1138 0, 0, 0, 0, 0))1139 time.sleep(delay)1140 os.close(fd)1141def findWord(word, detected_words = None, appearance=1):1142 """1143 Returns pair (score, corresponding-detected-word)1144 """1145 if detected_words == None:1146 detected_words = _g_words1147 if _g_words == None:1148 raise NoOCRResults()1149 scored_words = []1150 for w in detected_words:1151 scored_words.append((_score(w, word), w))1152 scored_words.sort()1153 if len(scored_words) == 0:1154 raise BadMatch("No words found.")1155 return scored_words[-1]1156def findText(text, detected_words = None, match=-1):1157 def biggerBox(bbox_list):1158 left, top, right, bottom = bbox_list[0]1159 for l, t, r, b in bbox_list[1:]:1160 left = min(left, l)1161 top = min(top, t)1162 right = max(right, r)1163 bottom = max(bottom, b)1164 return (left, top, right, bottom)1165 words = text.split()1166 word_count = len(words)1167 detected_texts = [] # strings of <word_count> words1168 if detected_words == None:1169 detected_words = _g_words1170 if _g_words == None:1171 raise NoOCRResults()1172 # sort by numeric word id1173 words_by_id = []1174 for word in detected_words:1175 for wid, middle, bbox in detected_words[word]:1176 # change word id from "word_2_42" to (2, 42)1177 int_wid = [int(n) for n in wid[5:].split("_")]1178 words_by_id.append(1179 (int_wid, word, bbox))1180 words_by_id.sort()1181 scored_texts = []1182 if word_count > 0:1183 for i in xrange(len(words_by_id)-word_count+1):1184 detected_texts.append(1185 (" ".join([w[1] for w in words_by_id[i:i+word_count]]),1186 biggerBox([w[2] for w in words_by_id[i:i+word_count]])))1187 norm_text = " ".join(words) # normalize whitespace1188 for t in detected_texts:1189 scored_texts.append((_score(t[0], norm_text), t[0], t[1]))1190 scored_texts.sort()1191 elif match == 0.0:1192 # text == "", match == 0 => every word is a match1193 for w in words_by_id:1194 detected_texts.append((w[1], w[2]))1195 scored_texts = [(0.0, t[0], t[1]) for t in detected_texts]1196 else:1197 # text == "", match != 0 => no hits1198 detected_texts = []1199 scored_texts = []1200 return [st for st in scored_texts if st[0] >= match]1201def _score(w1, w2):1202 closeMatch = {1203 '1l': 0.1,1204 '1I': 0.2,1205 'Il': 0.21206 }1207 def levenshteinDistance(w1, w2):1208 m = [range(len(w1)+1)]1209 for j in xrange(len(w2)+1):1210 m.append([])1211 m[-1].append(j+1)1212 i, j = 0, 01213 for j in xrange(1, len(w2)+1):1214 for i in xrange(1, len(w1)+1):1215 if w1[i-1] == w2[j-1]:1216 m[j].append(m[j-1][i-1])1217 else:1218 # This is not part of Levenshtein:1219 # if characters often look similar,1220 # don't add full edit distance (1.0),1221 # use the value in closeMatch instead.1222 chars = ''.join(sorted(w1[i-1] + w2[j-1]))1223 if chars in closeMatch:1224 m[j].append(m[j-1][i-1]+closeMatch[chars])1225 else:1226 # Standard Levenshtein continues...1227 m[j].append(min(1228 m[j-1][i] + 1, # delete1229 m[j][i-1] + 1, # insert1230 m[j-1][i-1] + 1 # substitute1231 ))1232 return m[j][i]1233 return 1 - (levenshteinDistance(w1, w2) / float(max(len(w1),len(w2))))1234def _hocr2words(hocr):1235 rv = {}1236 hocr = hocr.replace("<strong>","").replace("</strong>","").replace("<em>","").replace("</em>","")1237 hocr.replace("&#39;", "'")1238 for name, code in htmlentitydefs.name2codepoint.iteritems():1239 if code < 128:1240 hocr = hocr.replace('&' + name + ';', chr(code))1241 ocr_word = re.compile('''<span class=['"]ocrx?_word["'] id=['"]([^']*)["'] title=['"]bbox ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)["';][^>]*>([^<]*)</span>''')1242 for word_id, bbox_left, bbox_top, bbox_right, bbox_bottom, word in ocr_word.findall(hocr):1243 bbox_left, bbox_top, bbox_right, bbox_bottom = \1244 int(bbox_left), int(bbox_top), int(bbox_right), int(bbox_bottom)1245 if not word in rv:1246 rv[word] = []1247 middle_x = (bbox_right + bbox_left) / 2.01248 middle_y = (bbox_top + bbox_bottom) / 2.01249 rv[word].append((word_id, (middle_x, middle_y),1250 (bbox_left, bbox_top, bbox_right, bbox_bottom)))1251 return rv1252def _getScreenSize():1253 global _g_screenSize1254 _, output = _runcmd("xwininfo -root | awk '/Width:/{w=$NF}/Height:/{h=$NF}END{print w\" \"h}'")1255 s_width, s_height = output.split(" ")1256 _g_screenSize = (int(s_width), int(s_height))1257def iUseWindow(windowIdOrName = None):1258 global _g_lastWindow1259 if windowIdOrName == None:1260 if _g_lastWindow == None:1261 _g_lastWindow = iActiveWindow()1262 elif windowIdOrName.startswith("0x"):1263 _g_lastWindow = windowIdOrName1264 else:1265 _g_lastWindow = _runcmd("xwininfo -name '%s' | awk '/Window id: 0x/{print $4}'" %1266 (windowIdOrName,))[1].strip()1267 if not _g_lastWindow.startswith("0x"):1268 raise BadWindowName('Cannot find window id for "%s" (got: "%s")' %1269 (windowIdOrName, _g_lastWindow))1270 _, output = _runcmd("xwininfo -id %s | awk '/Width:/{w=$NF}/Height:/{h=$NF}/Absolute upper-left X/{x=$NF}/Absolute upper-left Y/{y=$NF}END{print x\" \"y\" \"w\" \"h}'" %1271 (_g_lastWindow,))1272 offset_x, offset_y, width, height = output.split(" ")1273 _g_windowOffsets[_g_lastWindow] = (int(offset_x), int(offset_y))1274 _g_windowSizes[_g_lastWindow] = (int(width), int(height))1275 _getScreenSize()1276 return _g_lastWindow1277def iUseImageAsWindow(imageFilename):1278 global _g_lastWindow1279 global _g_screenSize1280 if not eye4graphics:1281 _log('ERROR: iUseImageAsWindow("%s") called, but eye4graphics not loaded.' % (imageFilename,))1282 raise EyenfingerError("eye4graphics not available")1283 if not os.access(imageFilename, os.R_OK):1284 raise BadSourceImage("The input file could not be read or not present.")1285 _g_lastWindow = imageFilename1286 imageWidth, imageHeight = imageSize(imageFilename)1287 if imageWidth == None:1288 _log('iUseImageAsWindow: Failed reading dimensions of image "%s".' % (imageFilename,))1289 raise BadSourceImage('Failed to read dimensions of "%s".' % (imageFilename,))1290 _g_windowOffsets[_g_lastWindow] = (0, 0)1291 _g_windowSizes[_g_lastWindow] = (imageWidth, imageHeight)1292 _g_screenSize = _g_windowSizes[_g_lastWindow]1293 return _g_lastWindow1294def iActiveWindow(windowId = None):1295 """ return id of active window, in '0x1d0f14' format """1296 if windowId == None:1297 _, output = _runcmd("xprop -root | awk '/_NET_ACTIVE_WINDOW\(WINDOW\)/{print $NF}'")1298 windowId = output.strip()1299 return windowId1300def drawBboxes(inputfilename, outputfilename, bboxes):1301 """1302 Draw bounding boxes1303 """1304 if inputfilename == None:1305 return1306 draw_commands = []1307 for bbox in bboxes:1308 left, top, right, bottom = bbox1309 color = "green"1310 draw_commands += ["-stroke", color, "-fill", "blue", "-draw", "fill-opacity 0.2 rectangle %s,%s %s,%s" % (1311 left, top, right, bottom)]1312 _runDrawCmd(inputfilename, draw_commands, outputfilename)1313def drawBbox(inputfilename, outputfilename, bbox, caption):1314 """1315 Draw bounding box1316 """1317 if inputfilename == None:1318 return1319 draw_commands = []1320 left, top, right, bottom = bbox1321 color = "green"1322 draw_commands += ["-stroke", color, "-fill", "blue", "-draw", "fill-opacity 0.2 rectangle %s,%s %s,%s" % (1323 left, top, right, bottom)]1324 draw_commands += ["-stroke", "none", "-fill", color, "-draw", "text %s,%s '%s'" % (1325 left, top, _safeForShell(caption))]1326 _runDrawCmd(inputfilename, draw_commands, outputfilename)1327def drawWords(inputfilename, outputfilename, words, detected_words):1328 """1329 Draw boxes around words detected in inputfilename that match to1330 given words. Result is saved to outputfilename.1331 """1332 if inputfilename == None:1333 return1334 draw_commands = []1335 for w in words:1336 score, dw = findWord(w, detected_words)1337 left, top, right, bottom = detected_words[dw][0][2]1338 if score < 0.33:1339 color = "red"1340 elif score < 0.5:1341 color = "brown"1342 else:1343 color = "green"1344 draw_commands += ["-stroke", color, "-fill", "blue", "-draw", "fill-opacity 0.2 rectangle %s,%s %s,%s" % (1345 left, top, right, bottom)]1346 draw_commands += ["-stroke", "none", "-fill", color, "-draw", "text %s,%s '%s'" % (1347 left, top, _safeForShell(w))]1348 draw_commands += ["-stroke", "none", "-fill", color, "-draw", "text %s,%s '%.2f'" % (1349 left, bottom+10, score)]1350 _runDrawCmd(inputfilename, draw_commands, outputfilename)1351def drawIcon(inputfilename, outputfilename, iconFilename, bboxes, color='green', area=None):1352 if inputfilename == None:1353 return1354 if type(bboxes) == tuple:1355 bboxes = [bboxes]1356 show_number = False1357 else:1358 show_number = True1359 draw_commands = []1360 for index, bbox in enumerate(bboxes):1361 left, top, right, bottom = bbox[0], bbox[1], bbox[2], bbox[3]1362 draw_commands += ["-stroke", color, "-fill", "blue", "-draw", "fill-opacity 0.2 rectangle %s,%s %s,%s" % (left, top, right, bottom)]1363 if show_number:1364 caption = "%s %s" % (index+1, iconFilename)1365 else:1366 caption = iconFilename1367 draw_commands += ["-stroke", "none", "-fill", color, "-draw", "text %s,%s '%s'" % (1368 left, top, _safeForShell(caption))]1369 if area != None:1370 draw_commands += ["-stroke", "yellow", "-draw", "fill-opacity 0.0 rectangle %s,%s %s,%s" % (area[0]-1, area[1]-1, area[2], area[3])]1371 _runDrawCmd(inputfilename, draw_commands, outputfilename)1372def drawClickedPoint(inputfilename, outputfilename, clickedXY):1373 """1374 clickedXY contains absolute screen coordinates1375 """1376 if inputfilename == None:1377 return1378 x, y = clickedXY1379 x -= _g_windowOffsets[_g_lastWindow][0]1380 y -= _g_windowOffsets[_g_lastWindow][1]1381 draw_commands = ["-stroke", "red", "-fill", "blue", "-draw", "fill-opacity 0.2 circle %s,%s %s,%s" % (1382 x, y, x + 20, y)]1383 draw_commands += ["-stroke", "none", "-fill", "red", "-draw", "point %s,%s" % (x, y)]1384 _runDrawCmd(inputfilename, draw_commands, outputfilename)1385def _screenToWindow(x,y):1386 """1387 Converts from absolute coordinats to window coordinates1388 """1389 offsetX = _g_windowOffsets[_g_lastWindow][0]1390 offsetY = _g_windowOffsets[_g_lastWindow][1]1391 return (x-offsetX, y-offsetY)1392def _windowToScreen(x,y):1393 """1394 Converts from window coordinates to screen coordinates1395 """1396 offsetX = _g_windowOffsets[_g_lastWindow][0]1397 offsetY = _g_windowOffsets[_g_lastWindow][1]1398 return (x+offsetX, y+offsetY)1399def drawLines(inputfilename, outputfilename, orig_coordinates, final_coordinates):1400 """1401 coordinates contains the coordinates connected by lines1402 """1403 if inputfilename == None:1404 return1405 # The command which will be run1406 draw_commands = []1407 for pos in xrange(len(final_coordinates)-1):1408 # Get the pair coordinates1409 (x, y) = (final_coordinates[pos][0], final_coordinates[pos][1])1410 (nextX, nextY) = (final_coordinates[pos+1][0], final_coordinates[pos+1][1])1411 # Convert to window coordinates1412 (drawX, drawY) = _screenToWindow(x,y)1413 (drawnextX, drawnextY) = _screenToWindow(nextX, nextY)1414 # Draw a pair of circles. User-given points are blue1415 if (x, y) in orig_coordinates:1416 draw_commands += ["-fill", "blue", "-stroke", "red", "-draw", "fill-opacity 0.2 circle %d, %d %d, %d" % (drawX, drawY, drawX-5, drawY-5)]1417 # Computer-generated points are white1418 else:1419 draw_commands += ["-fill", "white", "-stroke", "red", "-draw", "fill-opacity 0.2 circle %d, %d %d, %d" % (drawX, drawY, drawX-5, drawY-5)]1420 # Draw the line between the points1421 draw_commands += ["-stroke", "red", "-draw", "line %d, %d, %d, %d" % (drawX, drawY, drawnextX, drawnextY)]1422 draw_commands += ["-stroke", "black", "-draw", "line %d, %d, %d, %d" % (drawX+1, drawY+1, drawnextX+1, drawnextY+1)]1423 if len(final_coordinates) > 0:1424 lastIndex = len(final_coordinates)-11425 (finalX, finalY) = _screenToWindow(final_coordinates[lastIndex][0], final_coordinates[lastIndex][1])1426 draw_commands += ["-fill", "blue", "-stroke", "red", "-draw", "fill-opacity 0.2 circle %d, %d %d, %d" % (finalX, finalY, finalX-5, finalY-5)]1427 _runDrawCmd(inputfilename, draw_commands, outputfilename)1428def evaluatePreprocessFilter(imageFilename, ppfilter, words):1429 """1430 Visualise how given words are detected from given image file when1431 using given preprocessing filter.1432 """1433 global _g_preprocess1434 evaluatePreprocessFilter.count += 11435 preprocessed_filename = '%s-pre%s.png' % (imageFilename, evaluatePreprocessFilter.count)1436 _runcmd("convert '%s' %s '%s' && tesseract %s eyenfinger.autoconfigure hocr" %1437 (imageFilename, ppfilter, preprocessed_filename,1438 preprocessed_filename))1439 detected_words = _hocr2words(file("eyenfinger.autoconfigure.html").read())1440 scored_words = []1441 for w in words:1442 try:1443 score, word = findWord(w, detected_words)1444 except BadMatch:1445 return1446 scored_words.append((score, word, w))1447 scored_words.sort()1448 avg_score = sum([s[0] for s in scored_words])/float(len(scored_words))1449 evaluatePreprocessFilter.scores.append( (scored_words[0][0] + avg_score, scored_words[0][0], avg_score, ppfilter) )1450 evaluatePreprocessFilter.scores.sort()1451 # set the best preprocess filter so far as a default1452 _g_preprocess = evaluatePreprocessFilter.scores[-1][-1]1453 drawWords(preprocessed_filename, preprocessed_filename, words, detected_words)1454 sys.stdout.write("%.2f %s %s %s\n" % (sum([s[0] for s in scored_words])/float(len(scored_words)), scored_words[0], preprocessed_filename, ppfilter))1455 sys.stdout.flush()1456evaluatePreprocessFilter.count = 01457evaluatePreprocessFilter.scores = []1458def autoconfigure(imageFilename, words):1459 """1460 Search for image preprocessing configuration that will maximise1461 the score of finding given words in the image.1462 Returns configuration as a string.1463 """1464 # check image width1465 iUseImageAsWindow(imageFilename)1466 image_width = _g_windowSizes[_g_lastWindow][0]1467 resize_filters = ['Mitchell', 'Catrom', 'Hermite', 'Gaussian']1468 levels = [(20, 20), (50, 50), (80, 80), (5, 5), (95, 95),1469 (30, 30), (40, 40), (60, 60), (70, 70), (60, 60),1470 (10, 30), (30, 50), (50, 70), (70, 90), (80, 100)]1471 zoom = [1, 2]1472 for f in resize_filters:1473 for z in zoom:1474 for blevel, wlevel in levels:1475 evaluatePreprocessFilter(1476 imageFilename,1477 "-sharpen 5 -level %s%%,%s%%,3.0 -sharpen 5" % (blevel, wlevel),1478 words)1479 evaluatePreprocessFilter(...

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