Word by word presentation with varying lengths

Hi all,

OS (e.g. Win10): Win8
PsychoPy version (e.g. 1.84.x): 1.84.2
Standard Standalone? (y/n) If not then what?: y

I am building an experiment where participants read sentences on a word-by-word basis. I want each word to display for 0.5 seconds plus an additional 0.17 seconds per letter in the word.

The code I have is as follows (found in another helpful discussion):

In code component ‘Begin Routine’:

words = sentence.split()
numWords = len(words)
totalDuration = numWords * 0.5
currentWordIndex = -1

In code component ‘Each frame’:

checkIndex = int(t/0.5)
if checkIndex < numWords:
   if checkIndex != currentWordIndex:
      currentWordIndex = checkIndex
      text.setText(words[currentWordIndex]) # update to the current word

I am aware that the above has each word presented for 0.5 seconds only.

Any help would be much appreciated!

Something simple like this might work

#each frame
for thisWord in words:
     timer = core.CountdownTimer(0.5 + ((len(thisWord.split()) * 0.17))
     while timer.getTime() >= 0:
          text.setText(thisWord)

Thanks, Oli – this seems to be on the right track!

Any idea why only the last word from each sentence might be displaying? So, if I have two sentences as follows:

(a) marshmallows and cats are nice
(b) tokyo and london I like,

Only “nice” and “like” show up on the screen.

Another question: what should the duration of the text component be set to?

The code, again, is as follows:


#begin routine
words = sentence.split()
#each frame
for thisWord in words:
     timer = core.CountdownTimer(0.5 + ((len(thisWord.split())) * 0.17))
     while timer.getTime() >= 0:
          text.setText(thisWord)

Thanks,
R

This is because although @Oli’s suggestion would be fine if you were writing the experiment from scratch in your own code, when inserted into a Builder experiment, it breaks Builder’s screen refresh cycle.

i.e. any code that runs in the Each frame tab in Builder has to be completed within one screen refresh. The suggested code is supposed to run over many seconds. It will indeed do that, but also effectively pause Builder’s drawing cycle while it does so. Builder will only start refreshing the screen once the code is complete, hence showing only the last word.

What you should do is try to amend the code in your first post to account for your extra constraint of one screen refresh per letter.

Hi Michael,

Thanks, your response makes sense. I’ve been trying for the last few hours to implement your suggestion, but haven’t managed yet.

What I thought about was something like the following:

for thisWord in words: #take a word from the sentence
	letters = thisWord.split() #split the word into letters
	for letter in letters:
		text.setText(thisWord)	#for each letter in the word: display the word

But in the ‘each frame’ tab, this still aims to run over multiple frames.

Could you help? Apologies for my multiple questions - I’m trying my best to learn.

Yep - when I actually got round to testing it that was what I found :confounded:

#begin routine
words = sentence.split()
timer = core.CountdownTimer(0.0)
i = 0

#each frame:
if i <= len(words)-1:
    if timer.getTime() <=0:
        timer = core.CountdownTimer(0.5 + ((len(words[i].split())) * 0.17))
        text.setText(words[i])
        i+=1
else:
    continueRoutine = False

That’ll do it

Oli

Hi @Oli,

I could see the problem but was too slow/lazy to actually propose a useful solution :wink:

I would suggest, though that counting frames rather than using timers to do this (assuming @Rob’s screen is running at 60 Hz). i.e. 0.17 is far enough away from 1/60 s that timing errors will soon start accumulating…

Builder keeps a counter of the current frame number in the trial (frameN) and I guess Rob would need to count 30 frames (i.e. 0.5 s) + the number of letters in the word. And possibly add the total number of frames up to that point (from the preceding words)?

Thanks, Oli and Michael!

I do think it’s better to work with frames – my attempts at modifying Oli’s code have so far produced the following:


#begin routine
if i <= len(words)-1:
   wordFrames = (30 + (len(words[i].split())))
   for frameN in range(wordFrames):
       text.setText(words[i])
   i+=1
else:
   continueRoutine=False

This seems to cycle through all the words at lightning speed.

#begin routine
words = sentence.split()
i = 0
frameCounter = 0 #it all happens within the same routine so make our own counter

#each frame
if i <= len(words)-1: #-1 to account for the list index starting at 0
    text.setText(words[i]) #set the word
    frameCount = 30 + (len(words[i].split()) * int(0.17*60)) #int because there is a leftover so the == below doesn't work. Change frameCounter set and reset to 0.2 instead of 0 if this is an issue.
    frameCounter+=1 #our own frame counter
    if frameCounter == frameCount:
        i+=1
        frameCounter = 0
else:
    continueRoutine = False

This should do it

3 Likes

Oli, this works perfectly - thank you so much to you and Michael for taking the time to help me.

All the best to you both.

Great news - please can you mark the relevant post as a solution?

Cheers,

Oli

Hi there. @ Rob did you manage to run this experiment in Pavlovia? I’m using a similar code to split sentences but if gives me an error for ((len(thisWord.split()) this code…did you get that too?

Hi Elise, I’m afraid I’ve never tried Pavlovia, so I can’t help you there. Good luck!

1 Like

Hi there,

I used your code and adjusted it to design an RSVP sentence comprehension experiment. Thank you for the beautiful code! :slight_smile:

However, when I try to run it on Pavlovia, I get an error saying “cannot read property map of undefined”. Do you happen to know what’s causing this error? Here’s my code:

Begin routine:

words = practice_item.split()
i = 0
frameCounter = 0

Each Frame:

if i <= len(words)-1 and words[i] == “+”: #-1 to account for the list index starting at 0
Practice_Text.setText(words[i]) #set the word
Practice_Text.color=“black”
frameCount = 31
frameCounter+=1 #our own frame counter
if frameCounter == frameCount:
i+=1
frameCounter = 0

elif i <= len(words)-1 and words[i] != “blank”: #-1 to account for the list index starting at 0
Practice_Text.setText(words[i]) #set the word
Practice_Text.color=“black”
frameCount = 20
frameCounter+=1 #our own frame counter
if frameCounter == frameCount:
i+=1
frameCounter = 0

elif i <= len(words)-1 and words[i] == “blank”: #-1 to account for the list index starting at 0
Practice_Text.setText(words[i]) #set the word
Practice_Text.color=“white”
frameCount = 13
frameCounter+=1 #our own frame counter
if frameCounter == frameCount:
i+=1
frameCounter = 0

else:
continueRoutine = False

Many thanks in advance!

Hossein