psychopy.org | Reference | Downloads | Github

Making a continue button from scratch online

I have created a “continue” button using the following code. It is supposed to be conditional, only appearing when the participant has answered multiple slider questions on the screen. However, it appears right away and is functional when I test it online. Does anyone know why this is?


Button code: using the “both” option instead of Auto->JS.

In begin experiment (Py side):

continueButton = visual.ShapeStim(win,  fillColor= [0.3, 0.3, 0.3], fillColorSpace='rgb', vertices=((-0.08, -0.05), (-0.08, 0.05), (0.08, 0.05), (0.08, -0.05)), closeShape=True, pos=(.70, -.40), name='continueButton')
continueButtonText = visual.TextStim(win, text='Continue', height = 0.03, pos=(.70, -.40))
continueButtonSelected = False

In begin experiment (JS side):

continueButton = new visual.ShapeStim({"win":psychoJS.window,"fillColor": [0.3, 0.3, 0.3], "fillColorSpace": "rgb", "vertices": [[(- 0.08), (- 0.05)], [(- 0.08), 0.05], [0.08, 0.05], [0.08, (- 0.05)]], "closeShape": true, "pos": [0.7, (- 0.4)], "name": "continueButton"});
continueButtonText = new visual.TextStim({"win":psychoJS.window, "text": "Continue", "height": 0.03, "pos": [0.7, (- 0.4)]});
continueButtonSelected = false;

In begin routine (Py side):

effortRate_resp = event.Mouse(win=win)
continueButton.setAutoDraw(False)
continueButtonText.setAutoDraw(False)
continueButtonSelected = False
nasaAnswered = False

In begin routine (JS side):

mouse = new core.Mouse({win: psychoJS.window});
continueButton.setAutoDraw(false);
continueButtonText.setAutoDraw(false);
continueButtonSelected = false;
nasaAnswered = false;

In each frame (Py side):

if effortRate_resp.isPressedIn(continueButton):
        continueButtonSelected = True
    if continueButtonSelected:
        continueRoutine = False

In each frame (JS side):

if ((continueButton.contains(mouse)) & (mouse.getPressed()[0] === 1)) {
    continueButtonSelected = true;
    if (continueButtonSelected) {
        continueRoutine = false;
    }
}
1 Like

You’ve created the object in Begin Experiment, which is good.

Use continueButton.setAutoDraw(True) in Each Frame to show it at the appropriate moment (make sure you use a flag so the command is only executed once)

continueButton.setAutoDraw(False) in End Routine to turn it off again.

Thanks @wakecarter! I actually had continueButton.setAutoDraw(True) in the code already, but I accidentally cut it out while trying to simplify for this post. I will play with the flags to see whether I can get it to work better. And edit the original post to include that.

Okay, so I have some “Each Frame” code that works on the Py side (here it is in more detail).

At the beginning of this routine, I set continueButtonOn and continueButtonSelected to False, as well as continueButton.setAutoDraw(False). The variable nasaAnswered checks if all the sliders have been answered; I want it to be True in order for the button to appear.

# enable button when form is complete
if nasaAnswered and not continueButtonOn:
    # draw continue button
    continueButton.setAutoDraw(True)
    continueButtonText.setAutoDraw(True)
    continueButtonOn = True

# check for click in button when it is on
if continueButtonOn and effortRate_resp.isPressedIn(continueButton) and not continueButtonSelected:
     continueButtonSelected = True

# if activated button is pressed, store info & proceed
if continueButtonSelected:
    # store NASA-tlx result
    <some code>
    
    # store NASA-tlx RT
    <some code>

    # get out of this routine
    continueRoutine = False

Here is the JS side. I tried to replicate the python side as closely as possible.

// enable button once when form is complete
if ((nasaAnswered) & (!continueButtonOn)) {
    continueButton.setAutoDraw(true);
    continueButtonText.setAutoDraw(true);
    continueButtonOn = true;
}

//check for click in button when it is on
if ((continueButtonOn) & (!continueButtonSelected) & (continueButton.contains(mouse)) & (mouse.getPressed()[0] === 1)) {
    continueButtonSelected = true;
}

//if activated button is pressed, store info & proceed
if (continueButtonSelected) {
    //store NASA-tlx result
    <some code>
    
    //store NASA-tlx RT
    <some code>
    
    //get out of this routine
    continueRoutine = false;
}

…but the button appears immediately when I hit that routine, before any sliders have been clicked, and is functional-- i.e. I can click it and go on, skipping the sliders. Any ideas?

… okay I think I figured it out. The problem is actually with how the slider checks were translating into JS. To check whether there was a rating yet in Python, I was using:

slider1.getRating() is not None

With Auto --> JS, this was getting translated to:

(slider1.getRating() !== null);

A few print statements later and I found out that an unanswered slider actually has a value of undefined… which is not null, so my code ended up spitting out “true” for that variable even without the user clicking anything.

So, the correct statement to check the slider status was:

(slider1.getRating() !== undefined);

Changing this has fixed the main problem. Here is the working “Each Frame” code for anyone reading this thread later (nasaAnswered is true once all the sliders are done):

// enable button once when form is complete
if ((nasaAnswered) & (!continueButtonOn)) {   
    continueButton.setAutoDraw(true);
    continueButtonText.setAutoDraw(true);
    continueButtonOn = true;
    console.log("continue button on");
}

//check for click in button when it is on
if ((continueButtonOn) & (!continueButtonSelected)) {
    if ((continueButton.contains(mouse)) & (mouse.getPressed()[0] === 1)) {
        continueButtonSelected = true;
        console.log("continue button selected");
    }
}

//if activated button is pressed, store info & proceed
if (continueButtonSelected) {
    //store NASA-tlx result
    nasaResult = [nasaMental.getRating(), nasaPhysical.getRating(), nasaTemporal.getRating(), nasaPerform.getRating(), nasaEffort.getRating(), nasaFrustration.getRating()];
    iblockLoop.addData("NASAtlx_scores", nasaResult);
    
    //store NASA-tlx RT
    nasaRTs = [nasaMental.getRT(), nasaPhysical.getRT(), nasaTemporal.getRT(), nasaPerform.getRT(), nasaEffort.getRT(), nasaFrustration.getRT()];
    iblockLoop.addData("NASAtlx_rts", nasaRTs);
    
    //get out of this routine
    continueRoutine = false;
}

I probably need to highlight the None / null / undefined thing a bit more strongly in my crib sheet. I’m not sure whether it applies generally or just to a few components

Ah, I see it in the crib sheet now. Thanks for keeping everything documented!