[HowTo] onAppear() and observe() --- use the handler function and how it works

Asked by Shawn Robertson

Version using 1.1.0
Windows 7 64 bit
use onAppear command

I use onAppear quite often to read dialog boxes and perform commands. one example is:

onAppear("1421877826215.png", paste('List Item'))

in 1.0.1 it performs this fine and continues script.

in 1.1.0 it performs this but then stops, supplies an error and the script halts.

i am calling this function from another script so the error just says error in line XXX where im calling it so I end up having to go find the parent script and run it individually to get the actual error which asys:

[error] script [ TestScratchpad2 ] stopped with error in line 1
[error] java.lang.ClassCastException ( java.lang.ClassCastException: java.lang.Integer cannot be cast to org.sikuli.script.ObserverCallBack )

you can use any parameter with onAppear and it will run with the parameter but always gives an error. the error changes depending on which parameter you call.

if you call it like this:

onAppear("1421877826215.png", popup('List Item')

then you get this error:

[error] script [ TestScratchpad2 ] stopped with error in line 1
[error] java.lang.NullPointerException ( java.lang.NullPointerException )

for now I am falling back to 1.0.1 since i use this in so many areas I can not change them all to wait() functions to work around it.

Question information

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

to make the info available for others

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

You just ran into a trap with version 1.0.1:
Despite the fact, that nominally a function object (simply the name of the function, that should be called back in case of event happened and which is clearly stated in the docs), your usage worked (meaning: did not produce an error) due to a sloppy implementation.
But though it "works" in 1.0.1, it does not do anything, that makes sense (or might look ok accidentally):
at the time, the script executes the line with the onAppear, the given function (paste or popup in your case) is executed.
A following observe() simply does nothing (due to a bug: if you want an infinite observe you have to specify a very long timeout).
But if you use an observe(someTime), you will get an exception in 1.0.1 as well.

The correct usage for that what you want according to the Python language specs, is to use an anonymous function, which is a so called lambda expression in Python (for 1.0.1):

onAppear(img, lambda e:popup("image appeared"))
observe(60*60*24) # waits one day ;-)

and with 1.1.0:
onAppear(img, lambda e: popup("image appeared"))
observe() # waits forever

with the next build of 1.1.0 even this works:
onAppear(img, lambda: popup("image appeared"))
observe() # waits forever

So since you have to adjust your code anyways, I suggest to go back to 1.1.0 again, since the observe feature in 1.1.0 is dramatically enhanced and observe() does what it should.

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

Sorry Raiman I dont understand. When I read the Python documentation it stated that if I wanted to use the onAppear() function that it accepted one parameter so I thought "oh how neat i can use this onAppear to say when this item appears perform this function.

but you are saying that is not how it was designed and not its intended function?

You are saying in 1.0.1 that onAppear(img, paste('text')) was NOT support to work but a defect allowed it to work with out displaying an error in the IDE and that in 1.1.0 that defect is fixed now which is why i get an error in the IDE now?

What are the cons to allowing the onAppear() function to call another function?

I dont understand how the observe() is supposed to work and what lambada is etc so now i'm more confused unfortunately. I apologize.

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

ok, no problem.

say you have just this line
onAppear("someImage.png", paste("text"))

let us look, what the Jython interpreter does when he arrives at this line (somewhat simplificated):

1. sees the name onAppear and knows, that this is defined as a method in class Region and takes 2 parameters
2. finds the 2 items comma separated enclosed in pair of brackets
3. so the onAppear is correct and is pushed on a stack
4. as 1st parameter it sees "someImage.png" - nothing to evaluate - just push on stack
5. as 2nd parameter it finds paste("text"), which is now processed similar like the steps 1 - 4 for the Region method paste
6. after having pushed paste and "text" to the stack, nothing more to evaluate on this line
7. now the line can be executed - nothing happened until now
8. now the stack is resolved and executed backwards (popped)
9. the paste("text") is executed, does nothing of value at this moment and is replaced by the return value (0 or 1) on the stack
10. now the onAppear can be executed, which simply registers with the given region the advice, that when the next observe() is executed for this region, it should be checked, wether "someImage.png" has appeared. the second parameter is stored for future use as a callback target (handler name), when the image appears at the next observe.
11. still nothing happened until now (besides the useless paste), that has something to do with searching images

with version 1.0.1 (caused by the sloppy implementation) at the time of this execution of onAppear, the mismatch of the second parameter (should be the name of a handler function) was not detected and on top did not show any problem during observe.

This has changed in 1.1.0 due to a more strictly implementation, so the parameter mismatch is already detected when the onAppear is processed.

Why you got the impression, that this did something useful for you with version 1.0.1: I cannot judge, because I do not know your workflow and what happens on your screen.

So what observe does:
when you call observe() for a region it simply loops around the registered onXXX advices and for every onXXX that happens, it calls the registered handler function and waits until your handler finishes and returns.

lambda x,y,... : statement (see Python docs)

is simply a handler, that has no name (anonymous), but is a function object, that is registered as handler function when used with onAppear and that is called during observe, when the event happens. so it produces a valid parameter.

I hope it helps.

Can you help with this problem?

Provide an answer of your own, or ask Shawn Robertson for more information if necessary.

To post a message you must log in.