Raise_event and raise_event_on_children do not 'bubble' or 'traverse"?

I thought of using a custom event to signal, in a deep hierarchy of components, that some data has to be reloaded. So in the child I used raise_event(‘x-mysignal’) and expected to catch it (by setting an event handler) in a component somewhere higher in the component hierarchy. This did not work. It seems only the component that you use to raise the event gets signalled.
As an alternative I thought of using get_open_form().raise_on_children(‘x-mysignal’) to send a signal from the top of the component hierarchy so that the intended target would get the signal. This did nog work either. Only when raising on the direct parent did the raise_on_children have the intended effect.

I assumed the event would bubble up the component hierarchy in the case of raise_event, or descend the component tree with raise_on_children. But it seems this is not the case.
The docs are not very clear on this.

IMO this severely limits the usefulness of the event system. If you know the exact location of a target component in the hierarchy you can just call a method on that component and be done with it. The advantage of the event system would decouple components making the whole easier and more robust to changes.

What am I missing?

PS I would be nice if the raise_event methods were documented in the API Docs.

Are you able to share a small example clone app that demonstrates the issue? This might encourage others to jump in and possibly help.

In the end, if the event system does not work as you expect, you could submit a feature request. A clearer example would be nice in that case as well.

My app (first Anvil app!) would be a little too complex for demonstrating my problem.
Was my problem description clear enough?

An small example app would show folks the exact hierarchy/nested structure of your app and therefore may be able to help you find improvements.

1 Like

Working on it … :wink:

2 Likes

Hi,
it’s not that your description is necessarily unclear, it’s just seeing the issue is far more useful. If you don’t provide an example then someone has to try and recreate your code, and that’s a big ask.

If you can’t share your project, please can you try to create a new, smaller project that demonstrates just that issue? That way someone like @alcampopiano or me or anyone else can easily see the problem and offer help.

1 Like

Demo app: Demo app

Hope I’m doing this right. The app has a form with a panel (Form2) added in code. In the panel an event is raised, which is supposed to be caught in the main form.
Change comments in Form2 to change behaviour.

Caveat - I’m a bottle of red in…

Initial thought is that you are attaching a signal handler to “self” and you are raising the event in “self”.

Problem is, the "self"s are both different :slight_smile:

So -
in form 1, change this :

self.card_4.add_component(Form2())

to this -

f = Form2()
self.card_4.add_component(f)

and change this -

self.set_event_handler('x-mysignal', self.mysignal_handler)

to this -

f.set_event_handler('x-mysignal', self.mysignal_handler)

But that effectively attaches the handler to the component (Form2) that raises the event. That does not make sense to me; it could just call a method on that form instead.
I’d like the event raising form to be ignorant of the receiver. The receiver catches the event by registering the handler without knowing the location of the sender.

edit rereading your original post, i think it might take someone a little better at the event system than me to help.

The form raising the event is ignorant of the receiver - the receiving party is the one attaching a listener. It could call a method on the form, but that surely would involve polling?

I think I might not be understanding your use case, my apologies if that is so, but are you looking for a “global” event system? Something like this maybe :

edit 2
Yeah, I definitely misunderstood what you were getting at, sorry.

Then we’re both trying to understand ;-).
Since I’m developing my first ‘real’ Anvil app, I’m trying to understand the Anvil way of doing things. So I think I’m not looking for a global event system (I’d probably use Blinker for that), but more using the Anvil event system more fully.

In your code example you assume that the receiving Form1 has a handle on the sending Form2 (which is indeed the case in my demo app), but which is not necessarily so in more complex cases, and is not even desirable.

I’ve had a think about this and here are some thoughts.

Since you are treating Form2 as a component then a thought experiment would be to consider what would happen if you used an anvil component there instead. So replace Form2() with Button()

On click that Button will raise a ‘click’ event. You can click that button as much as you like but you’re not going to catch that event unless you set the Button‘s ‘click’ event handler within Form1.

With Button we do this in the design view, or in the code view with .set_event_handler

So you’d have to take the same approach with Form2() - use as a component and set its ‘x-my-signal’ event handler in the design view or with .set_event_handler in the code view

I can follow your reasoning (I think). However it presumes that you want to catch the event in the containing Form. But my point is that this is limiting: the component emitting the event should not care who catches the event. And the catching component (Form or otherwise) should not know the emitter.

Usecase example: a component deep in the component hierarchy modifies some data. It emits a custom event signalling this change. Other components in the hierarchy. whereever they are, that. are interested in these changes will register an event handler. They will then get notified.
No direct dependencies, nice decoupling.

PS how does one go about setting the event handler in design view for a custom event?

If memory serves, in a standard component, setting an event handler for a custom event is normally only possible via code.

For a custom component, if the custom event is defined as part of the custom component’s interface, then the event is exposed to the design view, so then you can set the handler there.

2 Likes

You’ve described a relatively common publish-subscribe event dispatch mechanism. For any given event, there is an open-ended set of publishers, and a set of subscribers.

Even with the above, there are still many variations on this theme.

  1. All subscribers get the event.
  2. The set of subscribers is an ordered list instead of a set. Some systems let you register a subscriber to be first, or last, or sorted by “priority”. Others implement the list by traversing the tree of instantiated GUI components, seeking interested parties.
  3. Each subscriber decides whether to “consume” a given event instance, or not. Once consumed, no further subscribers receive it.

So there is no one-size-fits-all.

The link I gave was for a general subscribe & dispatch system, which increasingly seems to fit what @mjmare is describing. In my opinion, anyway.

I’ll certainly have a look at PyEventEmitter.

Or were you referring to one of the other links in that post?

Yes, that’s the one. There are two in that post, i think I ended up using the first but the second also looked good.

Ooo…

(emphasis mine)

I’m definitely going to take a closer look!

Edit: @david.wylie, what do you think about using Weak References to avoid keeping handlers alive longer than necessary. (E.g., when a Form closes).

1 Like

It’s not an area of Python I’ve explored. Looks like my evening reading after work …