psychopy.org | Reference | Downloads | Github

Position of Stimuli


#1

Hello~

I’m having trouble coding the positions of my individual stimuli around the fixation cross. I’ve got a For loop, but I’m not sure how to code the individual positions of the stimuli. Any help would be appreciated.
I am getting this error a lot
’ ’ '
stimuli.pos = (0.5, 0)
AttributeError: ‘list’ object has no attribute ‘pos’

’ ’ '
Below is the code for my experiment so far.

’ ’ '
from psychopy import visual, core, event #import some libraries from PsychoPy
import random, copy
from random import randint
from decimal import *

#draw a window
mywin = visual.Window([800,600], monitor=“testMonitor”, units=“deg”)
mywin.update()

stimuli = [“ProbeCity1”, “ham”, “more spam”, “even more spam”]
for i in range(len(stimuli)):
message = visual.TextStim(mywin, text=str(i)+": "+stimuli[i])
message.draw()
mywin.flip()
stimuli.pos = (0.5, 0)
core.wait(8.0)

’ ’ '
Many many thanks


#2

Stimuli is a list. You need to get the individual stimulus out of it first:

stimulus = stimuli[i]
stimulus.pos = (0.5, 0)

And please format your posts so that the code appears correctly, three backticks (```) on the line before, and three after. It’s very difficult to read otherwise. … Just realized you tried that, but were entering the wrong key. On a US keyboard, the backtick is on the top left of the keyboard.

Edit

Looking again at your code, you should be setting the position on ‘message’:

message.pos = (0.5, 0)

And you need to set its position before you draw and flip.


#3

Hi Daniel,

Many thanks for your reply, its was very helpful. Yes, you are right about the backticks, it does look confusing without.

I entered the code and managed to get the list to appear in a different area of the screen. However I’m still not sure how to get the individual stimuli to be created around the fixation cross. I initially thought perhaps
using 4 different lists but that would not make sense. Sorry to act so thick, very new to python
The updated code is:

from psychopy import visual, core, event #import some libraries from PsychoPy
import random, copy
from random import randint
from decimal import *

#draw a window
mywin = visual.Window([800,600], monitor="testMonitor", units="deg")
mywin.update()

stimuli = ["ProbeCity1", "ham", "more spam", "even more spam"]
for i in range(len(stimuli)):
    message = visual.TextStim(mywin, text=str(i)+": "+stimuli[i])
    message.pos = (10, 4)
    message.draw()
    mywin.flip()
    core.wait(8.0) 

stimulus = stimuli[i]
stimulus.pos = (2, 0)

ProbeCity1.pos = (0.5, -0.2) 


#4

You need to consider more carefully what your variables actually represent. Try sprinkling things like:

print(stimulus)
print(type(stimulus))

around your code, which would helpfully show why it makes no sense to try setting the position of a string object. i.e. in your code above, stimulus is a simple string of characters, such as 'ham'. It doesn’t have a position, any more than it has a colour or a size. message, meanwhile, is a TextStim, which does have those attributes.

Also describe exactly what you want to achieve. In the code above, you keep re-creating a single text stimulus (message), giving it a new text value and then drawing it in the same position. At the end of the loop, you have only a single text stimulus, which contains the last string value in the stimuli list. If you want to be able to update the text stimuli later, you probably want to store four such stimuli in a list.

Describe exactly what you want to achieve to get some more useful solutions.

[EDIT] @daniel.riggs1 below says similar things, but much more thoroughly and carefully than me :wink:


#5

I’m not entirely sure what you’re trying to achieve, but it does seem like you’ll be needing to woodshed some Python.

Your list named “stimuli” is not actually a list of stimuli, it’s a list of strings. The TextStim objects you create in the loop are what is actually shown on the screen, and is what has a “pos” attribute, etc. So your lines:

stimulus​ = stimuli[0]
stimulus.pos = (2, 0)

… won’t do anything you’re hoping for.

Make sure you understand what’s happening in the for loop. Each time you go through, you’re creating a variable called message and creating a TextStim. Note that each time you do this, you overwrite the first one (you reset the value of “message” to the newly created TextStim), so after the for loop, “message” holds the last TextStim you created, and you have lost any reference to the all the ones you created before it.

Also note that you have given each TextStim the same position (10, 4), so they will overlap.

“ProbeCity1” on the last line will generate an error, because there is no variable named “ProbeCity1”.

Lastly, realize that no changes in position will actually be shown to the user until you call draw() and then flip() on the window. But the thing is, if you care about measuring reaction times from visual stimuli, there are a lot of things one needs to know how to do to not mess it up. In short, I know this wasn’t part of your question, but I recommend you consider using the Builder interface, because it should guarantee non-slip timing, you can always add code with code components, and you can study the code it generates to understand what’s happening.

But if you were to keep coding and wanted to make this work, here’s an approach using a loop. I’m assuming you want your stimuli to appear at 8 second intervals?

stimText = ["ProbeCity1", "ham", "more spam", "even more spam"]
positions = [
  (-10, 10), 
  (10, 10), 
  (-10,-10), 
  (10, -10)
]

stimuli = []

for i in range(len(stimText)): 
  stim = visual.TextStim(mywin, text=str(i)+": "+ stimText[i]) 
  stim.pos = positions[i]
  # this way we can access these stimuli later
  stimuli.append(stim)

  stim.draw()
  mywin.flip()
  core.wait(8.0)

Edit

Apparently @Michael and I were replying at the same time, oops! I agree with everything he said, of course!


#6

Hi both,

Thank you very much for your replies and guidance- they’ve been very helpful. I’ve managed to create a loop which has stimuli around the fixation cross. However, I’m stuck as to how to keep the stimuli there for around 4 seconds. Below is the code, in the proper format for easy viewing.

from psychopy import visual, core, event #import some libraries from PsychoPy
timer = core.Clock()

#draw a window
mywin = visual.Window([1920,1080], monitor="testMonitor", units="deg")


fixation = visual.GratingStim(win=mywin, size=0.5, pos=[0,0], sf=0, rgb=-1)
fixation.draw()
mywin.update()

#hardcoded positions
Target = visual.TextStim(mywin, text = "text default")
stimText = ["Berlin", "Paris", "London", "Nice", "Vienna", "Charming"]
positions = [
  (-10, 10), 
  (10, 10), 
  (-10,-10), 
  (10, -10),
  (-1, -10),
  (1, 10),
]

stimuli = []

for i in range(len(stimText)): 
    Target.setPos(positions[i])
    Target.setText(stimText[i])
    Target.draw()
    mywin.flip()
    core.wait(8.0)
    mywin.update()

Any help is much appreciated

Nathan


#7

You should nest some loops: one to cycle through the stimuli (or trials), and the other to draw those stimuli for the required amount of time on each trial. e.g.:

# first loop is at the trial/stimulus level:
# tip: you can use use an enumerator to simultaneously get an index and 
# the text value for each iteration:
for i , text in enumerate(stimText): 
    Target.setPos(positions[i])
    Target.setText(text)
        # the inner loop runs within each trial/stimulus:
        for frame in range(4 * 60): # e.g. 4 seconds at 60 Hz
            Target.draw()
            # also check for keypresses here, etc etc
            mywin.flip()

Avoid using core.wait(). By actively drawing to the screen on each refresh, you can also be doing things like checking the keyboard for responses, or doing anything else, rather than being locked out for 4 s.


#8

Hi Michael,

Thanks again for the help. I did try that code, but then I had the problem that the stimulus were appearing one at a time, rather than together, although the code was very useful.

I tried to combine a bit of the code you presented with my existing code, but I had the same problem as the stimuli appeared individually, rather than together. so,

from psychopy import visual, core, event #import some libraries from PsychoPy
timer = core.Clock()

#draw a window
mywin = visual.Window([1920,1080], monitor="testMonitor", units="deg")


fixation = visual.GratingStim(win=mywin, size=0.5, pos=[0,0], sf=0, rgb=-1)
fixation.draw()
mywin.update()

#hardcoded positions
Target = visual.TextStim(mywin, text = "text default")
stimText = ["Berlin", "Paris", "London", "Nice", "Vienna", "Charming"]
positions = [
  (-10, 10), 
  (10, 10), 
  (-10,-10), 
  (10, -10),
  (-1, -10),
  (1, 10),
]


stimuli = []

#On this loop, they appear but only briefly and can't work out how to keep them there
for i in range(len(stimText)): 
    Target.setPos(positions[i])
    Target.setText(stimText[i])
    Target.draw()
    mywin.flip()
    mywin.update()
    for frame in range(1 * 60): # e.g. 4 seconds at 60 Hz
            Target.draw()
            # also check for keypresses here, etc etc
            mywin.flip()

Any further thoughts on that…?

Nathan


#9

OK, that is a problem with long threads where it is easy to lose track of the overall goal.

I would suggest creating a list of six text stimuli, to avoid having to keep updating the attributes of a single stimulus, and that way they can be easily and rapidly drawn to the screen as required:

# create the six stimuli:
text_stim_list = [] # a list to hold them
stim_text = ["Berlin", "Paris", "London", "Nice", "Vienna", "Charming"] # their content

for i, text in stim_text:
    text_stim_list.append(visual.TextStim(mywin, text = text, pos = positions[i])

# draw them for 4 s:
for frame in range(4 * 60): # e.g. 4 seconds at 60 Hz

    # draw each stimulus to the off-screen buffer:
    for stimulus in text_stim_list:
        stimulus.draw()

    # send the buffer to the monitor for display
    win.flip()

Note that the indenting here is important.


#10

Hi Michael,

Thank you very much for your reply. I think I’m very close to figuring this out but I am getting the error message “SyntaxError: Invalid syntax” on the first line of the loop you suggested (which begins 'for frame in range…). Now, I suspect this is because I have not nested the loops correctly. Here’s the code:

from psychopy import visual, core, event #import some libraries from PsychoPy

#create a window
mywin = visual.Window([1920,1080], monitor="testMonitor", units="deg")
mywin.update()

# create the six stimuli:
Target = visual.TextStim(mywin, text = "text default")
text_stim_list = [] # a list to hold them
Stim_text = ["Berlin", "Paris", "London", "Nice", "Vienna", "Charming"] # their content

#the positions of the stimuli
positions = [
 (-10, 10),
 (10, 10),
 (-10, -10),
 (10, -10),
 (-1, -10),
 (1, 10),
 ]

#Initial message to participants about the study 
message1 = visual.TextStim(mywin, text = "You have been captured by the plotters. As a test to see if you have information regarding the event, an on screen test will be adminstered. Press Spacebar when ready")
message1.draw()
mywin.update()

#this will wait for a button press confirmation from p's to continue
response = event.waitKeys(keyList = ['space',], timeStamped = True)

#Initial message to participants about the study 
message1 = visual.TextStim(mywin, text = "On the next screen, you will be presented with a practice round of stimuli. Press Q if any of the cities are familiar, or press P if you do not recognise the cities")
message1.draw()
mywin.update()

#this will wait for a button press confirmation from p's to continue
response = event.waitKeys(keyList = ['space',], timeStamped = True)

#Loop for drawing the stimulus 
#On this loop, the stimuli appear but can't work out how to keep them in one position
for i in range(len(Stim_text)):
    Target.setPos(positions[i])
    Target.setText(Stim_text[i])
    Target.draw()
    mywin.flip()
    mywin.update()

for frameN in range(200):
    Target.draw()
    mywin.flip()






#Another attempt at a loop-this time nested loops-in this, stimuli appears for 4 secs but not simulatenously
for i, text in enumerate(Stim_text):
    Target.setPos(positions[i])
    Target.setText(text)

#the inner loop runs within each trial/stimulus
for frame in range(4 * 60):
    Target.draw()
    mywin.flip()

#Another Loop attempt

for i, text in stim_text:
    text_stim_list.append(visual.TextStim(mywin, text = text, pos = positions[i])

# draw them for 4 s:
for frame in range(4 * 60): # e.g. 4 seconds at 60 Hz

# draw each stimulus to the off-screen buffer:
    for stimulus in text_stim_list:
        stimulus.draw()

    # send the buffer to the monitor for display
    win.flip()

Nathan


#11

You need another closing bracket at the end of this line:

i.e.

text_stim_list.append(visual.TextStim(mywin, text = text, pos = positions[i]))