repeat([waitTime]) how is this syntax with a region onchange?

Asked by MP on 2019-04-09

So I am fiddling along, reading the docs and getting a better understanding of the commands. Yet what I do miss are basic simple examples to show the structure and syntax of the different commands.

I got the following code: (which works)

def changed(event):

        print "something changed in ", event.region
        for ch in event.getChanges():
                ch.highlight() # highlight all changes
        wait(1)
        for ch in event.getChanges():
                ch.highlight() # turn off the highlights

r = Region(Region(1058,316,22,23))
# any change in r larger than 250 pixels would trigger the changed function
r.onChange(250, changed)
# another way to observe infinite

r.observeInBackground();wait(FOREVER)

===================

The problem is dat the change event picks up a single change 2 or 3 times within a second. I want to delay it by 4 seconds. That way I am sure that the change only counts for 1 change in 4 seconds.

I looked up the command in the docs:

 repeat([waitTime])

I tryed several places where to put it but it doesnt delay the event. I assume I got some syntax wrong... there is no clear example using this. Anyone can help me?

Cheers

Question information

Language:
English Edit question
Status:
Solved
For:
Sikuli Edit question
Assignee:
No assignee Edit question
Solved by:
RaiMan
Solved:
2019-04-14
Last query:
2019-04-14
Last reply:
2019-04-13

This question was reopened

  • 2019-04-12 by MP
RaiMan (raimund-hocke) said : #1

looks good. good progress.

The repeat() function is bound to the observe event and hence only available in the handler.

so this should do what you want:

def changed(event):
        print "something changed in ", event.region
        for ch in event.getChanges():
                ch.highlight() # highlight all changes
        wait(1)
        for ch in event.getChanges():
                ch.highlight() # turn off the highlights
        event.repeat(4)

MP (jozzzzzz) said : #2

He is still giving me twice a red border indicating changes on that location. But I think I know the problem: I need to delay the screenshot making of the onchange event by 2 seconds. After that he can make a screenshot of that location.

I tryed to type "wait 2" in the event but didnt work. So I assume you have to put the delay somewhere at the observe event ?

def changed(event):

        wait(2)
        print "something changed in ", event.region
        for ch in event.getChanges():
                ch.highlight() # highlight all changes
        wait(2)
        for ch in event.getChanges():
                ch.highlight() # turn off the highlights

        event.repeat(5)

r = Region(Region(1056,312,26,30))
# any change in r larger than 50 pixels would trigger the changed function
r.onChange(50, changed)
# another way to observe for 30 seconds

r.observeInBackground();wait(FOREVER)

RaiMan (raimund-hocke) said : #3

yes, onChange should only start, when you have a stable screen situation.

... but it must look so:

def changed(event):
        print "something changed in ", event.region
        for ch in event.getChanges():
                ch.highlight() # highlight all changes
        wait(2)
        for ch in event.getChanges():
                ch.highlight() # turn off the highlights
        event.repeat(5)

r = Region(Region(1056,312,26,30))
r.onChange(50, changed)

wait(2)
r.observeInBackground();wait(FOREVER)

... meaning you have to delay the observerstart

waiting in the handler does not make sense (except of course for the highlight delay), because everything already happened (like evaluating/filling the changes)

you talked about 4 seconds repeat frequency, now you have delay of abot 7 - 8 seconds in total (higlight + repeat)

RaiMan (raimund-hocke) said : #4

please don't do that:
you have posted the same question on Stack Exchange

The main place for SikuliX questions is here at launchpad, where I will be notified instantly and usually answer within a short time.

You asked 4 hours ago and did not get any reaction.
Such docs related questions are usually not answered there.

MP (jozzzzzz) said : #5

Hey thx for the amazing support. I will stay here with my questions! I will be surely donating. :)

I tryed the wait 2 at the place you told me. But even with wait 10 + event.repeat(10) he STILL does it 2 times - _ -
The repeat just repeats after the inserted seconds... so after 4 seconds the event runs again with the red borders. (it feels like it is ignoring the condition of the onchange)

When a new number comes, it flashes a bit for 2 - 3 seconds. So I would assume a wait of 4 - 5 would fix this problem. Cause maybe he takes a screenshot of the flashing , so meaning after X amount of seconds he compares the flashing color one with the final color one and gives me a wrong onchange event.

But it still gives me twice a notification that 2 changes happened. :( (+ 2 times the red borders) (I use the red borders and the print command as a debug tool for testing the onchange command.)

I feel I am close but man, it is not easy :)

RaiMan (raimund-hocke) said : #6

ok, seems to be some systematic problem.

I will look into it and come back asap.

Any chance, to have a look at the case (webpage)?

MP (jozzzzzz) said : #7

Hey, you gave me the idea to test my code on more things. If you just go on you tube, start my code and play something for 3 seconds then stop. He will do the exact same behavior and give me 2 onchange notifications.

