Makie: layout of IntervalSlider and Slider and custom labels/text display

Hello,

Three questions regarding Makie layout of IntervalSlider and Slider.

  1. On the left side of the above image, I would like to have a :grey10 background for the IntervalSlider and Slider as in the Textbox. However, if I include the code
axis_controls_panel = 
    Axis(
        controls_grid_layout[:,1],
        backgroundcolor = :grey10)

hidedecorations!.(axis_controls_panel)

in the code fragment below, the :grey10 background does not display and the sliders become unresponsive. How to do this correctly?

  1. How can I add a custom label to both the IntervalSlider and Slider above and left-justified for each widget: “Window Width” and “Window Centre” in this case?

  2. How can I display as text the current value pair of IntervalSlider and the current value of Slider immediately below each widget and centred?

[The new Makie documentation looks good and the comprehensive layout tutorial was helpful in refactoring my code.]

Code fragment:

# Figure for GLMakie display
noto_sans = assetpath("fonts", "NotoSans-Regular.ttf")

figure = Figure(
            backgroundcolor = :black,
            resolution = (1920, 1040), 
            font = noto_sans)

# Top level grid layout
top_level_grid_layout = figure[1:2, 1:3] = GridLayout()

# Control panel grid layout
control_panel_grid_layout = top_level_grid_layout[1:2, 1] = GridLayout()            
text_panel_grid_layout = control_panel_grid_layout[1, 1] = GridLayout()
controls_grid_layout = control_panel_grid_layout[2, 1] = GridLayout()

# Display grid layout
displays_grid_layout = top_level_grid_layout[1:2, 2:3] = GridLayout()

# 3D image volume
volume_grid_layout = displays_grid_layout[1, 1]  = GridLayout()

# 2D image slice planes
axial_grid_layout = displays_grid_layout[1, 2]  = GridLayout()
sagittal_grid_layout = displays_grid_layout[2, 1]  = GridLayout()
coronal_grid_layout = displays_grid_layout[2, 2]  = GridLayout()

#
# Control Panel display
#

# Infomation text box
axis_text_panel = 
    Axis(
        text_panel_grid_layout[1,1],
        backgroundcolor = :grey10)

hidedecorations!.(axis_text_panel)

# Patient data
patient_string = string("a set of concatenated patient info")

# Image acquisition data
acquisition_string = string("a set of concatenated acquisition info")

display_string = string(
                    patient_string,
                    acquisition_string)

Textbox(
    text_panel_grid_layout[1, 1],
    bordercolor = :gray10, 
    bordercolor_focused = :gray10,
    bordercolor_hover = :gray10,
    boxcolor = :gray10,
    boxcolor_focused = :gray10,
    boxcolor_hover = :gray10,
    cursorcolor = :gray10,
    halign = :center, 
    displayed_string = display_string,
    stored_string = display_string,
    textcolor = :gray80,
    textpadding = (16, 16, 16, 16),
    space = :data,
    valign = :top) 

# Control widgets
#=
axis_controls_panel = 
    Axis(
        controls_grid_layout[:,1],
        backgroundcolor = :grey10)

hidedecorations!.(axis_controls_panel)
=#

interval_slider_window_width = 
    IntervalSlider(
        controls_grid_layout[1, 1], 
        range = LinRange(voxel_scalar_min:1.0:voxel_scalar_max),
        startvalues = (voxel_scalar_min, voxel_scalar_max),
        color_active = :orangered2,
        linewidth = 10.0)

slider_window_centre = 
        Slider( 
            controls_grid_layout[2, 1],
            color_active = :orangered2,
            linewidth = 10.0,
            range = LinRange(voxel_scalar_min:1.0:voxel_scalar_max), 
            startvalue = window_centre)

# To do: connect the above sliders

Box(controls_grid_layout[3, 1], color = :black)
1 Like

Try using Box for the background, that’s just a rectangle that shouldn’t interfere with slider responsiveness.

Your layout questions are just solved with more nested GridLayouts. One column with the slider labels, one column with the sliders and their additional labels in rows below. So 4 rows and 2 columns, with halign = :left for the two front labels and halign = :center for the other two.

And for the labels you just do Label where the input is lifted from a slider value attribute. Like Label(..., @lift(string($(slider.value))))

1 Like

Try using Box for the background, that’s just a rectangle that shouldn’t interfere with slider responsiveness.

Thank you for your suggestion. I had tried using Box in two different ways:

  1. Box first

Box(controls_grid_layout[1:2, 1], color = :grey10)

interval_slider_window_width = 
    IntervalSlider(
        controls_grid_layout[1, 1], 
        range = LinRange(voxel_scalar_min:1.0:voxel_scalar_max),
        startvalues = (voxel_scalar_min, voxel_scalar_max),
        color_active = :orangered2,
        linewidth = 10.0)

slider_window_centre = 
        Slider( 
            controls_grid_layout[2, 1],
            color_active = :orangered2,
            linewidth = 10.0,
            range = LinRange(voxel_scalar_min:1.0:voxel_scalar_max), 
            startvalue = window_centre)
  1. Box last.

