Help with event handlers and buttons

What I’m trying to do:
I’ve got a 5x5 grid of buttons. I’d like to create an event handler that can dynamically check the name of the button clicked, and make changes to the appearance of the button that called the event.

I know that I could just add an event for each button from the properties menu, but it seems like there’s a better way.

What I’ve tried and what’s not working:

Code Sample:

# this is a formatted code snippet.
def a1_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    pass

  def a2_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    pass

  def a3_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    pass

  def a4_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    pass

  def a5_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    pass
# paste your code between ``` 

Clone link:
share a copy of your app

Welcome to the Forum!

Yes, there is a shorter way, using one event-handler shared by all the buttons. Hint: take a closer look at event_args['sender'] in Events.

So when I print event_args[‘sender’] it just shows <anvil.Button object> no matter which button I press. I’m still not sure how to get the name of that sender

Unfortunately it is impossible to get the component name from code.

You can access other properties like:

button = event_args['sender']
print(button.text)
print(button.tag)

If text doesn’t work for you, then you should use tag.

2 Likes

For that, @jhdrake, you don’t need the button’s name. You just need a variable that refers to the actual button object, exactly as @stefano.menci showed you. In his code, local variable button contains that reference. You can not only read the button’s properties, as Stefano showed, but you can also write to them:

button = event_args['sender']
button.background = 'gold'

And if you can’t determine which button is which based on a single or group of properties with enough certainty, you can create a dictionary with each button as a key and each value be a dictionary of properties you want to change, like so in the init function:

self.buttons = {self.button_1: {'background': "black", "text": "one click"}, 
self.button_2: {"foreground": "blue", "space_above": "large"}}

Then in the function you want to assign to all the buttons you can do thus:

clicked_button_props = self.buttons[event_args['sender']]
for prop, val in clicked_button_props.items():
    setattr(event_args['sender'], prop, val)

If you want something better than defining events in code, you can also consider Repeating Panel Anvil Docs | RepeatingPanels

1 Like

Thank you all for the replies! I’ll give it a shot!

Ok I’m able to successfully manipulate the properties of the button that calls the event.
The next step of my game involves “highlighting” the buttons around the one I just clicked. So I’m still running into the problem of needing some type of identifier or label for the buttons that can be referenced by itself and other buttons.

Below, I’ve tried to create a function to assign tags to all 25 buttons, but I’m consistently getting a NameError. One of the buttons is called a1_button. And it’s tag after the tag function should be a1_button.tag = ‘a1’.

Any advice?

from ._anvil_designer import HomeTemplate
from anvil import *

ranks = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’]
columns = [1, 2, 3, 4, 5]
spaces = []
for rank in ranks:
for column in columns:
spaces.append(str(rank)+str(column))

class Home(HomeTemplate):
def init(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)

# Any code you write here will run when the form opens.
def tagButtons():
  for space in spaces:
    button_label = exec(space + '_button')
    button_label.tag = space
tagButtons()
print(a1_button.tag)

There is likely no local or global variable named a1_button. However, from your description, it sounds like your Form has an attribute with that name, e.g., self.a1_button.

This is where a short trip to the Python standard library comes in handy. See the getattr() function.

However, you can also make a list of buttons without having to systematically assign them names. For example:

self.button_row = [ self.a1_button, self.a2_button, self.a3_button, ... ]
for button in self.button_row:
    ...

With this method, you could conceivably tag each button with its own list of its (relevant) neighbors…

Lots of possibilities here!

The visible behavior you mentioned sounds a bit like Minesweeper…?

1 Like

Ah! I think that’s where I was having the disconnect. I wasn’t understanding that buttons were an attribute of the form!

and the getattr() tip helped with the rest! Thank you!

1 Like