Stimulus randomization from different blocks with respecting duration time

OS: Win10
PsychoPy v: v3.0.0b11
Coder/Builder: Coder/Builder

Problem:
I have to make an experiment procedure where participants will estimate duration of stimulus. I have 3 kinds of stimulus (Positive, Negative and Neutral). There are 14 positive pictures, 14 negative and 14 neutral ones and it should be randomize (for example. 1- Positive, 2- Neutral, 3- Negative,4- Neutral, 5- Negative, 6- Positive). There is also requirement of duration. Duration has to be 400,600,800,1000,1200,1400 or 1600 ms (this is also need to be randomize). Main problem is to respect equality this two things. THere should be for example 2 positive stimulus 400 ms, 2 negative stimulus 400 ms, 2 neutral stimulus 400 ms, 2 positive stimulus 600 ms and so on…

Current state:
I have a procedure where positive, negative and neutral stimulus are in separate blocks. Duration works (thanks Michael again :slight_smile: ) bur the problem of this way is separation. There are no randomization of stimulus kind (positive, negative neutral) but only inside every kind. There is 14 positive, after this -14 negative and after - 14 neutral.

I try to marge it to one block with single routine. After this, I don’t know how to control duration between positive, negative and neutral stimulus. I thought it should be possible by nested loop. As you can see below, I tried to difference by first 3 letters of image name but there is an error:

Traceback (most recent call last):
File “C:\Users\kamil\OneDrive\Pulpit\Test\Test.py”, line 353
else thisTrial[0:3] == “Pos”:
^
SyntaxError: invalid syntax

Code:

# set up handler to look after randomisation of conditions etc

trials = data.TrialHandler(nReps=1, method=‘fullRandom’,
extraInfo=expInfo, originPath=-1,
trialList=data.importConditions(‘Stim.xls’),
seed=None, name=‘trials’)
thisExp.addLoop(trials) # add the loop to the experiment
thisTrial = trials.trialList[0] # so we can initialise stimuli with some values

abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)

if thisTrial != None:
for paramName in thisTrial:
exec(’{} = thisTrial[paramName]’.format(paramName))

for thisTrial in trials:
currentLoop = trials
# abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
if thisTrial != None:
for paramName in thisTrial:
exec(’{} = thisTrial[paramName]’.format(paramName))

# ------Prepare to start Routine "Stimulus"-------
t = 0
StimulusClock.reset()  # clock
frameN = -1
continueRoutine = True
routineTimer.add(3.000000)
# update component parameters for each repeat
image.setColor([1,1,1], colorSpace='rgb')
image.setImage(Neutral)
# keep track of which components have finished
StimulusComponents = [image, FPoint]
for thisComponent in StimulusComponents:
    if hasattr(thisComponent, 'status'):
        thisComponent.status = NOT_STARTED

# -------Start Routine "Stimulus"-------
while continueRoutine and routineTimer.getTime() > 0:
    # get current time
    t = StimulusClock.getTime()
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    # *image* updates
    if thisTrial[0:3] == "Neu":
        timeNeu = durationsNeu.pop
        if t >= 1 and image.status == NOT_STARTED:
        # keep track of start time/frame for later
            image.tStart = t
            image.frameNStart = frameN  # exact frame index
            image.setAutoDraw(True)
        frameRemains = 1 + timeNeu- win.monitorFramePeriod * 0.75  # most of one frame period left
        if image.status == STARTED and t >= frameRemains:
            image.setAutoDraw(False)
    elif thisTrial[0:3] == "Neg":
        timeNeg = durationsNeg.pop
        if t >= 1 and image.status == NOT_STARTED:
        # keep track of start time/frame for later
            image.tStart = t
            image.frameNStart = frameN  # exact frame index
            image.setAutoDraw(True)
        frameRemains = 1 + timeNeg- win.monitorFramePeriod * 0.75  # most of one frame period left
        if image.status == STARTED and t >= frameRemains:
            image.setAutoDraw(False)
    else thisTrial[0:3] == "Pos":
        timePos = durationsPos.pop
        if t >= 1 and image.status == NOT_STARTED:
        # keep track of start time/frame for later
            image.tStart = t
            image.frameNStart = frameN  # exact frame index
            image.setAutoDraw(True)
        frameRemains = 1 + timePos- win.monitorFramePeriod * 0.75  # most of one frame period left
        if image.status == STARTED and t >= frameRemains:
            image.setAutoDraw(False)

Hi @Kamil12, you have a syntax error becuase of the way you used your conditional statement. Try:

elif thisTrial[0:3] == “Pos”:  # note elif vs else - see 

See Python docs for more info on if statements.

Thanks :slight_smile:
Now I have this :

Traceback (most recent call last):
File “C:\Users\kamil\OneDrive\Pulpit\Test\Test.py”, line 333, in
if thisTrial[0:3] == “Neu”:
TypeError: unhashable type: ‘slice’

But as I understand there is a good direction to solve this problem?

The variable thisTrial is an orderedDict and cannot be sliced. You should have a key, value pair that holds your condition values, where key is your variable name and value is the value. So try:

thisTrial['condition'][0:3]=='Neu':  # Where condition is your variable name defining whether a trial is Neutral etc

I understand. Almost everything works. The last problem (I hope so) is :
time1 = durationsNeu.pop()
IndexError: pop from empty list

I understand what it means, but in different version of procedure it works…
At the beginning of code there are lists:

Ensure that relative paths start from the same directory as this script

_thisDir = os.path.dirname(os.path.abspath(file))
os.chdir(_thisDir)

durationsNeu = [24, 36, 48, 60, 72, 84, 96] * 5
shuffle(durationsNeu)
durationsNeg = [24, 36, 48, 60, 72, 84, 96] * 5
shuffle(durationsNeg)
durationsPos = [24, 36, 48, 60, 72, 84, 96] * 5
shuffle(durationsPos)

and in the middle of code:

# -------Start Routine "Stimulus"-------
while continueRoutine and routineTimer.getTime() > 0:
    # get current time
    t = StimulusClock.getTime()
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    # *image* updates
    if thisTrial['Neutral'][0:3] == "Neu":
        time1= durationsNeu.pop()
        if t >= 1 and image.status == NOT_STARTED:
        # keep track of start time/frame for later
            image.tStart = t
            image.frameNStart = frameN  # exact frame index
            image.setAutoDraw(True)
    if image.status == STARTED and frameN >= time1:
        image.setAutoDraw(False)

But when I change it like in code below (time1 under if statement) first two samples are invisible (without picture) and third sample give an error

if image.status == STARTED and frameN >= time3:
NameError: name ‘time3’ is not defined

Code:

    # *image* updates
    if thisTrial['Neutral'][0:3] == "Neu":
        if t >= 1 and image.status == NOT_STARTED:
            time1= durationsNeu.pop()
        # keep track of start time/frame for later
            image.tStart = t
            image.frameNStart = frameN  # exact frame index
            image.setAutoDraw(True)
    if image.status == STARTED and frameN >= time1:
        image.setAutoDraw(False)