Closing popovers of popovers programmatically

The short version of the problem is that I want to hide a popover without having to click on it.

I have several RichText components with popovers from anvil extras (Great addition, by the way!). The popovers are added programmatically and contain a menu of things the user can do with the component (increase font size etc) and is triggered by a mouse click. Selecting font size in the first popover opens a new popover (a popover on a popover!) where the user can select a number.

After selecting font size and pressing enter, I want the popover to disappear. As it is, I have to click twice on the element itself to remove both the first and the second popover.

I have tried the .pop(‘hide’) method but it does not seem to work well since I cannot capture the component using .get_components (or?)

Also tried .raise_event(‘click’) and ‘x-click’ both on the original component (error since RichText components do not have that event, although it kind of does with popovers) and a random invisible component. Did not work. (Maybe augment event handler is possible?)

Something that almost worked was
comp=event_args[‘sender’].parent.parent
comp.remove_from_parent()
But it just left a small white empty component on the screen.

I have ended up with an inelegant solution: Popovers automatically disappear when the screen scrolls! So I add a large element and set focus on it in a way that forces the screen to scroll! And then delete it and set focus back to the same screen.

There must be a more elegant solution?

Are you able to create a minimal example and share the clone link?
The description is relatively clear but a clone link will make it easier to provide a solution.

Yes, I will try to do so. Thank you!

Here is a minimal example. Not with popovers on popovers (which may create extra problems), but with a popover that can change the font size that I would like to go away after enter is pressed)

https://anvil.works/build#clone:XEXB57MV6ODC7DJX=SWTKHMYIOI4RL5BZ3VX5SWEE

How about something like this:

    def set_font_size(self, sender, **event_args):
        comp = self.get_comp(sender.tag.uid)
        comp.font_size = int(sender.text)
        sender.popper.pop('hide')

When a component is used as a popover a .popper attribute is added to the component.

Yes, this works in the example. Thank you!

In mode complicated situations I still have problems. The sender is a textbox that is inside a panel that is inside a popover. In that case it does not seem to work, or maybe I just have to get at the right sender by using parents?

More generally: It seems that popovers are not components that we can get at by calling get_components() on an object? And if I can get at it: Can the parent of a popover be a popover?

Here is an example with a slightly more complicated popover structure. In this case it does not work.

https://anvil.works/build#clone:XEXB57MV6ODC7DJX=SWTKHMYIOI4RL5BZ3VX5SWEE

I get:
AttributeError: ‘RichText’ object has no attribute ‘popper’

For that example you can do

    def set_font_size(self, sender, **event_args):
        comp = self.get_comp(sender.tag.uid)
        comp.font_size = int(sender.text)
        sender.parent.popper.pop('hide')

But i would avoid doing any more .parent. We’re getting to the point where it feels too cumbersome.
The example clone writes the popover component in code - at this stage i’d consider making the popover a form, which might make the logic clearer.


the error you were getting was because you were doing comp.popper, which doesn’t make sense in this context.


Correct - Popovers don’t live inside anvil’s component hierarchy. A button can have a popover attached to it but it doesn’t have any way to access using ‘get_components()’. To interact with the popover use .pop() to interact with the component that popped used .popper

1 Like

Great. Thank you!

Also, thank you very much for all the extra work with anvil extras. Very useful!

Hans Olav