interval_slider_window_width = 
    IntervalSlider(
        controls_grid_layout[1, 1], 
        range = LinRange(voxel_scalar_min:1.0:voxel_scalar_max),
        startvalues = (voxel_scalar_min, voxel_scalar_max),
        color_active = :orangered2,
        linewidth = 10.0)

slider_window_centre = 
        Slider( 
            controls_grid_layout[2, 1],
            color_active = :orangered2,
            linewidth = 10.0,
            range = LinRange(voxel_scalar_min:1.0:voxel_scalar_max), 
            startvalue = window_centre)

Box(controls_grid_layout[1:2, 1], color = :grey10)

The Sliders displays appear to overwrite the Box display and vice versa.

Is there a way to set Box to background?

It’s visible in the first example, just hard to see due to the sliders. You can wrap the sliders into another nested GridLayout with alignmode=Outside(padding) for a border

Quite right. Thanks for pointing this out.

Gave it a go.

I had overlooked this bit in the current documentation.

Two further questions:

  1. How can I decrease the space between the rows in the “2D Window Level” box?
    Tried rowgap! but without success.

  2. How can I set the width of the “2D Window Level” box so that it is the same width as the Textbox above it?

Code fragement:

# Figure for GLMakie display
noto_sans = assetpath("fonts", "NotoSans-Regular.ttf")

figure = Figure(
            backgroundcolor = :black,
            resolution = (1920, 1040), 
            font = noto_sans)

# Top level grid layout
top_level_grid_layout = figure[1:2, 1:3] = GridLayout()

# Control panel grid layout
control_panel_grid_layout = top_level_grid_layout[1:2, 1] = GridLayout()            
text_panel_grid_layout = control_panel_grid_layout[1, 1] = GridLayout()
controls_box_grid_layout = control_panel_grid_layout[2, 1] = GridLayout()
controls_grid_layout = controls_box_grid_layout[1, 1] = GridLayout()

# Display grid layout
displays_grid_layout = top_level_grid_layout[1:2, 2:3] = GridLayout()

# 3D image volume
volume_grid_layout = displays_grid_layout[1, 1]  = GridLayout()

# 2D image slice planes
axial_grid_layout = displays_grid_layout[1, 2]  = GridLayout()
sagittal_grid_layout = displays_grid_layout[2, 1]  = GridLayout()
coronal_grid_layout = displays_grid_layout[2, 2]  = GridLayout()

#
# Control Panel display
#

# Modality units
modality_units = string(" [AU]") # MR and NM default - arbitrary units

if image_modality == "CT"
    modality_units = string(" [HU]")

else image_modality == "PET"
    modality_units = string(" [SUV]")

end

#
# Infomation text box display
#

axis_text_panel = 
    Axis(
        text_panel_grid_layout[1,1],
        backgroundcolor = :grey10)

hidedecorations!.(axis_text_panel)

# Patient data
patient_string = string("concatenated patient info")

# Image acquisition data
acquisition_string = string("concatented acquisition info")

display_string = string(
                    patient_string,
                    acquisition_string)

Textbox( . . .) 

#
# Control widget display
#

# Box to contain the control widgets
Box(
    controls_box_grid_layout[1, 1], 
    color = :gray10)

control_padding = 16

# 2D Window level controls
Label(
    controls_grid_layout[1, 1][1, 1:2], 
    "2D Window Level",
    align = (:left, :center),
    alignmode = Outside(control_padding),
    color = :gray80,
    halign = :left,
    justification = :left,
    textsize = 16)

# 2D Window min level control
Label(
    controls_grid_layout[1, 1][2, 1], 
    "Min",
    align = (:left, :center),
    alignmode = Outside(control_padding),
    color = :gray80,
    halign = :left,
    justification = :left,
    textsize = 16)

slider_window_level_min = 
    Slider(
        controls_grid_layout[1, 1][2, 2], 
        range = LinRange(voxel_scalar_min:0.1:voxel_scalar_max),
        alignmode = Outside(control_padding),
        startvalues = window_level_min_start,
        color_active = :orangered2,
        halign = :center,
        linewidth = 10.0)

slider_window_level_min_label_text = 
    lift(slider_window_level_min.value) do value
        string(round.(value, digits = 5), modality_units)

    end

Label(
    controls_grid_layout[1, 1][3, 2], 
    slider_window_level_min_label_text,
    align = (:left, :center),
    alignmode = Outside(control_padding),
    color = :gray80,
    justification = :left,
    textsize = 16)

# Window level max control
Label(
    controls_grid_layout[2, 1][1, 1], 
    "Max",
    align = (:left, :center),
    alignmode = Outside(control_padding),
    color = :gray80,
    justification = :left,
    textsize = 16)