Thx again for the support! I really wonder what the solution will be. :)

MP (jozzzzzz) said : #8

Even scrolling down this page (had my code on by accident) and I slide (slowly!) through a grey line of the website and end up in a white part. He still does the same behavior, giving me 2 notifications. :)

RaiMan (raimund-hocke) said : #9

I am sorry, but I made different tests and could not see this "double show" behaviour.

the last test was with this gif image (changing every 3 seconds from white to some content) visible in a browser:
https://github.com/RaiMan/SikuliX1/blob/master/API/src/main/resources/ImagesAPI/whitechange.gif

- at observestart it waits for changes
- the shot at this time is saved
- after waiting the given repeat-time it starts to wait for changes against the saved shot

MP (jozzzzzz) said : #10

I tested a lot this morning. And I also used your icon. With the icon and other tests I conclude that the code works when 1 frame, screen change happens.

But from the moment there is an animation (so more frames changes in a certain time) it gives the double onchange effect. You can easy try my test: use the code:
==========================================================
def changed(event):

        print "something changed in ", event.region
        for ch in event.getChanges():
                ch.highlight() # highlight all changes
        wait(1)
        for ch in event.getChanges():
                ch.highlight() # turn off the highlights
        event.repeat(6)

r = Region(Region(614,711,729,60))
# any change in r larger than 50 pixels would trigger the changed function
r.onChange(300, changed)
wait(6)
# another way to observe for 30 seconds

r.observeInBackground();wait(FOREVER)
==========================================================
, make the whole screen the region, play a you tube vid for 3 seconds. Then stop it. The timer in this code is put on 6 repeat and 6 wait. So it should only give 1 onchange effect, but it gives 2.

Hopefully this helps

RaiMan (raimund-hocke) said : #11

Nice try ;-)

... but when you stop the video after 3 seconds, then there are changes against the first shot, that reported the first changes after starting the script.
This then leads to the second changes after waiting the 6 seconds from the repeat.

the wait(6) before the observe only leads to that the observation is delayed 6 seconds after script start.

since you have a wait(FOREVER) in your script, it runs until killed by hand.

but after the second change event, there are no more changes.

To make it a bit clearer - this happens internally:
- at observestart a shot of the region is taken
- then with the ObserveScanRate it repeats looking for changes against the first shot
- if changes are detected, the last shot is saved and becomes the new reference and the handler is called
- if the handler returns normally, the observe will be repeated after the optional wait time

Your problem simply is, that you do not decide in the script when to stop the observation and/or the script.

MP (jozzzzzz) said : #12

I now understand better the mechanics behind it.

" - at observestart a shot of the region is taken"

1) I want that shot is being taken after 3 seconds after the onchange trigger. Is that possible?

2) I want to infinite let the observer be active, so that the onchange event can trigger when there is a change (or am I understanding it not clearly?)

My global idea:

I want to infinite observe the tiny region where a new number appears. Then take a shot after 3 seconds when the new number appears. When it does: I want to compare it with a grafic number I stored. He compares it and does a certain action that is programmed with each number. rinse and repeat. :)

MP (jozzzzzz) said : #13

Cause as mentioned in number one, it just doesnt work, even with a wait 6 , he still ignore the wait 6 and just makes an instant observe start shot. (just trying to fully grasp it and understand it :))

RaiMan (raimund-hocke) said : #14

at beginning of handler just wait 3 seconds and then make your compare/find logic on event.getRegion().

take a look at feature findAny(), which might be a bit faster with 10 images to search for, than doing this in a loop.
findAny() internally delegates the find op for each given image to a thread, so it works in parallel

MP (jozzzzzz) said : #15

1) "at beginning of handler just wait 3 seconds and then make your compare/find logic on event.getRegion()." Well isnt this the part that just doesnt work? I tryed it with the next code. Did I do something wrong? It still gives me the double notification+ 2 times the red border when tested on animation that lasted 3 seconds.

========================================================
def changed(event):
        wait(3)
        print "something changed in ", event.region
        for ch in event.getChanges():
                ch.highlight() # highlight all changes
        wait(1)
        for ch in event.getChanges():
                ch.highlight() # turn off the highlights

Settings.ObserveScanRate = 0.2

r = Region(Region(1056,314,27,27))
# any change in r larger than 50 pixels would trigger the changed function
r.onChange(50, changed)

# another way to observe for 30 seconds
r.observeInBackground();wait(FOREVER)
=====================================================

2) findAny(), I cant find it in your documentation. Can you show me simple example with observer, region and get any ? For example I have IMAGE1, IMAGE2, IMAGE3 and I want to match the appearing number. When it matches with IMAGES1 => print "image1 appeared!", etc

RaiMan (raimund-hocke) said : #16

