ButtonStim, how to set clickable back, next, exit button together

OS (e.g. Win10): Win10-64bit
PsychoPy version (e.g. 1.84.x): 2021.2.1
Standard Standalone? (y/n) If not then what?:y
**What are you trying to achieve?:

I am trying to make an online shopping page with “go back”, “continue”, and “exit” buttons at any stage of my experiment.
The “go back” button can allow participants to go back to any previous pages they want.
The “continue” button can let participants continue the experiment.
The “exit” button can allow participants to finish their current shopping trip and go to answer the question “why they want to quit?”

In my study, first I will show the instruction, then I will give participants the shopping scenario, next I will give them several items to choose from. After they click the “Continue” button, they will be brought to checkout page.

**What did you try to make it work?:

The “go back” button: I saw some answers using loops to implement the “go back” function. But it seems quite hard to implement this function if I need the button at every stage (routine).

The “continue” button: I assume the “force end of routine” option in the button component can make it work. But when I click the button, the whole experiment ends, instead of ending the current routine.

The “exit” button: I’ve already found some good answers to set an “exit” button to end up the whole experiment. But what I want to achieve here is the “exit” button can bring participants to a different routine so we can know why they give up the shopping trip.

I’m quite a newbie of Psychopy and I just have very limited knowledge of Python coding. I would pretty much appreciate it if any of you can help me with this.

Many thanks in advance!!

What do all of the pages look like? Are they just choosing from an array of items on one page or do they flip through pages of items like a catalogue? Is the “go back” button only on the checkout page? If not, where are they going back to? I have a continue/go back setup in my experiments, but I want to make sure I’m understanding what you’re trying to accomplish before I type all of that out.

Thank you so much for your fast reply!

In my experiment, the first page will be the description of the shopping scenario, the second page will be an array of items they need to choose from (all items are on one page, don’t need to flip), the third page will be the order summary (checkout page). I hope the three buttons including the “go back” button can be on every page so that they can check all of the info back and forth.

Hope it is clear for you:) Thanks again!

Okay got it. I’m going to be writing code in JavaScript here because it’s what I’ve been using in PsychoPy and I don’t want to look up the Python equivalents. If you’re running this on Pavlovia, you can just make a JS only code and copy/paste my code, or you can find the Python equivalents if you’re running it locally.

So you’re going to make a loop around the three routines (let’s call it shoppingLoop) and set it to loop 999 times. Then you’re going to have your three routines with everything you want in it (let’s call them scenario, items, checkout). At the top of each routine, make a code component:

scenario routine:
Begin Experiment:

loopPage = 0; //this will set it to start on page 1

Begin Routine:

//only do this routine if we're on this page
if (loopPage !== 0) { //if we're not on the first page
    continueRoutine = false; //don't run this routine
}

Each Frame:

if (mouse.isPressedIn(nextButton)) { //if they click on the next button component with the mouse component
    loopPage++; //increase the page number to 1
    continueRoutine = false; //end the routine
} else if (mouse.isPressedIn(exitButton)) { //if they click on the exit button component
    shoppingLoop.finished = true; //end the loop to go to your "why?" page outside of the loop
} //don't have a back button on page 1 because where would they go back to?

items routine:
Begin Routine:

//only do this routine if we're on this page
if (loopPage !== 1) { //if we're not on the second page
    continueRoutine = false; //don't run this routine
}

Each Frame:

if (mouse.isPressedIn(nextButton)) { //if they click on the next button component with the mouse component (the components will have different names now because they're in a new routine, so make sure to change them here too)
    loopPage++; //increase the page number to 2
    continueRoutine = false; //end the routine
} else if (mouse.isPressedIn(lastButton)) { //if they click on the go back button component
    loopPage = (loopPage - 1); //decrease the page number back to 0
    continueRoutine = false; //end the routine
} else if (mouse.isPressedIn(exitButton)) { //if they click on the exit button component
    shoppingLoop.finished = true; //end the loop to go to your "why?" page outside of the loop
}

