Change button color after multiple clicks

Hello,

I want to change the color of a button if the user presses on it to and from the original color.

Let’s say the original color is ‘darkgray’.
If the user presses on it once, it turns in ‘blue’.
If the user presses on it again, it turns from ‘blue’ to ‘darkgray’ again and so on and so forth.

I select colors in this format and not RGB (e.g. [0,0,0]) because I think it will be easier to convert to JS for the online one.

I wrote the below code so far, but it only works for the first click (turning the color from darkgray to blue, but not from blue back to darkgray again):

clickedN=0
for clickable in clickables:
    if mouseButton.isPressedIn(clickable):
        clickable.fillColor = 'blue'
        clickedN += 1
    elif mouseButton.isPressedIn(clickable) and clickable.fillColor == 'blue':
        clickable.fillColor == 'darkgray'
        clickedN += 1
        
if clickedN==1:
    continueRoutine == True

I also found the following resource here:

I hence tried adapting my code as such:

if mouseButton.getPressed()[0] == 0:
    fills = [polygonButtonYES.fillColor, polygonButtonNO.fillColor]

for clickable, index in enumerate([polygonButtonYES, polygonButtonNO])
    # If mouse pressed in square, match current square to square stored in fills list
    # Check if the colour has not changed. If it has not changed, change the colour of the square 
    # that contains the mouse click 
    if mouseButton.isPressedIn(clickable) and fills[index] == 'darkgrey':
       clickable.fillColor = 'blue'
    elif mouseButton.isPressedIn(clickable) and fills[index] == 'blue':
       clickable.fillColor = 'darkgrey'

But I receive this error:

Alert 4205:Python Syntax Error in 'Each Frame' tab. See 'for clickable, index in enumerate([polygonButtonYES, polygonButtonNO])
' on line number 7 of the 'Each Frame' tab.

Any ideas on how to solve this?
Can this be done using the buttonStim directly and not using “Polygon + Mouse” ?
Thanks!

Your loop condition is much more complicated than the original version. With just two buttons you could deal with them separately. What does index mean above? Do you have several of each button?

The index is used to indent the two polygons corresponding to each of the buttons.

So yes, the index should refer as in the original version to either of the two buttons.- in my case, it’s the YES and NO buttons.
I don’t see why the loop conditions is much more complicated - it’s basically the same, I just replaced some naming conventions, that’s all.

I will try to deal with each button separately.

Do you know if I could do this using a buttonStim instead of (polygon+mouse+text) ?

Thanks for the fast response!

Buttons don’t work online.

I’m not sure about two dimensional loops.

I’m not sure about enumerate. That might be fine

For me, buttons work online and can be synchronized with Pavlovia (I just tested and it works).

Do you know the condition for the button to be pressed?

I’m trying the following “IsPressedIn()” or “GetClicked()” , but they are not working:

For example:

if(buttonYES.getClicked() is not None):
    buttonYES.fillColor='blue'

if(buttonYES.fillColor=='blue'):
    buttonYES.fillColor='darkgrey'

I obtain the following error message (as expected)
AttributeError: 'ButtonStim' object has no attribute 'getClicked'

And I cannot find more information here or elsewhere:
https://psychopy.org/builder/components/button.html

Thanks,
Mihai

If buttons work in the latest version, then can’t you put the conditional code within the button if you keep it simple?

I tried doing that, but then it doesn’t switch back to darkgrey.

I have this:

After clicking once - it does turn blue, but it does not turn back afterwards.

I tried the following, but it does not work:

…as I assume that the condition to click the button is already there, but I don’t know it.

Hello,

I was wondering if you found an answer to your question, because I’m struggling with the same problem at the moment. Could you let me now if so?

Thanks!

Some issues occur because button.fillColor is a numpy array.
I found the following solution:

# Begin Routine:
mouseup = True # Prevents multiple clicks (see 1)

# Each frame:
if mouseup: # Checking whether the mouse is up or down
    if mouse.isPressedIn(button): 
        mouseup = False
        
        if list(button.fillColor) == [1, -1, -1]: # RGB Value of red
            button.fillColor = 'blue'
        else:
            button.fillColor = 'red'

