creating a loop with several possible matches

Asked by matthias

I want to create a loop until the first of several matches has been returned.
Each match will trigger a different set of commands.
I tried something like this, but did not succeed to include several image-options:

while not find(<img1>):
    sleep(1)

if find(<img1>):
    click()
    ...

if find(<img2>):
    click()
    ...

I am sure this is quite easy to do, so please help me out. =)
Many thanks!

Question information

Language:
English Edit question
Status:
Solved
For:
SikuliX Edit question
Assignee:
No assignee Edit question
Solved by:
matthias
Solved:
Last query:
Last reply:

This question was reopened

Revision history for this message
RaiMan (raimund-hocke) said :
#1

the following should work both in 0.9.8 and 0.9.9 (difference according exception FindFailed)

theEnd = False
while not theEnd:
   sleep(1)
   setTrowException = False
   if find(<img1>):
      case = 1
   elif find(<img2>):
      case = 2
   elif ...
      ...
   else:
      case = 0
   setThrowException = True

   if case == 0:
      continue # nothing found, check again

   if case == 1:
      .... # here go your corresponding commands
   elif case == 2:
      ...

   # you need some code, to decide to stop the loop by saying: theEnd = True

With 0.9.9 the default is, that find() throws exception FindFailed, so you need to switch this behavior off in your case for the sequence of finds(). The first found will terminate the sequence and continue with the processing. The match is in find.region.I divided this in two parts, so you have a chance to implement your error handling (try: except:) with the commands.

Since each unsuccessful find() may consume some time due to retries, you may substitute find(<img>) by wait(<img>, 1) which only makes one try (the pitfall with this is, that the match is not recorded in find.region in the moment, so if you need the match, you have to do a find(<img>) again during processing of the case.

Another possible coding:
theEnd = False
while not theEnd:
   sleep(1)
   setTrowException = False
   m1 = find(<img1>)
   m2 = find(<img2>)
      ...
   setThrowException = True

   if m1:
      .... # here go your corresponding commands
   if m2:
      ....

   # you need some code, to decide to stop the loop by saying: theEnd = True

This tries always every find() and may be useful if more than one could be possible. The m's are None in case of not found and otherwise contain the match.

Revision history for this message
matthias (mpuschmann) said :
#2

Both codings work great. Thanks a lot, Raimund!

Revision history for this message
stackless (tismer) said :
#3

you should use the function calls to set/delete the exception throwing.
The way you did it caused your typo to be not found (just by me, I guess):

   setTrowException = False

Revision history for this message
RaiMan (raimund-hocke) said :
#4

Sorry, its always a risk to write down code without checking by a compiler or even runtime system.

The correct use is:
setThrowException(True)
setThrowException(False)

Revision history for this message
treats (jwleaman) said :
#5

"
Since each unsuccessful find() may consume some time due to retries, you may substitute find(<img>) by wait(<img>, 1) which only makes one try (the pitfall with this is, that the match is not recorded in find.region in the moment, so if you need the match, you have to do a find(<img>) again during processing of the case.
"<quote from RaiMan>

So I have a script up and running similar to what you laid out in your first example. I swapped out my "finds" with "waits" and didn't see a significant performance. I am not worried if my match is recorded in the find.region but I am trying to get this as fast as possible... any thoughts?

Revision history for this message
treats (jwleaman) said :
#6

Is there, perhaps, a way to define a specific region to look at so the program doesn't need to search the entire screen?

Revision history for this message
RaiMan (raimund-hocke) said :
#7

find vs. wait: a timing difference is only in case of NOT found.

specific region: yes. that's possible.

use matches = Subregion(x, y, w, h).inside().find(img) (only find() possible!)
If you want to have the match, you have to say matches[0], because Subregion.inside().find() returns a list of matches.

You can get a Subregion(), by using the right button of the 3 capture buttons in the IDE.

Its possible to calculate x, y, w, h and say the following:
ref = find(<a reference for upper left corner of region>)
x = ref.getX()+offsetX
y = ref.getY()+offsetY
subReg = Subregion(x, y, 500, 300)
m1 = subReg.inside().find(<img1>)[0]
m2 = subReg.inside().find(<img2>)[0]
...

ATTENTION:
If you want to use the calculating-version, you have to "override" the Subregion(), with the following def(), to avoid running into a buggy situation, where the IDE corrupts your script (you loose your pictures), when saving and reloading a script containing Subregion(x, y, w, h), where x,y,w,h are variables and not numbers! (the above mentioned region-capture works).

def myRegion(x,y,w,h):
   return eval("Subregion"+"(x,y,w,h)")

and use
subReg = myRegion(x, y, 500, 300) instead.

Tipp: if you run a script from command line, the output on stdout gives some more information on timing. So you may get information on whether there is a pay off using Subregion() with some simple tests.

Revision history for this message
treats (jwleaman) said :
#8

We talked about using find within a subregion and the limitations of wait. Is it possible to "click()" within a subregion?

Revision history for this message
RaiMan (raimund-hocke) said :
#9

Not directly in the current version (will come with 0.10)

But since it is possible to click a Match and it's possible to construct a match object, the following works:
m = Match(x,y,w,h,1)
click(m)

x,y the coordinates of the upper left corner and w,h width and height of the rectangle of the screen, where you want to click the center. The 5th parm is formally needed and should be 1 (similarity: 1 means exact match).

So in the extreme you could say, if you want to click near the middle of the screen (1280 x 800):
click(Match(640,400,1,1,1))

So again you only have to calculate pos and dim some how.

Revision history for this message
matthias (mpuschmann) said :
#10

One of the loops should end at a certain time of day using the contained function strftime(). Any suggestion how to include that into the code? Thank you in advance!

Revision history for this message
RaiMan (raimund-hocke) said :
#11

Here you are:

Somewhere near top of script:

import time
theEnd = "22:00" # the time it should stop as your local time

in the loop that should end at the specific time (mind indentation!)

if theEnd < time.strftime("%H:%M"): # use < because you never know when the test happens ;-)
 break

Revision history for this message
matthias (mpuschmann) said :
#12

Works. Thank you very much!

Revision history for this message
Smythe Winsington (zrb0529) said :
#13

I am trying to make a modification of this script basically where it continues to run after it has made a match, for example:

the script runs as above and finds a match, executes associated command and then
restarts from the beginning, if nothing has changed (the same match is made) nothing happens but if something does change (a different match is made) it executes that command and restarts...maybe I am making this too complicated

Revision history for this message
Smythe Winsington (zrb0529) said :
#14

nevermind, I've figured it out :0) Thanks!