seems you did not really understand, what I tried to explain in comment #11

--- findAny()
what about this :
https://sikulix-2014.readthedocs.io/en/latest/region.html#find-more-than-one-image-in-a-region-at-the-same-time

RaiMan (raimund-hocke) said : #17

... and the docs have an index and a search feature.

--- Settings.ObserveScanRate = 0.2
... and the docs
https://sikulix-2014.readthedocs.io/en/latest/scripting.html#Settings.ObserveScanRate
would also have told you thet this leads to a check for changesd every 5 seconds.

And please stop playing around with not relevant examples.
Just start to implement your workflow:
- find out, when your number appears (probably with onChange())
- in the handler find out what had happened and evaluate your number (probably findAny())

I already asked, wether it is possible for me, to have a look at your number case myself.

MP (jozzzzzz) said : #18

Thx again, I am just trying to grasp it. Which is not easy. I will practice a bit more and yes, I gonna stay on my workflow step by step. I tend to go all over the place.

The number case is on https://livecasino.betway.com/lobby

You need an account I think, after that click "live betaway roulette". With your expertise maybe you can guide me in a direction I didnt see yet.

RaiMan (raimund-hocke) said : #19

I already thought that it is something about gaming.

I checked the registration, but I will not do that - too much private information wanted.

So I cannot help you.

You should first check, wether this plattform is not aware of robots. otherwise you might be banned.
There are many implementations out there, that allow such platforms to realize, that it is not a human.

MP (jozzzzzz) said : #20

I only want to read info from the screen, I dont use any keystrokes or mouse strokes on the website itself with a bot.

I gonna practice more basic exercises till I understand sikulix and python better. You will see me a lot on this forum :p

 Thank you for all the help so far!

MP (jozzzzzz) said : #21

Instead of making a new topic, I want to reopen this one. I found 3 ways to read the numbers perfectly. xD But with all 3 I hit the wall of onchange trigger... I read post nr 11 again and again and again (+the docs on onchange and observe) to understand it. So I gonna try to explain myself how I understand it and hopefully I will see what I dont understand yet with your help.

Can you give me a general workflow how to implement the following:

I want to trigger event"read number" 5 seconds (there is a 4 second flashing animation in between) after an onchange trigger. Does the onchange event also make a start screenshot? If so, how do I delay it 4 seconds?

=============
Onchange(event)
wait(4)
r.oberserve(FOREVER) #I want to trigger the onchange event every single time there is a change, so infinite loop)
=============

workflow:

old number
start flashing new number
Trigger onchange
after trigger onchange make a screenshot after 4 seconds
new number not flashing anymore
Trigger event read OCR number
do action with new number

I am so close to seeing it, I would be damned to give up now :D

RaiMan (raimund-hocke) said : #22

ok, admitted: It is indeed a bit complicated with no experience in scripting/programming.

An observation is bound to a region, where the given events are expected. For the region you might specify one or more events.
The observation itself is started with observe/observeInBackground and simply is a loop with the steps:
- make a shot of the region
- cycle through the events and check, whether happened
  - if happend, run the given callback function and wait for its completion
- check wether the observation should pause or even stop

This means, the timing of the observations (shot and check) is completely in the observation loop.
The handler can only influence the behavior in case it is called (event happened): delay, one more, stop event check, stop observation.

Hope it helps a bit.

Your case is much easier, than you think:
def handler(event):
    event.stopObserver()

regNumber.onChange(handler)
regNumber.observe(100) # a time within that a change should happen
# the script waits here until the handler is called and stops the observation
wait(4) # delay the flashing
newNumber = regNumber.text()

MP (jozzzzzz) said : #23

mmmm I gonna test and read your post a million times. I understand most of it now.

so basically you make a start shot outside the change period? And then you start the observer again and then he compares to the previous shot? (so that is the check?)

Can you pinpoint precisely where the shot is made before the 4 second change period in your code? And precisely where the check of the shot is being made? I am sure I will than 100% understand it :)

=====================
def handler(event):
    event.stopObserver()

regNumber.onChange(handler)
regNumber.observe(100) # a time within that a change should happen
# the script waits here until the handler is called and stops the observation
wait(4) # delay the flashing
newNumber = regNumber.text()
=====================

Best RaiMan (raimund-hocke) said : #24

I do not make a start shot or something else.

I simply start the observation and wait for any change in the region.

According to your description, a new number starts with 4 seconds flashing.
The start of this flashing will lead to a change detection, call the handler, that instantly stops the observation and lets the script continue.

The rest should be clear.

I think the observe cycle should be clear with comment #22, adding too things:
- how often the loop is started again is controlled by the ObserveScanRate
- onChange: since changes are detected by comparing the previous shot with the current shot, a change will only be detected earliest in the second loop

MP (jozzzzzz) said : #25

Thanks RaiMan, that solved my question.