| Reference | Downloads | Github

Extract location of words after presenting sentence on screen

I am presenting sentences (10-15 words) on the screen while collection eye-tracking data.
I want to know the specific word participant looked at in any time.
How can I extract the location of each specific word after presenting the whole sentence on screen?
(I am using visual.TextStim, and draw())


PsychoPy can’t (currently) know about the location of each individual character or word in a TextStim, as that is handled at a lower level by another package.

You have a couple of options:
(1) Use a monospaced font: each character here takes up the same amount of space on screen, so location of words and characters can be calculated by you (relatively) simply.
(2) Save a screen shot of each relevant display using the .getMovieFrame() and .saveMovieFrames() functions of the window. This should allow you to overlay your gaze tracking data over the images as required and determine location visually and/or by defining AOI extents in your analysis software.

1 Like

Thank, Can you please elaborate regarding option #1?
How it is done or do you have some code example, for the usage of monospaced font and the exact location calculation?

OK, apologies for when I said relatively simple…

Here is the API for the text stimulus:

I haven’t done this before, but I guess you’ll need to access three properties of your TextStim:

  • .pos : where on the screen it is centred
  • .boundingBox : “bounding box of the text (w,h). This differs from width in that the width represents the width of the margins, which might differ from the width of the text within them. NOTE: currently always returns the size in pixels (this will change to return in stimulus units)”
  • .wrapWidth: “The width the text should run before wrapping”. You might not actually need to use this if you don’t set it and your text doesn’t overflow the size of the stimulus.

e.g. Let’s say you are displaying the string u'Hello'. The len() function tells you its number of characters (for a unicode text at least). If the bounding box gives a width of 200 pixels, and the text has 5 characters, then you know that each takes 40 pixels. If the stimulus is centred at [0, 0], then for a monospaced font, the first character starts at 100 pixels left of centre and extends to 60 pixels left of centre. The next spans from -60 to -20, and so on. Similar calculations can be done for the vertical extent, yielding the vertices of a square which contain your character. The same process can be done for entire words, if that is what you are after, by calculating the entire width of word based on its number of letters.

The wrapWidth complicates things, but not too much, in terms of working out if and when text flows to the next line.

Make sense?

Yes, sound good I will give it a try.
Just one more thing - Is there any way to whether my text is 1 or 2 lines? Or maybe just know number of pixels before psychopy chooses to go one line down, or a way to set pixels per line?

I think you need to control that by explicitly setting a value for the TextStim’s .wrapWidth.

@Michael setting width of the line using wrapWidth is working well.
However, setting pixel for each letter/word using BoundingBox has no effect of the display.
I am creating a text stim:

    text_stim = visual.TextStim(self.window, bold= True, text= sentence, pos = (0,0) , color='white', height=30)

Then trying to set bounding box, however even when I try different extreme it looks exactly the same:

    text_stim.boundingBox = (555,1)
    text_stim.boundingBox = (1, 300)

Do you know why?


You can’t set the bounding box: it is a read-only property. i.e. it tells you the width of the actually displayed text, which could easily be less than the wrap width.

@Michael I get the error ‘TextStim’ object has no attribure ‘boundingBox’ for this code.

text_stim = visual.TextStim(self.window, wrapWidth = wrapWidth, bold= True, text= sentence, pos = (0,0) , color=‘white’, height=30)

Also tried text_stim.boundingBox() and several other variations, and looked at the object itself - no such attribute.
I’m using 1.82.1 , however I couldn’t find any mention for adding this attributes in all of the release notes since (looked here
Any idea what could be the reason?

That version is nearly three years old. I’m not about to dig into the Git repository to find when the bounding box function was added but would simply suggest that you upgrade to the latest stable version to get the value from all of the subsequent features and bug fixes, and to be consistent with the online documentation of parameters like this.

Sorry I had a typo I meant I’m using 1.85.
This is the latest stable release - So boundingBox should be there right?
Here I found it exists since 1.83:
However it is not there…

>>> psychopy.__version__

text_stim = visual.TextStim(self.window, wrapWidth = wrapWidth, bold= True, text= sentence, pos = (0,0) , color='white', height=30)

a = text_stim.boundingBox
AttributeError: 'TextStim' object has no attribute 'boundingBox'

b = text_stim.boundingBox() 
AttributeError: 'TextStim' object has no attribute 'boundingBox'

@Michael Any thoughts?

This (i.e. without the () ) works for me under version 1.85.6.

@Michael Any idea why it is not working for me in this version?