Forum Archive

Button action in a class

donnieh

I have a ui.button created in a class function. The button action is pointing to a different function in the same class.

The action will not call when the button is pushed. Should this work? ( This is simplified logic of the actual code)

class Note:

    def f1(self):
         print 'hello'

    def f2(self):
         b = ui.button()
         b. action = f1

c = Note()
c.f2()
dgelessus

In the line b.action = f1 you're trying to set the global f1 as the action of b - however there is no such global. To use the method f1 of self (an instance of Note) as the action, you need to explicitly refer to it as self.f1. (Unlike e. g. in Java, where f1 inside a class translates to this.f1.)

action functions also need to take a single parameter, sender, which is the UI object that calls the function. f1 currently doesn't have that, meaning that you'll get an exception when the Button tries to run its action.

ccc

Unfortunately, an exception is NOT thrown but instead the action just fails to get called silently :-(

A minimal working version:

import ui
class Note(ui.View):
    def f1(self, sender):  # adding the sender is key
         print('hello ' + sender.title)
    def f2(self):
         b = ui.Button(title='OK')
         b.action = self.f1
         self.add_subview(b)
c = Note()
c.present()
c.f2()
donnieh

Yes, the silent exception is what confused me. I think all your help explained it well. Thank you.

What if with the example above I wanted to call f1 independently (which now contains the sender argument)? When I try the code below it has an error:

c.f1() #this cause argument error

c.f1(0) # this works but why?

ccc

c.f1(0) does NOT work but instead it throws an AttributeError because an int does not have a .title attribute.

My recommendation would be that you write:

def f1(self, sender=None):  # added a default value for sender
    if sender:
        print('hello ' + sender.title)  # called as an action
    else:
        print('hello, world!')          # called as a method

# [ ... ]

c.f1()  # called as a method with no "sender" parameter
donnieh

Excellent, good info. Thank you.

dgelessus

When using None as a default value, it's best to check for None itself (if arg is None), instead of relying on its falseyness (if not arg). In this case it's unlikely that sender will ever be something that is falsey, but in other cases this can be problematic. For example 0 is falsey, and when writing a function that deals with numbers you don't want 0 to be considered as "no argument given".