5

I'm learning python and gpio using the examples here: https://gpiozero.readthedocs.io/en/stable/recipes.html

I wanted to extend the code to more than one button and pass a parameter to a function (for example the button number). The code works for both buttons when I don't pass a parameter, but doesn't when I pass one. I'm sure it's related to the note here, but I don't know how to get around that. Here's the code that doesn't work, in fact it prints Hello! 1 then Hello! 2 without any buttons being pressed then does nothing when either button is pressed, but prints Goodbye! when either button is released. How do I fix the code to call the function with a parameter when either button is pressed? Thank you.

from gpiozero import Button
from signal import pause

def say_hello(button_number):
    print("Hello!", button_number)

def say_goodbye():
    print("Goodbye!")

button1 = Button(2)
button2 = Button(3)

button1.when_pressed = say_hello(1)
button1.when_released = say_goodbye
button2.when_pressed = say_hello(2)
button2.when_released = say_goodbye

pause()

Code that works based on the answer below:

from gpiozero import Button
from signal import pause

def say_hello(button_number):
    if button_number == 1:
        return lambda: print("Hello, Button 1!")
    if button_number == 2:
        return lambda: print("Hello, Button 2!")

def say_goodbye():
    print("Goodbye!")

button1 = Button(2)
button2 = Button(3)

button1.when_pressed = say_hello(1)
button1.when_released = say_goodbye
button2.when_pressed = say_hello(2)
button2.when_released = say_goodbye

pause()

3 Answers 3

3

So when_pressed wants a reference to a function, maybe something like

def say_hello(x):
    if(x == 1):
        return lambda: print("Pressed 1")

... and so on for other cases.

2
  • 1
    Thanks, that works. I'll have to go learn about lambdas now and why that worked and mine didn't.
    – T. M.
    Commented Dec 10, 2018 at 1:20
  • For anyone else coming here: this answer works but it is not a very good way to write python :) Dirk's answer is much cleaner.
    – lbt
    Commented Nov 29, 2020 at 11:07
13

It is possible to use parameters with callback functions. See e.g. Documentation for button.when_pressed

This can be set to a function which accepts no (mandatory) parameters, or a Python function which accepts a single mandatory parameter (with as many optional parameters as you like). If the function accepts a single mandatory parameter, the device that activated will be passed as that parameter.

So the following code

from gpiozero import Button
from signal import pause

def say_hello(button):
    print(button.pin)

def say_goodbye():
    print("Goodbye!")

button1 = Button(20)
button2 = Button(21)

button1.when_pressed = say_hello
button1.when_released = say_goodbye
button2.when_pressed = say_hello
button2.when_released = say_goodbye

pause()

results in

GPIO20
Goodbye!
GPIO21
Goodbye!

(when pressing the appropriate buttons, of course)

So the button instance is always the first parameter. If you want to use additional parameters you can set the button instance manually:

from gpiozero import Button
from signal import pause

def say_hello(button, text = ""):
    print(text + str(button.pin.number))

def say_goodbye():
    print("Goodbye!")

button1 = Button(20)
button2 = Button(21)

button1.when_pressed = say_hello
button1.when_released = say_goodbye
button2.when_pressed = say_hello(button2, text = "Button 2 connected to ")
button2.when_released = say_goodbye

pause()

Pressing button 2 will result in

Button 2 connected to 21
3
  • Works as expected. You can even subclass the Button to pass any parameters for the callback using this technique
    – Woof
    Commented Feb 23, 2019 at 20:37
  • 1
    I don't see how it could work. On my side It returns the (expected) message CallbackSetToNone: The callback was set to None. This may have been unintentional e.g. btn.when_pressed = pressed() instead of btn.when_pressed = pressed Commented May 26, 2020 at 16:38
  • 1
    Great explanation. I missed such a clear example in the documentation. Thanks!
    – J0ANMM
    Commented Jun 5, 2020 at 11:04
1

You cannot pass a parameter to a callback function. You need separate functions for each callback.

3
  • Ok, more good things to learn, thanks. But in the working code above aren't I passing a parameter to the callback function?
    – T. M.
    Commented Dec 10, 2018 at 1:44
  • 1
    @T.M. Well NO. The lambda creates a new function which retains the calling scope. You are actually still creating separate functions. lambdas are an advanced topic, and not appropriate in a beginner program. By all means read about lambdas, but you need a lot of other knowledge tu understand them.
    – Milliways
    Commented Dec 10, 2018 at 2:44
  • 1
    You cannot pass a parameter to a callback function. That's not correct. See when.pressed doc
    – Dirk
    Commented Dec 10, 2018 at 11:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.