Box Layout in a Button in a Box Layout

876 views
Skip to first unread message

Marc Shapiro

unread,
Mar 21, 2016, 12:13:26 AM3/21/16
to Kivy users support
I have a vertical box layout in which I place buttons each containing a horizontal box layout.  The number of buttons is variable, so they are  added in python code, not in the kv code.  For some reason only the last (bottom) button is showing the data in its child layout.  The pertinent parts of my code are as follows:

    def setup_data(self):
        scroll = data.ids['idScroll']
        layout = data.ids['idData']
        height = layout.height
        count = int(txtDays.text)
        lineheight = max(height/count, 25)
        layout.clear_widgets()
        layout.height = count * lineheight

        remaining_caf = int(txtCoffee.text) / 2 * int(txtDays.text)
        remaining_decaf = remaining_caf
        remaining_pct = 100.0
        items = []
        buttons = []

        ## Connection to database made and dataset (rsData) obtained here ##

        for rec in rsData:
            item = BoxLayout(orientation='horizontal', height=line_height, width=self.width)
            item.clear_widgets()
            item.padding=[5,2]
            item.spacing=5

            c.execute('SELECT date("' + display_date + '", "+1 day")')
            display_date = str(c.fetchone()[0])

            ## Label widgets set and added to horizontal box layout (item) here ##

            btn = Button(font_size=16, padding=[5,1]) 
            btn.bind(on_press=partial(data.btnEdit, ## function parameters here ##))

            btn.add_widget(item)
            layout.add_widget(btn)

            i += 1


I get the layout with the correct number of buttons, but only the last button shows any data.  The others are completely blank.  If instead of:

            btn.add_widget(item)
            layout.add_widget(btn)

I use:

            item.add_widget(btn)
            layout.add_widget(item)

I get all of the data properly displayed with a button in each row to use for editing the data, so I know that all of the data is correctly retrieved and in the horizontal box layout (item).  I want to have the layout in the button instead of the other way around in order to save space and give the user a larger area in which to click.

Can anyone tell me what I am doing wrong?

Also, I know that a Listview should do this for me, but I have not yet been able to wrap my head around the data adapters for how to use them.  Since the Listview is supposed to be depreciated I really don't want to spend too much time trying to figure out how to use them. 

Oon-Ee Ng

unread,
Mar 21, 2016, 1:19:13 AM3/21/16
to kivy-...@googlegroups.com
The button is not a layout, hence each item doesn't know how to
position itself. You could bind the layout pos and size to the
button's pos and size, for example. Or use kv to do that for you,
write a rule for a MyButton class (subclass of Button) which contains
a layout.

Or you could subclass a Layout and ButtonBehaviour since what you
really want is button behaviour on a layout.
> --
> You received this message because you are subscribed to the Google Groups
> "Kivy users support" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to kivy-users+...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Marc Shapiro

unread,
Mar 21, 2016, 2:24:02 AM3/21/16
to kivy-...@googlegroups.com
I'm new to kivy, so bear with me. This is my first app using kivy and I
would like to be able to learn from it and reuse what I learn in later
apps. It seems that having a button that is a subclass of a box layout
would, indeed, be a good way to do what I want. Exactly how would I
create this, and add it in code as I am currently doing?

If I wanted to simply bind the location of the layout to that of the
button (a more general approach) how would I do that?

Oon-Ee Ng

unread,
Mar 21, 2016, 3:45:43 AM3/21/16
to kivy-...@googlegroups.com
The thing is there's multiple ways to do it (insert irrelevant comment
about violating zen of python here)

Here's one, in your kv file:-

<MyButton@Button+BoxLayout>:
# the rest is just normal kv file stuff, as you're doing in item

If you want to bind the location of the layout to that of the button,
in your kv file:-

<MyButton>:
BoxLayout:
size: root.size # or self.parent.size
pos: root.pos # or self.parent.pos

Then you just need to create instances of the MyButton class instead
of instances of BoxLayout and Button.

You can do similar in python (for binding, look up the 'bind' function
in kivy docs), with more flexibility but longer code.

Hope it helps, try it out and come back with questions =)

Marc Shapiro

unread,
Mar 23, 2016, 10:19:16 AM3/23/16
to kivy-...@googlegroups.com
My problem is that neither the Button (btn), nor the BoxLayout (item)
are defined in the .kv file. Both are defined in code as shown below.

layout is defined in the .kv file as a BoxLayout within a ScrollView

item = BoxLayout(orientation='horizontal', height=line_height,
width=self.width)
btn = Button(font_size=16, padding=[5,1])
btn.add_widget(item)
layout.add_widget(btn)

This means that I need to do the bindings in python, not kvlang.

Also, could you elaborate on creating the custom widget inheriting from
Button and BoxLayout. Again, since item is created in code and has no
kvlang component, I'm not following how to put it all together.
>>>> email tokivy-users...@googlegroups.com.
>>>> For more options, visithttps://groups.google.com/d/optout.
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Kivy users support" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email tokivy-users...@googlegroups.com.
>> For more options, visithttps://groups.google.com/d/optout.

ZenCODE

unread,
Mar 23, 2016, 5:24:17 PM3/23/16
to Kivy users support
Oon-Eee Ng is right in that a button is not a layout or a container. You don't want to put stuff inside a button. Rather use another layout as a container and put the button and the layout in that.

And it does not matter whether your widgets are created in kv or Python: you can do exactly the same thing either way...,

So it's difficult, because the example is not runnable, but I would suggest separating things. Put both your button and your layout in another layout. And try to avoid sub-classing multiple widgets: that is the path to the dark side. Rather separate concerns, so each which has a single responsibility: either layout or behaviour, but not both. Mixing too many things together makes things murky and difficult to debug.

Divide and conquer :-)

Oon-Ee Ng

unread,
Mar 23, 2016, 6:12:25 PM3/23/16
to kivy-...@googlegroups.com
Slight correction, ButtonBehavior is not a widget (subclasses object),
so it's not subclassing multiple widgets =). I think that's why it
exists, in the first place. For example a Button is just a Label with
ButtonBehavior mixed in.

Agree that it can confuse things though.
> --
> You received this message because you are subscribed to the Google Groups
> "Kivy users support" group.
> To unsubscribe from this group and stop receiving emails from it, send an

Marc Shapiro

unread,
Mar 24, 2016, 12:53:54 AM3/24/16
to kivy-...@googlegroups.com
Thank you! That was exactly what I needed. I completely missed the
whole idea of behaviors in your earlier post.

Solution:

Two lines to created a BoxLayout with ButtonBehavior.
Use that class instead of a BoxLayout for item.
Bind the partial function to item instead of a separate button.

Done!

I have spent the last week trying to figure out how to get this to
work. As soon as I understood that you were talking only about a
behavior, and not a full class inheritance I looked that up on the kivy
site and within five minutes I had the app doing exactly what I wanted.

Thank you, again!

Marc
Reply all
Reply to author
Forward
0 new messages