slider_window_level_max = 
        Slider( 
            controls_grid_layout[2, 1][1, 2],
            alignmode = Outside(control_padding),
            color_active = :orangered2,
            halign = :center,
            linewidth = 10.0,
            range = LinRange(voxel_scalar_min:0.1:voxel_scalar_max), 
            startvalue = window_level_max_start) 

slider_window_level_max_label_text = 
    lift(slider_window_level_max.value) do value
        string(round.(value, digits = 5), modality_units)

    end

Label(
    controls_grid_layout[2, 1][2, 2], 
    slider_window_level_max_label_text,
    align = (:left, :center),
    alignmode = Outside(control_padding),
    color = :gray80,
    justification = :center,
    textsize = 16)

# Box placeholder
Box(controls_grid_layout[3, 1], color = :black)

You seem to be using the outside alignmode with padding on every item, that introduces a lot of wasted space. I meant that you should use it on the container GridLayout so just the padding to the background edge is correct. The rest can be done with rowgap! / colgap! if you need to decrease gaps.

The lower panel will expand to full width if you give the two text labels below the sliders tellwidth = false, currently the box has the combined width of the labels in both columns. The sliders don’t have a known width so they don’t need this setting.

Now I understand.

# Control panel grid layout
control_padding = 16
control_panel_grid_layout = top_level_grid_layout[1, 1][1:4, 1] = GridLayout() 

text_panel_grid_layout = control_panel_grid_layout[1, 1] = GridLayout()

window_2D_box_level_menu_grid_layout = control_panel_grid_layout[2, 1] = GridLayout()
window_2D_level_menu_grid_layout = window_2D_box_level_menu_grid_layout[1, 1] = 
    GridLayout(
        alignmode = Outside(control_padding))

window_2D_box_level_sliders_grid_layout = control_panel_grid_layout[3, 1] = GridLayout()
window_2D_level_sliders_grid_layout = window_2D_box_level_sliders_grid_layout[1, 1] = 
    GridLayout(alignmode = Outside(control_padding))

Very nice.

Got it.

One question:

In the “2D Window Level” layout the Label “2D Window Level” spans two columns and the “Min”
and “Max” labels are in the first of two columns. How can I change the relative size of the two columns so that the first label column is more narrow thereby making the second column and sliders wider?

I found colsize!(f.layout, 1, Relative(2/3)) but did not have success in adapting it.

Code fragment:

# Box to contain the window level slider widgets
Box(
    window_2D_box_level_sliders_grid_layout[1, 1], 
    color = :gray10)

Label(
    window_2D_level_sliders_grid_layout[1, 1][1, 1:2], 
    "2D Window Level",
    align = (:left, :center),
    color = :gray80,
    halign = :left,
    justification = :left,
    textsize = 16)

# 2D Window min level control
Label(
    window_2D_level_sliders_grid_layout[1, 1][2, 1], 
    "Min   ",
    align = (:left, :center),
    color = :gray80,
    halign = :right,
    justification = :left,
    tellwidth = false,
    textsize = 16)

slider_window_level_min = 
    Slider(
        window_2D_level_sliders_grid_layout[1, 1][2, 2], 
        range = LinRange(voxel_scalar_min:0.1:voxel_scalar_max),
        startvalues = window_level_min_start,
        color_active = :orangered2,
        halign = :center,
        linewidth = 10.0)

slider_window_level_min_label_text = 
    lift(slider_window_level_min.value) do value
        string(round.(value, digits = 5), modality_units)

    end

Label(
    window_2D_level_sliders_grid_layout[1, 1][3, 2], 
    slider_window_level_min_label_text,
    align = (:left, :center),
    color = :gray80,
    justification = :left,
    tellwidth = false,
    textsize = 16)

# Window level max control
Label(
    window_2D_level_sliders_grid_layout[2, 1][1, 1], 
    "Max   ",
    align = (:left, :center),
    color = :gray80,
    justification = :left,
    halign = :right,
    tellwidth = false,
    textsize = 16)

slider_window_level_max = 
        Slider( 
            window_2D_level_sliders_grid_layout[2, 1][1, 2],
            color_active = :orangered2,
            halign = :center,
            linewidth = 10.0,
            range = LinRange(voxel_scalar_min:0.1:voxel_scalar_max), 
            startvalue = window_level_max_start) 

slider_window_level_max_label_text = 
    lift(slider_window_level_max.value) do value
        string(round.(value, digits = 5), modality_units)

    end

Label(
    window_2D_level_sliders_grid_layout[2, 1][2, 2], 
    slider_window_level_max_label_text,
    align = (:left, :center),
    color = :gray80,
    justification = :center,
    tellwidth = false,
    textsize = 16)

# Box placeholder
Box(control_panel_box_placeholder_grid_layout[1, 1], color = :black)

now you’ve set “Min” and “Max” to tellwidth = false as well, but you do want the first column to auto-adjust to their size. You don’t want the sliders to shrink to their labels below. So tellwidth false for the right labels, tellwidth true for the left.

Got it. It’s a wrap for my current Makie layout questions. Thank you for your replies and advice.

1 Like