Multiple conditional statements work correctly in PsychoPy but not PsychoJS

Description of the problem:

An experiment I’m building involves rating stimuli in a loop along two dimensions of emotion (valence and arousal) using two sliders appearing side by side. I want participants to press ‘enter’ after they input their responses along both sliders to complete a trial. The following condition statement works in Psychopy for a keyboard response component to become active:

Start condition: $valenceRatingScale.rating and arousalRatingScale.rating

However, the Pavlovia experiment page won’t load the experiment with this code. Is there an alternative way to do this? I’m not very familiar with Python or Javascript so I am pasting the routine’s code below.

This start condition for the keyboard component throws the following two error messages:

ERROR Line 399: Unexpected identifier in newExperiment.js
ERROR Line 392: Unexpected identifier in newExperiment-legacy-browsers.js

Many thanks in advance!

# -------Run Routine "test"-------
while continueRoutine:
    # get current time
    t = testClock.getTime()
    tThisFlip = win.getFutureFlipTime(clock=testClock)
    tThisFlipGlobal = win.getFutureFlipTime(clock=None)
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    # *valenceRatingScale* updates
    if valenceRatingScale.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
        # keep track of start time/frame for later
        valenceRatingScale.frameNStart = frameN  # exact frame index
        valenceRatingScale.tStart = t  # local t and not account for scr refresh
        valenceRatingScale.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(valenceRatingScale, 'tStartRefresh')  # time at next scr refresh
        valenceRatingScale.setAutoDraw(True)
    
    # *arousalRatingScale* updates
    if arousalRatingScale.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
        # keep track of start time/frame for later
        arousalRatingScale.frameNStart = frameN  # exact frame index
        arousalRatingScale.tStart = t  # local t and not account for scr refresh
        arousalRatingScale.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(arousalRatingScale, 'tStartRefresh')  # time at next scr refresh
        arousalRatingScale.setAutoDraw(True)
    
    # *continuneText* updates
    if continuneText.status == NOT_STARTED and valenceRatingScale.rating and arousalRatingScale.rating:
        # keep track of start time/frame for later
        continuneText.frameNStart = frameN  # exact frame index
        continuneText.tStart = t  # local t and not account for scr refresh
        continuneText.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(continuneText, 'tStartRefresh')  # time at next scr refresh
        continuneText.setAutoDraw(True)
    
    # *key_resp_10* updates
    waitOnFlip = False
    if key_resp_10.status == NOT_STARTED and valenceRatingScale.rating and arousalRatingScale.rating:
        # keep track of start time/frame for later
        key_resp_10.frameNStart = frameN  # exact frame index
        key_resp_10.tStart = t  # local t and not account for scr refresh
        key_resp_10.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(key_resp_10, 'tStartRefresh')  # time at next scr refresh
        key_resp_10.status = STARTED
        # keyboard checking is just starting
        waitOnFlip = True
        win.callOnFlip(key_resp_10.clock.reset)  # t=0 on next screen flip
        win.callOnFlip(key_resp_10.clearEvents, eventType='keyboard')  # clear events on next screen flip
    if key_resp_10.status == STARTED and not waitOnFlip:
        theseKeys = key_resp_10.getKeys(keyList=['return'], waitRelease=False)
        _key_resp_10_allKeys.extend(theseKeys)
        if len(_key_resp_10_allKeys):
            key_resp_10.keys = _key_resp_10_allKeys[-1].name  # just the last key pressed
            key_resp_10.rt = _key_resp_10_allKeys[-1].rt
            # a response ends the routine
            continueRoutine = False
    
    # check for quit (typically the Esc key)
    if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]):
        core.quit()
    
    # check if all components have finished
    if not continueRoutine:  # a component has requested a forced-end of Routine
        break
    continueRoutine = False  # will revert to True if at least one component still running
    for thisComponent in testComponents:
        if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
            continueRoutine = True
            break  # at least one component has not yet finished
    
    # refresh the screen
    if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
        win.flip()

# -------Ending Routine "test"-------
for thisComponent in testComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
thisExp.addData('valenceRatingScale.response', valenceRatingScale.getRating())
thisExp.addData('valenceRatingScale.rt', valenceRatingScale.getRT())
thisExp.addData('valenceRatingScale.started', valenceRatingScale.tStartRefresh)
thisExp.addData('valenceRatingScale.stopped', valenceRatingScale.tStopRefresh)
thisExp.addData('arousalRatingScale.response', arousalRatingScale.getRating())
thisExp.addData('arousalRatingScale.rt', arousalRatingScale.getRT())
thisExp.addData('arousalRatingScale.started', arousalRatingScale.tStartRefresh)
thisExp.addData('arousalRatingScale.stopped', arousalRatingScale.tStopRefresh)
thisExp.addData('continuneText.started', continuneText.tStartRefresh)
thisExp.addData('continuneText.stopped', continuneText.tStopRefresh)
# check responses

Update: The error only occurs when involving multiple conditions.
The following works as a start condition:

$arousalRatingScale.rating

However, this doesn’t work:

$arousalRatingScale.rating and valenceRatingScale.rating

EDIT: I’ve found a work-around that does the trick for me.

Hi, would you mind sharing the fix you found here so that others might benefit from it?

Hi Bobby,

Thanks for the reminder! My work-around does not solve the problem regarding the multiple conditions.

Instead, I modified the start conditions of the components, making the second scale (arousalRatingScale) appear only after participants rate the first scale, making its start condition valenceRatingScale.rating.

I then used a polygon component to serve as a confirmation box and set its start condition to arousalRatingScale.rating .

Finally, I used a mouse component and set the confirmation box component as a clickable stimulus so that the routine ends after participants have rated both scales.

Interesting fix. In the meantime I looked into the keyboard component more (I’ve only ever used time to enable it).
I’m not positive why $arousalRatingScale.rating and valenceRatingScale.rating is working in PsychoPy but not PsychoJS, but I think it is because in JS and is not a valid logical operator, it uses &&. Given this difference, I don’t think you can put a single statement in the start condition for the keyboard component that involves an and statement and have it work both locally and online.
But I think an easy way around this issue would be to create a custom code component, and in the start routine tab have EnableKeyboard= False
Then in the each frame tab you would define this locally (in Python) as
EnableKeyboard= valenceRatingScale.rating and arousalRatingScale.rating
Given the newest update to PsychoPy it should translate this to JS for you, but I think the manual translation would be
EnableKeyboard=valenceRatingScale.rating && arousalRatingScale.rating

Then in the keyboard component you make your new start condition just $EnableKeyboard, and if you have built the code component correctly, this should work locally and online.

I understand if you don’t want to go back and try this solution since you have it working, but I figured I would put it here in case anyone else comes looking for help.

1 Like

Thank you very much for the suggestion! I’ll definitely try it out as I think it’s better than mine.