I have a couple of questions about setting event handlers to form components in code.
The first question is when to add an event handler - before or after adding component to the form. An example:
l = Link(text="Some link")
self.add_component(l)
l.set_event_handler("click", self.handle_my_event)
or:
l = Link(text="Some link")
l.set_event_handler("click", self.handle_my_event)
self.add_component(l)
The second question is about changing the event handler for another - are there any limitations?
Iām asking this because I try to build dynamic menus and submenus. For each menu and submenu I create lists of Link() components, and when I have to change menu options (i. e. user chooses a submenu), I remove_from_parent() each of the Link() components and with add_component() Iām adding new components to the sidebar.
Firstly, main menu is shown and everything works great. When I go to submenu, still works. When I go back to main menu, click event doesnāt fire anymore. The menu rebuild goes like this:
def displayMenu(self, menuName): #function, takes name of menu to show
if len(self.displayedMenu) > 0: #if some menu is displayed, remove links
for choice in self.menus[self.displayedMenu]: #each choice (link) gets removed
choice.remove_from_parent()
for lnk in self.menus[menuName]: #for all links in a menu menuName
lnk.set_event_handler("click", self.evhndl) #... set event handler
self.add_component(lnk, slot='sidebar') #... and add link to the sidebar
self.menus is a dictionary where keys are menu names and values are lists of Links.
Maybe I should be changing the link texts instead of the Links, but if you have objects, then you use them 
I can answer the first part - it doesnāt matter which order you do that in, as long as you do it within the scope of the variable you assign to the link (in your case, l
). Iāve just tested that.
For the second part - why are you changing the event handlers for the links? Wouldnāt a link always go to the same handler, even if that handler dynamically altered what it did based on other data (such as the link object passed to it)? Just trying to understand ā¦
Thanks for nr. 1!
Nr. 2 - I made the code thinking that the component should already be on the form, so if I remove it, the event handler has to be āreestablishedā. Iāll try to change this, since we know the answer for question nr. 1. The trouble might be the āscope of the variableā that you mentioned, because I basically have:
def buildingMenus(self):
self.myMenu = []
l = Link(text="Some link")
l.set_event_handler("click", self.handle_my_event)
self.myMenu.append(l)
def showMenus():
for m in self.myMenu
self.add_component(m)
So, I donāt know whether the handler gets lost in between or not ā¦
Ok, hereās what I just tried :
from anvil import *
class Form1(Form1Template):
def __init__(self, **properties):
# You must call self.init_components() before doing anything else in this function
self.init_components(**properties)
self.myMenu=[]
def buildingMenus(self):
l = Link(text="Some link")
l.set_event_handler("click", self.event_1)
self.myMenu.append(l)
l = Link(text="Some link 2")
l.set_event_handler("click", self.event_1)
self.myMenu.append(l)
def showMenus(self):
print("menu=",self.myMenu)
for m in self.myMenu:
self.add_component(m)
def event_1(self,**rest):
print("In 1",rest)
rest['sender'].set_event_handler("click",self.event_2)
def event_2(self,**rest):
print("In 2")
def form_show (self, **event_args):
self.buildingMenus()
self.showMenus()
It assigns event_1 handler to each new created link.
When clicked, the event handler assigns a new event handler to the object passed.
It sort of says that changing the event handlers works.
Have I got the right end of the stick, so to speak?
Yes, this works. I have a bit more complicated case - if a user hits an option to go to submenu, and then he goes back, the click event doesnāt fire anymore, if the links are removed and then added again.
Hereās a simplified version - event handler calls showMenus again and rebuilds menus:
from anvil import *
class Form1(Form1Template):
def __init__(self, **properties):
# You must call self.init_components() before doing anything else in this function
self.init_components(**properties)
self.myMenu=[]
def buildingMenus(self):
l = Link(text="Some link")
l.set_event_handler("click", self.eventHandler)
self.myMenu.append(l)
l = Link(text="Some link 2")
l.set_event_handler("click", self.eventHandler)
self.myMenu.append(l)
def showMenus(self):
if self.menusShown:
for m in self.myMenu:
print("removed:",m)
m.remove_from_parent()
for m in self.myMenu:
print("added:",m)
self.add_component(m, slot='sidebar')
self.menusShown = True
def eventHandler(self, **event_args):
self.showMenus()
def form_show (self, **event_args):
self.buildingMenus()
self.menusShown = False
self.showMenus()
Does that code fail for you?
When I run that code, it does what I expect. First time I run the app it just adds the links, then with each click it removes the links and adds them again. Clicked them both many times.
I might not be understanding something.
(edit) - that āslotā parameter to the add_component, Iām not sure I know what that does?
slot parameter puts a component in predefined area, like default - cards, sidebar and nav-right on MaterialDesign theme (forum post)
In fact, I tried to run the above code without the slot parameter, and if works from some reason! Maybe it is related to particular browser, or the fact that Iām still on a trial plan, so there might be some additional iframes around my screen.
But I found an even better and āprettierā solution - by putting a LinearPanel around Link elements, so each menu is a LinearPanel, which can then be hidden or shown, like this:
from anvil import *
class Form1(Form1Template):
def __init__(self, **properties):
self.init_components(**properties)
self.myMenu1Items=[]
self.myMenu2Items=[]
def buildingMenus(self):
l = Link(text="First menu 1")
l.set_event_handler("click", self.eventHandler)
self.myMenu1Items.append(l)
l = Link(text="First menu 2")
l.set_event_handler("click", self.eventHandler)
self.myMenu1Items.append(l)
l = Link(text="Second menu 1")
l.set_event_handler("click", self.eventHandler)
self.myMenu2Items.append(l)
def showMenus(self):
self.myMenu1 = LinearPanel(visible=True)
for m1 in self.myMenu1Items:
self.myMenu1.add_component(m1)
self.myMenu2 = LinearPanel(visible=False)
for m2 in self.myMenu2Items:
self.myMenu2.add_component(m2)
self.add_component(self.myMenu1, slot='sidebar')
self.add_component(self.myMenu2, slot='sidebar')
def eventHandler(self, **event_args):
self.add_component(TextBox(text="menu was clicked"))
self.myMenu1.visible = not self.myMenu1.visible
self.myMenu2.visible = not self.myMenu2.visible
def form_show (self, **event_args):
self.buildingMenus()
self.showMenus()
Thanks for your guidance!
1 Like
Iād forgotten all about that post.
Glad you got it working. I think your new way is much better.