DearPyGui icon indicating copy to clipboard operation
DearPyGui copied to clipboard

How to set height for listbox

Open h5rdly opened this issue 11 months ago • 12 comments

Hi,

I'm trying to make a template for a two pane file manager, but I can't seem to stretch the listbox all the way down.

I have -

import ctypes, platform
import dearpygui.dearpygui as dpg
from subprocess import Popen, PIPE


dpg.create_context()
dpg.set_global_font_scale(3)
# dpg.show_style_editor()

if platform.system() == "Windows":
    user32 = ctypes.windll.user32
    screen_width, screen_height = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)
elif platform.system() == "Linux":
    output = Popen('xrandr | grep "\*" | awk \'{print $1}\'',shell=True, stdout=PIPE).communicate()[0]
    resolution = output.strip().split(b'x')
    screen_width, screen_height = int(resolution[0]), int(resolution[1].split(b'\n')[0])
else:
    screen_width, screen_height = 800, 600


dpg.create_viewport(title='Custom Title', width=screen_width, height=screen_height)

with dpg.window(label='Dual Pane File Manager', width=screen_width, height=screen_height):
    with dpg.group(): #optional group for better layout
        dpg.add_text('Left Panel')
        with dpg.child_window(width=screen_width // 2, height = screen_height - 40):
            listbox_tag = dpg.generate_uuid()
            listbox = dpg.add_listbox(items=('check', '1', '2', '3', '4'), 
                            tag=listbox_tag) 
    # dpg.set_item_height(listbox, screen_height - 40)

    
# dpg.set_item_height(listbox_tag, screen_height - 40)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

It seems that listbox doesn't support height, and I was unable to make dpg.set_item_height() work either.

Is there a way to make a listbox use up all the height available by its container?

Thanks, Eli

h5rdly avatar Jan 25 '25 11:01 h5rdly

Hi,

There is lisbox's paramenter: num_items.

I would specify width and num_items in a listbox, then specify autosize in window and auto_resize_x, auto_resize_y in child_window.

import dearpygui.dearpygui as dpg

dpg.create_context()
dpg.create_viewport()

with dpg.window(autosize=True):
    with dpg.group():
        dpg.add_text("Left Panel")
        with dpg.child_window(auto_resize_x=True, auto_resize_y=True):
            dpg.add_listbox(items=("0 - check", "1", "2", "3", "4"), num_items=10, width=300)

dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

nvglucifer avatar Jan 25 '25 14:01 nvglucifer

Much obliged, using num_items makes the listbox fan out height-wise.

Could you perhaps advise - trying to add navigation using the up and down arrow. I got -

import dearpygui.dearpygui as dpg

selected_index = 0

def on_key_press(sender, data):

    global selected_index
    if dpg.is_key_pressed(dpg.mvKey_Up) and selected_index > 0:
        # dpg.set_item_focus(listbox, selected_index - 1)
        selected_index -= 1
        dpg.set_value(listbox_tag, items[selected_index])
    elif dpg.is_key_pressed(dpg.mvKey_Down) and selected_index < dpg.get_item_count(listbox) - 1:
        # dpg.set_item_focus(listbox, selected_index + 1)
        selected_index += 1
        dpg.set_value(listbox_tag, items[selected_index])


screen_width, screen_height = get_screen_resolution()

dpg.create_context()
dpg.set_global_font_scale(3)
dpg.create_viewport(width=screen_width, height=screen_height)

items = 'check', '1', '2', '3', '4'
with dpg.window(label='Dual Pane File Manager', height=screen_height,): 
    with dpg.group(): 
        dpg.add_text('Left Panel')
        with dpg.child_window(width=screen_width // 2):
            listbox = dpg.add_listbox(items=items, num_items=30,
                            default_value=items[selected_index], callback=on_key_press) 
    
# dpg.add_key_press_handler(callback=on_key_press)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

Can't seem to get the arrow keys to work for navigating the listbox. What am I missing?

h5rdly avatar Jan 27 '25 01:01 h5rdly

I think dpg.is_key_pressed/released are meant to be used with handler/item_handler, except is_key_down can be queried in normal callback.

import dearpygui.dearpygui as dpg

dpg.create_context()
dpg.create_viewport()

listbox_tag = dpg.generate_uuid()
items = ["0 - check", "1", "2 - second", "3", "4"]


def listbox_cb(s, a, u):
    print(f"holding LControl: {dpg.is_key_down(dpg.mvKey_LControl)}, listbox value: {dpg.get_value(listbox_tag)}")
    

# def listbox_focused_cb(s, a, u):  # mouse down at the listbox
# def listbox_key pressed-released-down ...
def listbox_visible_cb(s, a, u):

    listbox_cfg = dpg.get_item_configuration(listbox_tag)
    num_items = listbox_cfg["num_items"]
    curr_val = dpg.get_value(listbox_tag)
    idx = items.index(curr_val)

    if not dpg.is_item_hovered(listbox_tag):
        return
    else:  # doing something even the listbox not focusing
        pass
    
    if dpg.is_key_down(dpg.mvKey_LControl):
        if dpg.is_key_released(dpg.mvKey_E):  # increasing listbox height
            # (not ideal) or is_key_down that it will call several frames

            dpg.configure_item(listbox_tag, num_items=num_items + 1)
            # listbox_cb(s, a, u)

        if dpg.is_key_released(dpg.mvKey_Q):  # decreasing listbox height
            dpg.configure_item(listbox_tag, num_items=num_items - 1)
            # listbox_cb(s, a, u)

    if dpg.is_item_hovered(listbox_tag):
        len_items = len(items)

        if dpg.is_key_released(dpg.mvKey_Up):
            decrease = idx - 1
            if decrease + 1 == 0:
                return
            dpg.configure_item(listbox_tag, default_value=items[decrease])
            listbox_cb(s, a, u)

        if dpg.is_key_released(dpg.mvKey_Down):
            increase = idx + 1
            if increase + 1 > len_items:
                return
            dpg.configure_item(listbox_tag, default_value=items[increase])
            listbox_cb(s, a, u)

    # # better remove this section, use normal callback.
    # # because it prints previous frame value.
    # if dpg.is_mouse_button_clicked(dpg.mvMouseButton_Left) and dpg.is_item_hovered(listbox_tag):
    #     listbox_cb(s, a, u)
    #     dpg.split_frame()
    #     print(f"left clicked! listbox selected: {dpg.get_value(listbox_tag)}")


with dpg.window(autosize=True):
    with dpg.group():
        dpg.add_text("Left Panel")
        with dpg.child_window(auto_resize_x=True, auto_resize_y=True):
            dpg.add_listbox(
                items=items,
                default_value="2 - second",
                callback=listbox_cb, # above concern
                num_items=10,
                width=300,
                tag=listbox_tag,
            )

with dpg.item_handler_registry() as listbox_handler_reg:
    dpg.add_item_visible_handler(callback=listbox_visible_cb)
    dpg.bind_item_handler_registry(listbox_tag, listbox_handler_reg)

dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

nvglucifer avatar Jan 27 '25 12:01 nvglucifer

Again, much obliged!

Managed to make up / down work based on your example -

import dearpygui.dearpygui as dpg


dpg.create_context()
dpg.set_global_font_scale(3)
screen_width, screen_height = get_screen_resolution()  # Omitted for brevity
dpg.create_viewport(width=screen_width, height=screen_height)

items = 'check', '1', '2', '3', '4'

with dpg.window(label='Dual Pane File Manager', height=screen_height,): 
    with dpg.group(): 
        dpg.add_text('Left Panel')
        with dpg.child_window(width=screen_width // 2):
            listbox_tag = dpg.generate_uuid()
            listbox = dpg.add_listbox(items=items, num_items=30, default_value='check', tag=listbox_tag) 
            listbox_cfg = dpg.get_item_configuration(listbox)


# Up / Down arrow scrolling
def listbox_scroll_cb(s, a, u):

    idx = items.index(dpg.get_value(listbox))
    if dpg.is_key_released(dpg.mvKey_Up):
        if idx < 1:
            return
        dpg.configure_item(listbox, default_value=items[idx - 1])

    if dpg.is_key_released(dpg.mvKey_Down):
        if idx > len(items) -2:
            return
        dpg.configure_item(listbox, default_value=items[idx + 1])


with dpg.item_handler_registry() as listbox_handler_reg:
    dpg.add_item_visible_handler(callback=listbox_scroll_cb)
    dpg.bind_item_handler_registry(listbox, listbox_handler_reg)


dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

I had to remove the if dpg.is_item_hovered(listbox): pre-condition, as this would only let me scroll when the mouse was already over one of the items.

h5rdly avatar Feb 01 '25 12:02 h5rdly

How to set height without using num_items as that value could be variable ?

IvRogoz avatar May 06 '25 10:05 IvRogoz

@IvRogoz,

with dpg.window(label="demo", autosize=True):
    dpg.add_listbox(("A","B","C"),tag="listbox")

    # might need to define separate callback instead of lambda for replacing
    # user_data with your dict["abcxyz"] or class_instance.variable_abcxyz,
    dpg.add_button(
        label="increase listbox's height",
        user_data=3,  # default num_items of listbox if not specified in listbox above
        callback=lambda sender, app_data, user_data: [
            dpg.configure_item("listbox", num_items=user_data+1),
            dpg.set_item_user_data(sender, user_data+1)
        ]
    )

    default_height = 90  # it will become 80 =4*20 as num_items=4 fits in 90
    dpg.add_button(
        label="reset listbox's height",
        callback=lambda s, a, u: dpg.configure_item("listbox", num_items=int(default_height/20)),
        # the number 20 might vary if you change something relate to its text (font, size. padding...)
        
        # TODO: modify the previous button's user_data
    )

nvglucifer avatar May 06 '25 10:05 nvglucifer

Thank you. It works but its not the same as having a height=-1 regardless of number of items.

IvRogoz avatar May 06 '25 11:05 IvRogoz

@IvRogoz, In that case, you need to get the height or rect_size of listbox's parent (group, child_window, window,...), for computing the listbox's num_items.

or make your own that comprise a child_window(height=-1), checkboxes, selectable,...

nvglucifer avatar May 06 '25 12:05 nvglucifer

I met such a problem as well. I cannot set a height to listbox. I follow here.

gokhanergen-tech avatar May 07 '25 11:05 gokhanergen-tech

DPG uses ImGui::ListBox https://github.com/hoffstadt/DearPyGui/blob/d3577817fa69d6b5a8b56fde023b0200c0ff83ef/src/mvBasicWidgets.cpp#L4365 Which originates from https://github.com/ocornut/imgui/blob/139e99ca37a3e127c87690202faec005cd892d36/imgui_widgets.cpp#L8152-L8197

ImGui::ListBox seem abstracted away the ImGui::BeginListBox() that the size is distinguished (height_in_items vs frame_bb) https://github.com/ocornut/imgui/blob/139e99ca37a3e127c87690202faec005cd892d36/imgui_widgets.cpp#L8105-L8110 https://github.com/ocornut/imgui/issues/8194

Also, notice the float height_in_items_f = height_in_items + 0.25f; It explains why you will have redundant pixels at the bottom (num_items=33, there redundant of 33*0.25=8.25 pixels)

For me, it kind of mutually exclusive: num_items and height can't be used altogether. If there is height param, we have to ditch num_items (that someone comfortably using)?

nvglucifer avatar May 07 '25 16:05 nvglucifer

@IvRogoz, In that case, you need to get the height or rect_size of listbox's parent (group, child_window, window,...), for computing the listbox's num_items.

or make your own that comprise a child_window(height=-1), checkboxes, selectable,...

https://github.com/ocornut/imgui/blob/139e99ca37a3e127c87690202faec005cd892d36/imgui_widgets.cpp#L8091

nvglucifer avatar May 07 '25 16:05 nvglucifer

Also, notice the float height_in_items_f = height_in_items + 0.25f; It explains why you will have redundant pixels at the bottom (num_items=33, there redundant of 33*0.25=8.25 pixels)

I was wrong about a point, It actually 33.25 plus redundant,

The redundant at the bottom of listbox is because of + g.Style.FramePadding.y * 2.0f https://github.com/ocornut/imgui/blob/139e99ca37a3e127c87690202faec005cd892d36/imgui_widgets.cpp#L8162

nvglucifer avatar May 08 '25 00:05 nvglucifer