How to stop waiting in main script by observer handler

Asked by partosXP

Hello! Thx for a good tool.

I have a standard script with loop and try/except logic like:
***
While True:
try:
wait("1.png",300) #up to 5 min waiting
click("1.png")
except FindFailed:
print("cant find image")
***
and it works perfectly.

But sometimes i have an error messeges in my program and i want to handle them whithout many "if - else" constractions and whithout waiting 5 minutes.
I found "onAppear" and "observe" functions and it seems exactly what i need, but i cant stop waiting in main script by handler. For example:

***
def ErrorHandler(event):
print("Error found")

While True:
try:
onAppear("error.png",ErrorHandler)
observe(300,background = True) #set observer
wait("1.png",300) #up to 5 min waiting
click("1.png")
except FindFailed:
print("cant find image")
***

If i get error messege(error.png) after 30 sec - observer catch it, print "Error found", but main script still running and i have to loose 250sec more for waiting 1.png.
Is it possible to force next loop in main script from handler?

I checked two ways:
1) add something like "break" or "continue" in handler function (ok, that was stupid i suppose, it doesnt work even in clear python)
2) raise Exception in handler function to force end of loop in main script.
raise
raise FindFailed
raise FindFailed ("test")
return raise
return FindFailed
and it doesnt work.

Am i doing something wrong or it's not possible at all?

P.S. i found workaround with many loops and with "if exists" instead of long-wait, but it's not beauty and not usable in some more difficult situations (like one possible error in many steps).
for i in range (30):
if exists(1.png)
else if exists(2.png)
wait(3)

Question information

Language:
English Edit question
Status:
Solved
For:
SikuliX Edit question
Assignee:
No assignee Edit question
Solved by:
partosXP
Solved:
Last query:
Last reply:
Revision history for this message
RaiMan (raimund-hocke) said :
#1

solution: use a global variable

***
def ErrorHandler(event):
global shouldStop
print("Error found")
shouldStop = True

shouldStop = False
While True:
if shouldStop: break
try:
onAppear("error.png",ErrorHandler)
observe(300,background = True) #set observer
wait("1.png",300) #up to 5 min waiting
click("1.png")
except FindFailed:
print("cant find image")
***

... mind the indents ;-)

Revision history for this message
partosXP (partosxp) said :
#2

Thanks for the answer,RaiMan!

I thought about this way, but it can't *interrupt* current running of the loop, only break the next one.

I checked this code one more time now - after revealing of error.png - it still waiting for target 1.png for 300 sec more and break loop only after this.

The main idea of the question - how interrupt main script, while its running, by background process and its handler.
(ofcourse, i can rework script to not use so long waitings, but its just an example. The same problem if we have many short steps and one error handler for all of them.)

Wrap all micro steps to construction like:
***
while True:
if shouldStop:
break
ifexists("object"):
break
wait(1)
***
- seems to be so monstrous... i cant believe that its part of light side of power). is it?

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

what happens if you simply use exit() in the handler?

Revision history for this message
partosXP (partosxp) said :
#4

Using "exit()" - just stops execution of handler-function, but dont make any change to main process script.

def ErrorHandler(event):
print "First Error message by Handler"
exit()
print "Second Error message by Handler"

- it prints only first messege, not the second one (execution terminated, i think)

 And after that - still waiting 300 sec for target element in main script.

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

in a few minutes I will have a solution for you ;-)

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

this you can do:

def handler(e):
    # whatever needs to be done
    RUNTIME.terminate(1, "script ececution stopped in handler)

everything else as normal

if you run this in the IDE, take care, that the script is saved before running, since this ia brute force and stops the IDE.

most elegant:
- edit and save your script in the IDE
- run your script from commandline and watch it dying ;-)

Revision history for this message
partosXP (partosxp) said :
#7

Yes, it works like you said - all process terminated.
And i've got some pleasure - watching it dying)

But i dont want to kill all process, i just want to start next loop of main script.
I'm using this tool for non-stop monitoring. One loop failed - log error and go next try. Just looking for way to force "next loop", whithout waiting all timeout in command "wait(obj,300)".
I'm sorry if i didnt explained it clearly.

Great thanks for your time, btw. May be its just impossible to do it like this :(
Feel free to close this thread, if you have no more ideas too.

P.S> main script (remind):

While True:
try:
onAppear("error.png",ErrorHandler)
observe(300,background = True) #set observer
wait("1.png",300) #up to 5 min waiting. Needs force "continue" loop here, if handler catches error (no loose time for waiting and dont execute other steps).
click("1.png")
except FindFailed:
print("cant find image")

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

ok, then there are only 2 options left:

--- make the loop in a batch file on commandline using the brute force method comment #6, check the returncode and repeat the scriptrun if appropriate

--- integrate the continuation logic ito your workflow.
with 1.1.4 there might be another option instaed of observing: findany
https://sikulix-2014.readthedocs.io/en/latest/region.html#find-more-than-one-image-in-a-region-at-the-same-time

you can pack it in a def:

def checkImageAndError(image, errorImage, time):
    # time in seconds
    end = time.time() + time
    while time.time() < end:
        matches = findAny(image, error)
        if (len(matches) == 0):
            wait(3)
            continue
        if (len(matches == 1)): # one image found
            if matches[0].getIndex == 0:
                return matches[0] # good image
            else:
                return None # error image
        return None # both images found

# ... and then instaed of
wait("1.png",300)

# use
match = checkError("1.png", "error.png", 300)
if not match: continue

Revision history for this message
partosXP (partosxp) said :
#9

"findany", hm...
Nice idea, i'm not sure about it would resolve all my problems, but it's definitly better then others ways.
Need more tests and may be some modify it, i have to work)

Thank you very much, Raiman, its my first time when i asked for help about non-commercial soft and get answers so fast by developer, its amazing.