[How To] wait for more than one image to appear

Created by RaiMan on on 2011-04-27
Last updated by:
RaiMan on on 2011-05-08

**** the challenge:
You have a situation, where in your workflow more than on image might appear in parallel or nearly at the same time. This might be valid in different combinations of appearing images. You want to know which images appeared, if at least one of them was found.

**** general solutions:

to reduce the complexity to a minimum, one can either use
- Region.exists() combined in a waiting loop
- or some Region.observe() with one or more handlers

**** using exists()

# img1 and img2 contain valid image filenames
weWait = 20 # max seconds we want to wait
while weWait > 0:
  mImg1 = exists(img1,0) # see comment
  mImg2 = exists(img2,0)
  if mImg1 or mImg2: break # leave the loop if at least one found
  weWait -= 1 # count down wait time

if not (mImg1 and mImg2):
  print "images did not appear"; exit(1)

if mImg1:
  print "image 1 appeared

if mImg2:
  print "image 2 appeared

--- comment
exists(img,0) does only one search and does not wait the standard 3 seconds, if image is not found. So for timing purposes you can calculate 0.5 seconds for every exists. So in your case it will last about 25 to 30 seconds, if n one of the images appears. So if 20 seconds is what you want, set weWait to an appropriate lower value (e.g. 15).

***** a generalized, reusable version with exists() packed in a def() function

Some sequences can be optimized in the script, but are written as is to avoid long lines in this post.
It is only tested with images, not with text to search for!
Be careful with the indentations, when pasting this script (here one blank is one level).

def waitOne(secs, *more):
 # analyze parameters
 reg = SCREEN
 weWait = secs # max time to wait
 if isinstance(more[0], Region):
  # a Region is given to restrict the search
  reg = more[0]
  more = more[1:]
 # check how the list of images is given
 if isinstance(more[0], tuple):
  images = more[0]
 elif isinstance(more[0], list):
  images = more[0]
  images = more

 # prepare the dictionary to return
 result = {"first" : None}
 for img in images:
  result[img] = None
 nImg = len(images)

 # look for the images to appear
 while weWait > 1:
  start = time.time()
  for img in images:
   if reg.exists(img,0):
    # the first one appeared
    result[img] = reg.getLastMatch()
    result['first'] = img
  if result.values().count(None) < nImg:
   # look again for the others
   for (key, value) in result.iteritems():
    if not value:
     result[key] = reg.exists(key,0)
  actWait = time.time()-start
  if actWait < getAutoWaitTimeout():
   # we wait the standard wait time minimum
   actWait = getAutoWaitTimeout()
  weWait -= actWait # count down wait time
 return result

--- the returned result
- is a dictionary whose keys are the image filenames
- the values are None for the images that did not appear
- the values contain the match, if this image was found
- the key "first" contains None, if nothing could be found and the match of the first image that was found

--- comment on result['first']
since the images are searched in turn one after the other in the sequence of the given parameters or the given list/tuple, result['first'] contains the filename of the image that in this sequence triggered the first success. Since the sequential search consumes time, this image need not be the first one, that appeared with respect to the real timeline. This can only be evaluated, if the searches would be threaded to happen "parallel" (this can (nearly) be achieved with a solution using observe() - see below)

--- usages:

--- search multiple images on the whole screen for about 20 seconds
anyFound = waitOne(20, "image1.png", "image2.png", "image3.png")

--- search multiple images in a given region for about 20 seconds
someRegion = Region(0,0,500,300) # or any other definition of a Region
anyFound = waitOne(20, someRegion, "image1.png", "image2.png", "image3.png")

--- search in a given region for about 20 seconds - images given as list
someRegion = Region(0,0,500,300) # or any other definition of a Region
someImages = ["image1.png", "image2.png", "image3.png"]
anyFound = waitOne(20, someRegion, someImages)

--- using the result in the last case
if not anyFound["first"]:
    # we had success
    print "this one came first:", anyFound["first"]
    for (key, value) in anyFound:
        if anyFound[key] == anyFound["first"]: continue # we know that
        if value: print key, "appeared"
        else: print key, "did not appear"
    print "None of them appeared"; exit(1)

***** using observe()

under construction