2

I'm struggling to understand layout positioning in Kivy.

My aim is to create a simple form containing 3 rows, each one consisting of a label and a text input.

For example, like on this wireframe drawn with PlantUML, Salt (source):

UI-prototype as rendered PlantUML using Salt

What I tried

I've tried combining different layouts but cannot achieve the right alignment and I also don't understand what's going wrong.

First attempt:

KV file:

<CustomLabel@Label>:
    size_hint: .2, .1
    text_size: self.size
    
<CustomTextField@TextInput>:
    size_hint: .2, .1
    
<CustomFloatLayout@FloatLayout>:
    
<TestLayout>:     
    orientation: 'horizontal'
    CustomFloatLayout:
        CustomLabel:
            text: 'Field 1:'
            pos_hint: {'x': .1, 'center_y': .95}
        CustomTextField:
            hint_text: 'hint 1'
            pos_hint: {'x': .1, 'center_y': .92}
            
    CustomFloatLayout:
        CustomLabel:
            text: 'Field 2:'        
            pos_hint: {'x': .1, 'center_y': .75}
        CustomTextField:
            hint_text: 'hint 2'
            pos_hint: {'x': .1, 'center_y': .72}    
            
    CustomFloatLayout:
        CustomLabel:
            text: 'Field 3:'
            pos_hint: {'x': .1, 'center_y': .55}
        CustomTextField:
            hint_text: 'hint 3'
            pos_hint: {'x': .1, 'center_y': .52}

Python code:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

class TestLayout(BoxLayout):
    pass

class TestApp(App):
    def build(self):
        return TestLayout()

if __name__ == '__main__':
    TestApp().run()

Issue

The y-center of the labels is almost at the bottom of the y-center of the text input fields instead of being aligned at the same y-value. And what is even more puzzling: Each row has another offset from the left border of the window.

Second attempt:

KV file:

<CustomLabel@Label>:
    size_hint: .2, .1
    text_size: self.size
    
<CustomTextField@TextInput>:
    size_hint: .2, .1
        
<CustomBoxLayout@BoxLayout>:
    orientation: 'horizontal'
    pos_hint: {'x': .1}
    

<TestLayout>:     
    CustomBoxLayout:
        CustomLabel:
            text: 'Field 1:'
            pos_hint: {'x': .1, 'center_y': .95}
        CustomTextField:
            hint_text: 'hint 1'
            pos_hint: {'x': .5, 'center_y': .95}
            
    CustomBoxLayout:
        CustomLabel:
            text: 'Field 2:'        
            pos_hint: {'x': .1, 'center_y': .75}
        CustomTextField:
            hint_text: 'hint 2'
            pos_hint: {'x': .5, 'center_y': .75}    
            
    CustomBoxLayout:
        CustomLabel:
            text: 'Field 3:'
            pos_hint: {'x': .1, 'center_y': .55}
        CustomTextField:
            hint_text: 'hint 3'
            pos_hint: {'x': .5, 'center_y': .55}

Python code:

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout

class TestLayout(FloatLayout):
    pass

class TestApp(App):
    def build(self):
        return TestLayout()

if __name__ == '__main__':
    TestApp().run()

Issue

Looks better, but label and text input are still not aligned at the same center y value. In addition, if I change the pos_hint of e.g. the second CustomLabel to pos_hint: {'x': .1, 'bottom': .75}, then this label appears at the very bottom of the entire window. I would have expected it to be at the bottom of the current BoxLayout row.

Question

  1. How can I achieve a proper aligning of the labels and the text input fields?
  2. Why are the labels/input fields of the first attempt located on the diagonal of the window?
  3. Why do the labels appear at the bottom of the entire window when changing their bottom value within the second attempt code?
hc_dev
  • 8,389
  • 1
  • 26
  • 38
AnjaM
  • 2,941
  • 8
  • 39
  • 62
  • 1
    Well formulated question, with good attempts and clear questions. I added a prototype-/[wireframe image](https://plantuml.com/salt) (as far as I understood the desired layout). Correct ? Would help to see images showing issues with 1st and 2nd attempt. – hc_dev Dec 30 '21 at 12:12

1 Answers1

2

For your desired layout as Grid (arranging 3 rows with 2 columns) use GridLayout:

GridLayout:

Widgets are arranged in a grid defined by the rows and cols properties.

The order in automatic arrangement of widgets (using add_widget) is controlled by property orientation:

orientation is an OptionProperty and defaults to ‘lr-tb’.

Python code (including widget declaration)

Adjusted from the examples in documentation of GridLayout:

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput

class TestApp(App):
    def build(self):
        return layouted_widgets()  # replace this by `pass` for trying with KV


    def layouted_widgets():
        layout = GridLayout(rows=3, cols=2, row_force_default=True, row_default_height=40)
        # 1st row
        layout.add_widget(Label(text='Label 1'))
        layout.add_widget(TextInput(text='Value 1', multiline=False, width=100))
        # 2nd row
        layout.add_widget(Label(text='Label 2'))
        layout.add_widget(TextInput(text='Value 2', multiline=False, width=100))
        # 3rd row
        layout.add_widget(Label(text='Label 3'))
        layout.add_widget(TextInput(text='Value 3', size_hint_x=None, width=100))
        return layout


if __name__ == '__main__':
    TestApp().run()

See how the 3rd text-input is reduced in size to fit the text-value. This is because of the property size_hint_x=None which overrides the given width=100.

enter image description here

For specific positions inside each cell:

The original Kivy Guide: Widgets, Organize with Layouts is a bit vague on that.

KV file

Using both, your Custom.. inherited widgets (1st and 2nd row) and the basic widgets (in 3rd row only):

<CustomLabel@Label>:
    # to add space from left and top-bottom, effects position x,y
    text_size: self.width - dp(20), self.height - dp(10)
    halign: 'left'
    valign: 'center'

<CustomTextField@TextInput>:
    size_hint: self.width - dp(20), self.height - dp(10)
    # pos_hint: not supported as expected, thus use padding
    # left, right
    padding_x: dp(20)
    # top, bottom
    padding_y: dp(5)

GridLayout:
    rows: 3
    cols: 2
    row_force_default: True
    row_default_height:40
    CustomLabel:
        text: 'Field 1:'
    CustomTextField:
        hint_text: 'hint 1'
    CustomLabel:
        text: 'Field 2:'        
    CustomTextField:
        hint_text: 'hint 2'
    Label:
        text: 'Field 3:'
    TextInput:
        hint_text: 'hint 3'

Renders the UI in 3 rows and 2 cols - almost all labels and text-inputs left-aligned. Only the 3rd label looks different to demonstrate the alignment-effect on others. GridLayout with aligns and padding

hc_dev
  • 8,389
  • 1
  • 26
  • 38
  • Thanks for your reply. In the KV file you provided, it seems that the `pos_hint` for the `x`-value does not have any effect. Increasing this value does not lead to the text of the label moving to the right. If I remove the `text_size: self.size` row from the custom label, the label texts move to the right but I cannot control their exact position and they are not left-aligned with respect to each other (you can see this by changing the text "Field 1" to "Longfield 1"). Isn't it possible to control the position within the cells of a grid layout? – AnjaM Jan 01 '22 at 14:56
  • Also, could you please add an explanation on the 2nd and 3rd questions from my original post? – AnjaM Jan 01 '22 at 14:57