else:
    if not 1 in mouse.getPressed():
        mouseup = True

[1]

Experiment example:
changeButtonColor.psyexp (12.2 KB)

Hi! Thank you very much for answering. I’m trying to use the code component within the buttons (8) to keep it simple, but I’m very new here. Do you have any idea how to write a conditional code that allows the button to light up within a loop? In this scenario, the button should get his original color every time the routine starts over again.

Hey, I don’t think I fully understand your intention.
Let’s say you have a red button. When the participants press the button, what should happen?
Should the button change its color, It is ending the routine? Do you want to toggle the color on each click? Do you want to change the button color once and return it to its original color at the start of the routine?
Please try to cover exactly what you are trying to do. I’ll gladly try to help you.
You can also open a new question regarding it.

Hi! Sorry for my unclear answer. I’ll try to explain what I’m trying to do.

In my experiment, participants hear an audio stimulus and have to press the button that corresponds to what they heard. There are 8 buttons (words). Then they have to indicate to what extent the word is well realised on a slider. The loop restarts as soon as the participant has done so. Then a new stimulus is presented.

What I would like to happen, is that there is some sort of feedback as soon as a participant presses a button, for example, that the button lights up.

So yes, the button should change its color, but it is not the ending of the routine, because afterwards participants use the slider. I would like to return the button to its original color at the start of the loop.

Thank you very much for your help!

Hey,
Okay, it’s much clearer now. I hope I understood you correctly.

You can create 8 button components.
After that, create a code component. At the start of the code, create a list containing all your buttons. This way, we can iterate over them and check each one.
We will then create a function that checks which button is being selected. We will change the fillColor of the selected button to one color and change the colors of all the other buttons, to their original color.
At the start of each routine, the buttons will reset to their original color. No code is necessary for the last part.

# Begin Experiment:
buttons = [button_1, button_2, button_3] # A list of the buttons (You will have 8 instead of 3)

def buttonSelector(selectedButton):
    for btn in buttons:
        if btn == selectedButton:
            btn.fillColor = "orange"
        else:
            btn.fillColor = "darkgray"

Then, we will trigger the function from each button this way:


button_2 will have the same callback function but with itself as the argument, and so on with all the other buttons.
It means that whenever we press one of the buttons, it calls the functions with the selected button as its argument.

If we get back to the code, what we essiently do is iterate through the list of buttons. We check each button. If the button is equal to the selected button, change its color to orange. Otherwise, change its color back to darkgray.

Example:
selectButton.psyexp (15.1 KB)

Hey,
This works perfectly in PsychoPy! Unfortunately, when I synchronized with Pavlovia, I received the message that the platform couldn’t find the function buttonSelector. Do you think of a solution, or is it just not possible to to transfer this function to Pavlovia?
Thanks, Hanna

I think that buttons are not working online, it might be outdated though.
Do you know anything about this issue? @wakecarter

The problem with buttons is that the callback function doesn’t get translated to JavaScript. However, I haven’t tested them recently.

1 Like

For me, the buttons do work online; I tried that beforehand, without the code components. Is there a way to go around the callback function? (Sorry for my evident questions. I’m very new here, as I said.) Thank you and have a great day!

For your information, I’ll just add the exact message I got on Pavlovia:
“Unfortunately we encountered the following error: ReferenceError: buttonSelector is not defined Try to run the experiment again. If the error persists, contact the experiment designer.”

Have a look at my crib sheet for how to define functions in JavaScript.

In the first instance try defining in Before Experiment instead of Begin Experiment.

1 Like

If the callback function doesn’t work, remove the callback functions from the buttons and try the following. No idea if it works online.

# Before Experiment:
def buttonSelector(selectedButton):
    for btn in buttons:
        if btn == selectedButton:
            btn.fillColor = "orange"
        else:
            btn.fillColor = "darkgray"
     
# Each Frame:
if len(mouse.clicked_name):
    buttons = [button_1, button_2, button_3]
    
    currentButton = mouse.clicked_name[-1]
    for btn in buttons:
        if currentButton == btn.name:
            currentButton = btn
        
    
    buttonSelector(currentButton)
1 Like