Search code examples
pythonpython-2.7kivyword-wrap

Kivy: Sizing Buttons to fit wrapped text within dropdown


I'm having issues building a Kivy dropdown with word wrapping enabled for the text, such that the button widgets size accordingly to accommodate the full text.

I have followed guidance from the stack overflow thread below and similar blog post that is also linked.

The text wraps as expected, however as the text string gets longer, there is an increasing amount of "padding" above and below the text when the button is rendered. I am unsure of what is causing this and would like to eliminate this effect.

Updated: (Edited code to be more concise to the issue. Edited image to match)

The "extra padding" is not related to the length of text, but rather the index of the loop and/or widget count added to the dropdown.

Further editing this line of code:

btn2 = WrapButton(text=('|' + str('long text..is long...%d' % (21-index) * index) + '|') , size_hint=(1,None))

To:

btn2 = WrapButton(text=('|' + str('long text..is long...%d' % (21-index) * index) + '|') , size_hint=(None,None),width=700)

(set size_hint=(none,none) rather than (1,none) and add width=700)

Eliminates the issue. I can not wrap my head around what is causing this behavior. The edited code loses the automatic width sizing for the button, and I can't imagine how a width size_hint is causing vertical "padding".

Screenshot showing issue

This code demonstrates the issue:

from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.boxlayout import BoxLayout
from kivy.base import runTouchApp
from kivy.lang import Builder

Builder.load_string('''
<WrapButton>:
    halign: "center"
    valign: "center"
    font_size: 20
    size_hint_y: None
    text_size : self.size
    height: self.texture_size[1]
''')

class WrapButton(Button):
    pass

dropdown2 = DropDown()

layout = BoxLayout(padding=0,orientation='vertical')

mainbutton2 = WrapButton(text='Select...', size_hint=(1, None),height=95,pos_hint={'center_x': .5, 'center_y': 0})
mainbutton2.bind(on_release=dropdown2.open)
layout.add_widget(mainbutton2)

for index in range(20):
    btn2 = WrapButton(text=('|' + str('long text..is long...%d' % (21-index) * index) + '|') , size_hint=(1,None))
    btn2.bind(on_release=lambda btn2: dropdown2.select(btn2.text))
    dropdown2.add_widget(btn2)

dropdown2.bind(on_select=lambda instance, x: setattr(mainbutton2, 'text', x))

runTouchApp(layout)

screenshot

Update: The answer accepted below is resulting in artifacts on Android. I am working to test on other devices to rule out the device itself. Any input from the community here would be appreciated!

Artifacts..broken Kivy install?


Solution

  • The solution is to replace text_size : self.size with text_size : self.width, None. Please refer to the example and output for details.

    Example

    main.py

    from kivy.uix.button import Button
    from kivy.uix.dropdown import DropDown
    from kivy.uix.boxlayout import BoxLayout
    from kivy.base import runTouchApp
    from kivy.lang import Builder
    
    Builder.load_string('''
    <WrapButton>:
        halign: "center"
        valign: "center"
        font_size: 20
        size_hint_y: None
        text_size : self.width, None
        height: self.texture_size[1]
    ''')
    
    
    class WrapButton(Button):
        pass
    
    
    dropdown2 = DropDown()
    
    layout = BoxLayout(padding=0, orientation='vertical')
    
    mainbutton2 = WrapButton(text='Select...', size_hint=(1, None), height=95, pos_hint={'center_x': .5, 'center_y': 0})
    mainbutton2.bind(on_release=dropdown2.open)
    layout.add_widget(mainbutton2)
    
    for index in range(20):
        btn2 = WrapButton(text=('|' + str('long text..is long...%d' % (21-index) * index) + '|'), size_hint=(1, None))
        btn2.bind(on_release=lambda btn2: dropdown2.select(btn2.text))
        dropdown2.add_widget(btn2)
    
    dropdown2.bind(on_select=lambda instance, x: setattr(mainbutton2, 'text', x))
    
    runTouchApp(layout)
    

    Output

    Img01