[open] "Animated" distractor
Greetings.
I am coding an experiment which requires a distractor stimuli in some of the trial conditions. The distractor is supposed to be an X, that scrolls diagonally a bit off to the side of the main stimuli in the center. I have written the following inline script, in which I have attempted to create a for loop, where each subsequent "frame" of the canvas would offset the x and y coordinates of the distractor by 30 pixels, creating a short 5-frame "animation". The first appearance of the stimuli in each trial is drawn from a range of random offsets, but all subsequent iterations should follow in the above 30-pixel pattern. But alas, they all appear at random. Does anyone see what I am missing?
Here's the code:
from openexp.canvas import canvas import random c1 = self.copy_sketchpad("stimuli") # copy stimuli sketchpad start=[-250, -150, -100, 100, 250, 500] # starting offset range for x and y x = random.choice(start) + c1.xcenter() # random choice from offset range added to canvas center y = random.choice(start) + c1.ycenter() # random choice from offset range added to canvas center p_step = 30 # pixel steps t_step = 500 # time steps for time in range(0, 1500, t_step): # for loop to display animated distractor "X" c1.clear() c1.text('X', x = x + p_step, y = y + p_step) c1.show() self.sleep(100)
Best regards,
Jákup
Comments
Hi Jákub!
x = x+ p_step when used in the text() function of a Canvas object does not mean the same as x = x+ p_step when used as a single line of Python code. So, basically, your code works, but it keeps drawing the X on the same location. With the small adjustment you see above, the x- and y-coordinates now do change with every step.
EDIT: I now notice that you speak of a five frame animation, but what you have now is a script for three frames (since 1500/t_step = 3). I don't know if this is your whole script, so it might be that you've solved this already, but I thought a small heads-up was appropriate
Good luck!
Edwin
Hello Edwin.
Thank you soooo much!
I love it when the solution is so elegant as this
And thanks for the heads up! With the solutions I was trying out, a three-step animation was easier to keep track of, but the final version will indeed be with 300 ms steps
Thanks again man!
Hello again.
Now I have another problem I did not foresee. This distractor animation is supposed to overlay the experimental stimuli, which I thought I could implement by displaying another copy of the sketchpad in the for loop:
However this gives a blinking display, each time the distractor sketchpad (c1) is cleared in the for loop. I can get past this by discounting the clear() function, but then makes the distractor Xs appear as a continuous line progressing across the screen instead of one X for each frame.
How can I implement this distractor animation while keeping a background sketchpad constant?
Best regards,
Jákup
What you're doing now is updating the screen every t_step with either the blank or the distractor display. This will cause a blinking between those to displays at a rate of t_step (causing an annoying flickering). Why don't you just draw the X on the canvas with experimental stimuli?
Also, without the clear function, you draw the distractor on different locations, but on the same canvas. What you want to do, is copy the basic canvas (with everything on it but the distractor) and draw the distractor onto that canvas for each different location.
Mind you, in this way of presenting stimuli, you are drawing inside of the loop where you are updating the screen. This means you're adding extra delay. For this small drawing and calculating two new coordinates, it won't be that much extra delay (probably neglectable considering the size of t_step), but when you are doing more elaborate stuff (and/or really precise timing), this would be something to take into consideration.
NOTE: I haven't tested the above script myself, so if it doesn't work, please inform me
Good luck!
Edwin
Aaaah ok. Thanks again! Especially for explaining exactly what is going with(out) the clear function in contrast with copy(). That did exactly what I wanted. So thanks again!
The reason I'm doing this in script form, is that I am transferring an existing matlab experiment to OpenSesame, and this was how the distractor was coded. When you ask, why I don't draw the X on the canvas with experiment stimuli, are you thinking about drawing an X in a sketchpad, and then having its coordinated defined by loop variables, or something similar? Would that actually be faster then in the above case? And if I stick with this, perhaps it would be preferable to move some of that code to the prepare faze.
But thank you again, so much, for your help
ATB
No, I meant to draw the X on the same canvas as the stimuli were on (in this case: c1), since you had two different canvasses on which you either drew the X or only the stimuli. I wouldn't recommend drawing the X in a sketchpad, since it would produce a messy experiment (with either a lot of different sketchpads or an extra loop, both constructions having their downside).
I've created an experiment with moving stimuli a while back: a Stoplight Game. The screen drawing for that one took quite a while (that is: on slow computers, the frame rate would drop). What I did there was creating all my canvasses up front and then just playing them in the trials. You could do something similar, by creating the canvasses in the prepare phase (or even before the experimental loop).
Preparatory script:
Run script:
But, as I said before: you do a really small amount of drawing, so I don't think this would be necessary.
Ah I see. Well, I need to have two canvases because, in addition to the animation we talked about above, I need it to repeat itself 5 times within a 1800 ms window. And each iteration should be as random as above. If it would be possible with one canvas, I would love to do that, as I loath redundancy
Again, thanks for the example. I really like your solution. It sort of keeps the messy stuff out-of-mind, and hidden behind the neat run tab However, I have not been able to figure out, how to use this if movement should be randomized at each iteration. I think I shall need some more practice, in order to fully grasp and utilize the preparatory phase of canvas scripting.
As to the timing issue, while it is not a problem with my short animation, it is when I try to implement the repeated animation in the 1800 ms window in nested loops. I've been trying various range() solutions and tested the timing using print time.time(), and I cannot get it to consistently perform as need. With one for loop, the timing was truly excellent on both Psycho and Legacy backends.
This is the best solution, I have come up with, after working on the problem for the better part of a full 24 hours [yes, I am a neophyte coder ]. The timing is a bit off, but the animation works as intended:
Is it possible to shift most of this to the preparatory phase? And does anyone have a clue, why the timing on this ranges from 1100 to 1500 ms? I suspect, that I'm missing something crucial, but for the life of me, I cannot seem to be able to find it.
Best wishes for the weekend!
Booyah! Using the system time seems to do the trick:
Now it runs for exactly 1800 ms
And you gotta love Python's randomizing functions - compare the p_step list and the choice() function with the unsightly if calls in the prior example
All the best!
Hi Jakup!
Great that you got it to work! And the random module is indeed a really nice one. On your question: it is possible to move most of this to the prepare phase. If this is the prepare phase of an item placed within the trial sequence (thus run once every trial) this will not effect the randomization, since at the start of every trial the prepare phase is run (with your random.choice solution).
BUT considering the large amount of canvasses that would need to be stored, it would make more sense to prepare all the individual canvasses up front (even before the trial sequence) and then in the prepare phase, randomly determine which of the screens to run. In the run phase you would simply show the specified screens. This, however, is a far to elaborate process to implement when the drawing time of your own solution isn't that large. So basically, I'd keep it as it is
What might be something to check, is whether you show your canvasses with appropriate timing with regard to your display's refresh rate. You'll probably show 'em two frames each (about 33 ms, if you use a 60 Hz monitor), but I'm not quite sure how fast the drawing is done.
Best!
Edwin
So when preparing canvases up front, would that simply mean inserting inline scripts at the top of OS overview tree, in which all the canvases were coded, declaring them global and then preparing and running them as needed further down the experiment? And would these then be created when OpenSesame launches the experiment, so you could have faster presentation times in exchange for one longer loading time?
I think you're right, btw., that my simple calculations do not necessarily need this amount of optimization at this point, but it's an interesting distinction to learn early on, before I might move on to more elaborate experiments.
In regards to the refresh rate, yes I noticed that sometimes, the time printed to the debug window would indicate that the timing was off by about a frame. I don't think it is critical for our paradigm, but thanks for pointing it out. I will run sum tests on the actual computers where we will conduct the experiment, just to be sure.
And thanks again for all your help! I wouldn't have come this far without it
All the best!
Jákup