6

I am trying to modify the visibility of an ActionButton accordingly to the current screen (using Screen Manager). I could not find a Visible property or something like that that can simply toggle visibility (neither for ActionButton nor for Widgets in general) .

A post from 2013 suggests changing the texture of the button, but I don't want to rely on such a hack to accomplish such a simple task, besides, the background of my app will be variable.

Another post suggest removing the widget and adding it again as needed. Despite its unnecessary complexity. I modified to work in my case (ActionBar and ActionButton), so I clear the widgets from the ActionView and then try to add the ActionButton. I tried storing both a weakref and the self member, with both I got the following error:

WidgetException: Cannot add <kivy.uix.actionbar.ActionButton object at 0x7fcd3fe22ce8>, it already has a parent <kivy.uix.actionbar.ActionView object at 0x7fcd3fe22870>

Any idea would be greatly appreciated. I am working with the dev version, but it neither work with 1.8.

EDIT I tried the following code:

<AppActionBar>:
    ActionView:
        id: av

        ActionButton:
            id: btn_next
            text: 'Next screen'
            icon: 'data/icons/next_dark.png'
            important: True
            on_release: app.go_next()

This function is run after the scene is loaded:

def _initialize(self):
  self.next = self.ids.btn_next.__self__ # same result if I don't use .__self__

This code raises the exception posted above:

self.ids.av.clear_widgets()
self.ids.av.add_widget(self.next)

Here is the full exception trace:

     self._mainloop()
   File "/usr/local/lib/python2.7/dist-packages/kivy/core/window/window_pygame.py", line 266, in _mainloop
     EventLoop.idle()
   File "/usr/local/lib/python2.7/dist-packages/kivy/base.py", line 330, in idle
     Clock.tick_draw()
   File "/usr/local/lib/python2.7/dist-packages/kivy/clock.py", line 429, in tick_draw
     self._process_events_before_frame()
   File "/usr/local/lib/python2.7/dist-packages/kivy/clock.py", line 562, in _process_events_before_frame
     if event.tick(self._last_tick) is False:
   File "/usr/local/lib/python2.7/dist-packages/kivy/clock.py", line 309, in tick
     ret = callback(self._dt)
   File "/usr/local/lib/python2.7/dist-packages/kivy/uix/boxlayout.py", line 174, in do_layout
     c.width = w
   File "properties.pyx", line 345, in kivy.properties.Property.__set__ (kivy/properties.c:3589)
   File "properties.pyx", line 377, in kivy.properties.Property.set (kivy/properties.c:4064)
   File "properties.pyx", line 431, in kivy.properties.Property.dispatch (kivy/properties.c:4657)
   File "/usr/local/lib/python2.7/dist-packages/kivy/uix/actionbar.py", line 552, in on_width
     self._layout_all()
   File "/usr/local/lib/python2.7/dist-packages/kivy/uix/actionbar.py", line 441, in _layout_all
     super_add(child)
   File "/usr/local/lib/python2.7/dist-packages/kivy/uix/boxlayout.py", line 212, in add_widget
     return super(BoxLayout, self).add_widget(widget, index)
   File "/usr/local/lib/python2.7/dist-packages/kivy/uix/layout.py", line 78, in add_widget
     return super(Layout, self).add_widget(widget, index)
   File "/usr/local/lib/python2.7/dist-packages/kivy/uix/widget.py", line 466, in add_widget
     % (widget, parent))
 WidgetException: Cannot add <kivy.uix.actionbar.ActionButton object at 0x7fecb5d6ed50>, it already has a parent <kivy.uix.actionbar.ActionView object at 0x7fecb5d6e8d8>
Community
  • 1
  • 1
papirrin
  • 2,004
  • 22
  • 30
  • Can you send some code which you tried ? – paarth batra Apr 22 '14 at 05:39
  • Sure, I added a short example. – papirrin Apr 22 '14 at 05:44
  • 1
    @papirin - I can help you probably if you could post full example . A running code ... I also faced such issues with some buttons and i fixed it .Probably we would need to tweak by either using try catch or otherwise some thing else ( something else would be much neater than a try catch ) :) – paarth batra Apr 23 '14 at 06:54
  • @paarthbatra Thank you very much for taking a look at this. I finally figured out that the problem is really a bug in the way ActionView handles its children, but this feature is still under development. I will look again when ActionBar stabilizes. – papirrin Apr 25 '14 at 03:21
  • You can also set opacity `self.next.opacity = 0` to hide the button and `self.next.opacity = 1` to show it although it's maybe not exactly what you want because it just makes it transparent. – martin Jan 22 '15 at 15:15

4 Answers4

11

Removing widgets from parents is not a very good idea, if the goal is to hide them. When something is removed from the Kivy tree structure, it will be picked up by the garbage collector. You could try to keep a reference to it, but conceptually hidding is different from removing.

The best solution for me has been using the opacity property. It is kind of the same principle of a visible property but more powerful because it accepts gradients (and animations).

The "caveat" is that you have to consider that the Widget is still there. It is just invisible. In some cases, you might want to try and combine the opacity with the disabled property.

Button:
    opacity: 0
    disabled: True # To make sure it doesn't capture events accidentally
toto_tico
  • 17,977
  • 9
  • 97
  • 116
  • Could you please provide an example? – DaNNuN May 09 '18 at 18:11
  • 1
    Button: id: button1 text: '' opacity: 0 size: (0,0) – DaNNuN May 09 '18 at 18:57
  • I add an example, i you are resizing to `0,0`, you probably won't need the opacity. If `size` doesn't work, you should try `size_hint` first. – toto_tico May 10 '18 at 10:16
  • Sup Tico, In my case I want my button to appear and grow, so opacity and size both grow and shrink using animation. – DaNNuN May 10 '18 at 15:26
  • This doesn't fully work for me. Even when disabled, the button still doesn't propagate events (touch events in my case) to the widgets below it. – iliis Aug 15 '23 at 14:32
5

There's a better way to hide widgets than adding or removing them. Simply set the position to include an offscreen coordinate :

# save the old y-coordinate of MyWidget.pos
root.saved_y = MyWidget.y
# Now move the widget offscreen (recall that pos is just an alias for (x, y))
MyWidget.y = 5000

(I've tested this solution; it works.)

1

I'm still learning kivy but it appears that, surprisingly, there is no property or member function to do this. Instead, you have to remove the widget from its parent, or set its color alpha to 0 (which will only work in cases where you have one color).

Update:

The traceback indicates that self.next still has a parent when self.ids.av.add_widget(self.next) is called. Since this call is preceded by a self.ids.av.clear_widgets(), the only way that self.next is still in widget tree is that it is actually not a child of self.ids.av. Maybe it is a child of the default layout used by av, and layout doesn't immediately get garbage collected. Try

print 'next in av.children:', self.next in self.ids.av.children
print 'parent of next:', self.next.parent
# self.ids.av.clear_widgets()
# self.ids.av.add_widget(self.next)
parent = self.next.parent
parent.remove_widget(self.next)
parent.add_widget(self.next)
Oliver
  • 27,510
  • 9
  • 72
  • 103
  • The code I posted removes the widget, the problem is that there is an error when I try to add it again. – papirrin Apr 22 '14 at 19:36
0

You could try to set size_hint to 0. I did this to hide a layout that was nested inside a BoxLayout. It worked flawlessly. Another method is to remove the widget from its parent. But if you are planning to re-add it you need to instantiate it again. For example:

widget = Widget()
parent.add_widget(widget)
parent.remove_widget(widget)
# Create the widget again
widget = Widget()
parent.add_widget(widget)
nico
  • 11
  • 3