Set_event_handler not calling the defined method

Hi everyone

I am trying to add a textbox to my form via code and define what happens when the user presses enter on the textbox.
Once the user presses enter a method called FrontLength is meant to run and check if the inserted value is valid according to pre-defined limits on the server. If the number is outside the limits a warning popup should appear

It seems that the number the user inserts is updated every time enter is pressed but the FronLength method only runs once when the form uploads, where it should run every time the user presses enter

What am I doing wrong?

Here is the app:
https://anvil.works/build#clone:CC7UWMLM7YUPSM7E=FOEYRRXVMTVHLJZGZVDA6OWR

Here is the code:

from ._anvil_designer import TestingAddingTextTemplate
from anvil import *
import anvil.server
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import TestModule as MD
  
class TestingAddingText(TestingAddingTextTemplate):
    def __init__(self, **properties):
        # Set Form properties and Data Bindings.
        self.init_components(**properties)
          
        # Any code you write here will run before the form opens.
        cp  = self.column_panel_1
        if MD.MainPackage['Components']['Front']:
        self.FrontLengthTextBox = TextBox(type='number', align='right')
        fp_Front = FlowPanel(align='right')     
        fp_Front.add_component(Spacer(height=32))
        fp_Front.add_component(self.FrontLengthTextBox)
        fp_Front.add_component(Label(align = 'right', text='Insert length in cm'))
        self.FrontLengthTextBox.set_event_handler("pressed_enter", self.FrontLength())
        
        cp.add_component(fp_Front)   
    
    def FrontLength(self, **event_args):
        """This method is called when the user presses Enter in this text box"""
        LengthOK = anvil.server.call('LengthAllowanceCalculation', MD.MainPackage['GeneralProjectData']['KitchenShape'], self.FrontLengthTextBox.text, 'Front', MD.MainPackage['GeneralProjectData']['BarMatrix'][0])
        if not LengthOK[0]:
        t = TextBox(placeholder=f"Insert value bigger than {LengthOK[1]} cm", align = 'Right')
        result = alert(content=t, title= "Lenght is too short")
        pass
      
    def button_1_click(self, **event_args):
        """This method is called when the button is clicked"""
        print('Button clicked, Length value is:', self.FrontLengthTextBox.text)
        pass
        ```

Don’t use the parenthesis when you set the event handler. e.g. self.FrontLength, not self.FrontLength()

1 Like

Thanks @jshaffstall it worked!
Do you know what the difference is (I would assume an error / warning if the parenthesis is an issue but the code didn’t indicate a problem)?

The issue is the type of data Python expects as that parameter to set_event_handler. It wants a function. In Python, a function is a data type like any other. You get a function data type by not using the parentheses.

If you use the parentheses, you are then calling the function, and the value returned from that function is used instead. If your function doesn’t return anything, then None is returned.

So you were passing a None into set_event_handler, and it probably has error checking code to ignore None values. It’d be nice if it printed out a warning of some sort, suggesting that you probably used parentheses when you shouldn’t, but it doesn’t currently do that.

Ok
So how can I set the same function for 2 different buttons to run once enter is pressed but with a difference inside the function determined by a parameter?

This is how I would expect to define the handles:
self.FrontLengthTextBox.set_event_handler(“pressed_enter”, self.FrontLength(self, ‘Right’))
self.LeftLengthTextBox.set_event_handler(“pressed_enter”, self.FrontLength(self, ‘Left’))

But this obviously doesn’t work based on your explanation

Python provides the partial function to create those sorts of functions. Partial Functions in Python - GeeksforGeeks

Normally partials are used to create functions that fill in some of the parameters, while the rest will get filled in when the partial function is actually called. But you can use them to create functions that fill in all the parameters, too.

You’d create a partial function for each call, something like:

front_length_right = partial(self.FrontLength, 'Right')
front_length_left = partial(self.FrontLength, 'Left')

And then use those partial functions to set the event handler, e.g.:

self.FrontLengthTextBox.set_event_handler(“pressed_enter”, front_length_right )
self.LeftLengthTextBox.set_event_handler(“pressed_enter”, front_length_left )

But, you can also do the same thing without partial functions by defining your own specific callback functions that will call FrontLength with the appropriate parameter.

2 Likes

Thanks again @jshaffstall your proposed solutions are always spot on!

2 Likes