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