Update stimulus attribute with button press

Hey all,

I’m trying to update a stimulus (probe) orientation with a button press, but more basically i can’t seem to get it to update the stimulus at all with basic commands. I’m doing this from a bufferimagestim, and code is below. It’s super basic, and doesn’t seem to be coming up with any error it just isn’t doing it… I have tried it with stimulus.setOri(***) function but doesn’t work either, oops.

any help would be hugely appreciated, and do let me know if just being a noob…

Thanks,
Sammi

from psychopy import visual, core
screen      = visual.Window(size = (900,700),units='deg', colorSpace='rgb', fullscr = False, monitor = "testMonitor")
probecirc   = visual.Circle(screen, radius = 2.0, edges = 100, pos = (0.0, 0.0) , lineWidth = 5, lineColor = (0.25,0.25,0.25))
prbend      = visual.Line(screen, start = (1.8, 0.0), end = (2.2,0.0), lineWidth = 3, lineColor = (-1.0,-1.0,-1.0))


stimlist = [probecirc, prbend]
probe = visual.BufferImageStim(screen, stim = stimlist, rect = (-1,1,1,-1))
core.wait(1.00) #this is just so it happens before making everything stop, so its ready to draw/flip straight away

probe.draw()
screen.flip()
core.wait(1.0)

probe.ori += 15
probe.draw()
screen.flip()

This looks like a bug. I can update and draw the colour and opacity (in the demo too), but the new position or orientation values do not get passed on when BufferImageStim.draw() is called- even if the object itself lists the updated values.

There’s a comment in the BufferImageStim code that mentions uncertainty about a self.updateList() method which appears to be missing when comparing it to the original ImageStim I think it’s based off. I suspect this is the issue, though I’m kind of new to looking at the internals of the software so if anyone else has ideas…:confused:

Agree, probe.flipHoriz = True and probe.flipVert = True also work, so there seems to be a specific bug with updated position and orientation values not being applied. Would be good to report this as a bug here:

If you could make your code easily reproducible (i.e. add imports for visual and core and remove the specific monitor name (ideally replace with testMonitor, which most people have), and post that there, it would aid things no end.

Thanks for the suggestion - i’ve posted it on the github now and adjusted the code there (and above too) to help things along!

Thanks @srchekroud! xref:
https://github.com/psychopy/psychopy/issues/1288

I’m curious why you are using a BufferImageStim, and not a regular ImageStim, as your probe. [Edit: now I see why, reading your code more closely. Can you convert that to a single image?]

The idea of a buffer is that its essentially a STATIC screenshot (of visual things that are not going to need updating dynamically). The aim is to draw that screenshot very quickly, instead of drawing potentially dozens or even 100’s of individual *Stim (like: ImageStim, TextStim, ShapeStim, and so on). Its purpose is to optimize the drawing time of the statics bits, so that drawing any dynamic things can be done without dropping a frame. Think of it as being a visual background image, and you can draw things on top of it.

So one of the goals was to streamline the .draw() method a lot (as compared to an ImageStim). Currently, there’s a comment in .draw() that orientation should be update-able, but there is not code to do that. It could be added in, probably just copy and paste from visual.ImageStim.draw() or pretty close to it. But there are tradeoffs: you can have more features (like dynamic orientation) but slower draw times, or have fewer features with faster draw times. Adding support for orientation will mean slower draw times for everyone who uses a BufferImageStim.

The comments and documentation should definitely reflect the actual code (or the code changed to match the docs).

For dynamic visual things, a BufferImageStim should not be your go-to class. So I don’t think its a bug (but the docs should be fixed).

Hi @jeremygray, that was my initial thought too, but then saw that the logic was that a screen shot would allow easy compositing of two stimuli that otherwise would requires a bit of geometry to rotate the little indicator line on the circle. I was surprised that the BufferImageStim apparently had an orientation option, and so it seems that the bug is in the docs rather than the code per se.

I guess the question is, can the bitmap from a BufferImageStim simply be supplied as the texture to a regular ImageStim, which can be rotated and translated as desired? I’m not familiar enough with the class to say (and perhaps don’t want to rely on the API docs to tell me :wink: ).

Nice idea, @Michael! This works:

buf = visual.BufferImageStim(win, stim=[probecirc, prbend])
probe = visual.ImageStim(win, image=buf.image)
probe.ori = 90
probe.draw()
1 Like

Ah, the docs thing is because BufferImageStim inherits from regular ImageStim.

Yeah, Michael had it right - its a probe that ultimately will be rotated based on mouse movements in order to report an item from memory. Easier to do it as a whole image generated from buffer, than to rotate using some geometric transformation…or so i thought

You can use the visual.BufferImageStim as a texture for visual.ImageStim as you suggest, Jeremy, but when i just ran that on my laptop it appears to lose a huge amount of resolution upon transformation. I’ll have to try figure out a better way of generating this, and updating it, without losing so much quality!

Thanks though!

In your case, I think you only need to rotate the line. The circle does not need to rotate. I think it will work if you set the line to have pos=(0,0), then if you rotate it by setting its ori attribute it will rotate around (0,0), behaving as you want.

2 Likes

NB: JUST IGNORE EVERYTHING BELOW. I THINK JEREMY IS PROBABLY RIGHT ABOVE THAT A SIMPLE ROTATION OF YOUR EXISTING LINE WILL WORK OK. I WAS OVER-THINKING IT.

But the line is also translated from the origin, so that it sits on the circle (like a tick mark on the perimeter of a clock face, rather than a hand which extends from the centre of the clock).

So the OP would need to not only rotate the line, but also calculate its position in cartesian coordinates. Something like:

    probecirc.draw() # draw the circle (doesn't need rotation or translation)

    # rotate the indicator line:
    prbend.ori += 15

    # calculate its (x,y) coordinates based on the new angle and the
    # radius of the circle:
    prbend.pos = [cos(prbend.ori) * 2.0, sin(prbend.ori) * 2.0)]

    # layer the line on top of the circle:
    prbend.draw()

    # update the display:
    win.flip()

This is really only one more line of code, and avoids issues with the BufferImageStim approach.

I’m not sure, but when the line prbend is initially created, it might need to be anchored on the origin for this to work. e.g. something like start = (0.0, 0.0), end = (0.0, 0.4)

@Michael @jeremygray Yeah, Jeremy is completely right, sorry! I assumed its point of rotation would be around its own centre, rather than (0,0) !

Definitely a huge over-complication, but thanks to both for the help!

to clarify

prbend.ori += 30 will rotate the tick mark 30 degrees around a circle originating from (0,0), rather than just rotate the line itself around the centre. If the line is centred around the original probe circle, then it will move around its circumference as desired!

1 Like