Search code examples
pythonjupyter-notebookwidgetipythonipywidgets

ipywidgets: Why does the checkbox have an offset towards the left in when being placed inside of a HBox container


I have the following script

from ipywidgets import Checkbox, Text, HBox, VBox, Layout, HTML
from IPython.display import display

checkbox = Checkbox(layout=Layout(width='8%', align_self='center'))
text_field = Text(layout=Layout(width='20%'))
header_row = HBox([
    HTML(value="<b>Checkbox</b>", layout=Layout(width='8%', text_align='center')),
    HTML(value="<b>Text Field</b>", layout=Layout(width='20%', text_align='center'))
])
row = HBox([checkbox, text_field], layout=Layout(align_items='center'))
ui = VBox([header_row, row])
display(ui)

I would expect this to return a column like structure, with a checkbox next to a text box. However, the result looks like this:

enter image description here

Why is my checkbox being pushed to the right? How can we adjust our script so that it is displayed correctly?


Solution

  • The core answer is I believed spelled out in the 'Details' section at the bottom of my answer to 'Align ipywidgets inside two HBoxes python' here.
    The key issue:

    By default, ipywidgets indents the checkboxes for better alignment with their descriptions. However, here you don't want this as it is causing a major issue with the alignment in your case. Fortunately, the setting indent=False ensures that the checkboxes are not indented and are aligned with the left edge of their containers.

    I think you want, instead for the checkbox line of code:

    checkbox = Checkbox(layout=Layout(width='8%', align_self='center'), indent=False)
    

    With that adjustment to your code, you'll see the following: enter image description here


    Additional option

    What though if you still want the Checkbox widget more-or-less centered below the 'Checkbox' text?
    Improve with a little padding, if you want:

    from ipywidgets import Checkbox, Text, HBox, VBox, Layout, HTML
    from IPython.display import display
    
    checkbox = Checkbox(layout=Layout(padding='0px 0px 0px 27px',width='8%', align_self='center'), indent=False)
    text_field = Text(layout=Layout(width='20%'))
    header_row = HBox([
        HTML(value="<b>Checkbox</b>", layout=Layout(width='8%', text_align='center')),
        HTML(value="<b>Text Field</b>", layout=Layout(width='20%', text_align='center'))
    ])
    row = HBox([checkbox, text_field], layout=Layout(align_items='center'))
    ui = VBox([header_row, row])
    display(ui)
    

    Key change there was the addition of padding='0px 0px 0px 27px'. The pixels you need may vary slightly.

    This how the output looks to me with that padding added:

    enter image description here



    A GridBox with a Grid Layout option

    In the referenced answer, the OP asked about a grid layout there and so I'll also add a starting point one for this example here in case that looks more in line with your preferences:

    from ipywidgets import Checkbox, Text, GridBox, HTML, Layout
    from IPython.display import display
    
    # Create the checkbox and text field
    checkbox = Checkbox(layout=Layout(padding='0px 0px 0px 29px',width='100%', align_self='center'), indent=False)
    text_field = Text(layout=Layout(width='100%'))
    
    # Create headers
    header_checkbox = HTML(value="<b>Checkbox</b>", layout=Layout(text_align='center'))
    header_text_field = HTML(value="<b>Text Field</b>", layout=Layout(text_align='center'))
    
    # Create a grid layout
    ui = GridBox(
        children=[header_checkbox, header_text_field, checkbox, text_field],
        layout=Layout(
            grid_template_columns='8% 20%',
            grid_template_rows='auto auto',
            grid_gap='10px 10px',
            justify_items='center',
            align_items='center'
        )
    )
    
    # Display the final UI
    display(ui)
    

    That code in my hands results like this:

    enter image description here

    Again, the specifics of the padding may need adjusting, and probably designating it with a percent is better as it is less arbitrary and will be better adapted to any window size, see below.

    UPDATE: Use of padding='0px 0px 0px 50%' a GridBox with a Grid Layout option

    A better option pointed out the OP for controlling the padding is to use a percent instead of a set pixel amount because presumably that will work no matter the size of the window. I found this worked well with the code for the GridBox with a Grid Layout option as shown below; however, I didn't find the correct combination where it works for the gridlayout, yet.

    from ipywidgets import Checkbox, Text, GridBox, HTML, Layout
    from IPython.display import display
    
    # Create the checkbox and text field
    checkbox = Checkbox(layout=Layout(padding='0px 0px 0px 50%',width='100%', align_self='center'), indent=False)
    text_field = Text(layout=Layout(width='100%'))
    
    # Create headers
    header_checkbox = HTML(value="<b>Checkbox</b>", layout=Layout(text_align='center'))
    header_text_field = HTML(value="<b>Text Field</b>", layout=Layout(text_align='center'))
    
    # Create a grid layout
    ui = GridBox(
        children=[header_checkbox, header_text_field, checkbox, text_field],
        layout=Layout(
            grid_template_columns='8% 20%',
            grid_template_rows='auto auto',
            grid_gap='10px 10px',
            justify_items='center',
            align_items='center'
        )
    )
    
    # Display the final UI
    display(ui)