checkout routine:
Begin Routine:

//only do this routine if we're on this page
if (loopPage !== 2) { //if we're not on the third page
    continueRoutine = false; //don't run this routine
}

Each Frame:

if (mouse.isPressedIn(nextButton)) { //if they click on the next button component with the mouse component (the components will have different names now because they're in a new routine, so make sure to change them here too)
    loopPage++; //increase the page number to 3
    continueRoutine = false; //end the routine
} else if (mouse.isPressedIn(lastButton)) { //if they click on the go back button component
    loopPage = (loopPage - 1); //decrease the page number back to 0
    continueRoutine = false; //end the routine
} else if (mouse.isPressedIn(exitButton)) { //if they click on the exit button component
    shoppingLoop.finished = true; //end the loop to go to your "why?" page outside of the loop
}

End Routine:

if (loopPage === 3) { //if you're out of pages, end the loop
    shoppingLoop.finished = true;
}

So what this does is loop through all of the routines in your loop every time they click any button, but it will skip the two routines that you aren’t currently on using the loopPage number. After they click ‘next’ on the final page, it will end the loop and go to your next routine outside of the loop, which should be your “why?” page. But you only want them to go to that page if they click exit, not if they finish the loop. So you’ll also want a code component in that routine and in the “Begin Routine” tab, write:

if (loopPage === 3) { //if they clicked 'next' on the final page
    continueRoutine = false; //skip the routine
}

I know that was a lot, but I hope that helps!

Just curious, would this same code work for an experiment with more than 3 routines? I tried implementing this for my demograpgic survey in which there are essentially pages for question sets and some scales, but I can’t seem to get it working.

I’m running on 2021.1.4 and made sure to translate from JS, but I keep getting an error that the mouse is not defined.

I’m fairly new to coding and psychopy so any tips for implementing this would be appreciated.

Have you created a mouse component called mouse in your first routine?

Yes, that actually occurred to me right after posting (of course), but now I’m getting a message that ‘false’ is not defined. When I click my next button, my experiment closes and I get that error

On my first routine I’ve placed the following code
Begin experiment
loopPage = 0

Begin Routine
if loopPage != 0:
continueRoutine = false

Every Frame
if mouse.isPressedIn(nextButton):
loopPage = loopPage + 1
continueRoutine = false

I’m not quite sure how I would define ‘false’ in this instance

False has a capital F in Python

Oh, I didn’t know that thank you!

Fixing that led to a new error:

for obj in range(nextButton):
TypeError: ‘ButtonStim’ object is not iterable.

I tried looking this error up and it seems to be common, but I’m not sure why it’s occurring here. Is this because my range in the function isn’t an integer by nature?

Is nextButton the name of one of your button components?

for obj in range(nextButton): doesn’t make sense to me. If nextButton is a list of button components, then I think you need for obj in nextButton:

Yes, nextButton is the name of my button component to advance to the next page so to speak. Unfortunately, I get the same type error even when changing it to for obj in nextButton:

To clarify, did you write for obj in range(nextButton): or did that just appear in the error message? If you wrote it, what did you want it to do?

Is nextButton an image or a ButtonStim? The error suggests it’s a ButtonStim but the code if mouse.isPressedIn(nextButton): suggests you are treating it like an image. I think the issue might be that you can’t use a mouse component to check for a click in a ButtonStim because the ButtonStim object already check for mouse clicks by itself.

I wrote obj in range(nextButton):. I was trying to implement a suggestion I saw on stack overflow and didn’t remove it before sending in that message.

nextButton is a ButtonStim. Is there a way to check for when the button is pressed? I tried looking up ButtonStim in the API but I coudn’t find it.

Off the top of my head I think you add code within the component. Have a look at Brookes template 2021 where I added code to count button clicks

1 Like