Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Dear ImGui

Dear ImGui is the foundation of ImGui Bundle. It’s a bloat-free graphical user interface library for C++ that outputs optimized vertex buffers for rendering.

ImGui Manual

The best way to learn Dear ImGui is through the interactive ImGui Manual:

ImGui Manual - Interactive reference for Dear ImGui

ImGui Manual - Interactive reference for Dear ImGui

Launch the ImGui Manual

The manual lets you:

Basic Usage

Python
C++
from imgui_bundle import imgui, immapp

value = 0.5
checked = False
text = "Hello"

def gui():
    global value, checked, text

    imgui.text("Hello, world!")

    if imgui.button("Click me"):
        print("Button clicked!")

    changed, value = imgui.slider_float("Value", value, 0.0, 1.0)
    changed, checked = imgui.checkbox("Enable", checked)
    changed, text = imgui.input_text("Name", text)

immapp.run(gui, window_title="ImGui Demo")

Key Concepts

Immediate Mode

ImGui uses an immediate mode paradigm: you call widget functions every frame, and they return whether they were interacted with.

Python
C++
# The button returns True when clicked
if imgui.button("Save"):
    save_file()

# Sliders return (changed, new_value)
changed, value = imgui.slider_float("Speed", value, 0.0, 100.0)
if changed:
    update_speed(value)

Widget IDs

ImGui identifies widgets by their label. You shall not have two widgets with the same label in the same scope.

Either use ## to add a hidden ID suffix:

imgui.button("OK")
imgui.button("OK##dialog2")   # Will be displayed as "Ok", but is different from "OK##dialog2"

Or add a scope using push_id()/pop_id():

for i in range(3):
    imgui.push_id(i)
    imgui.button("Button")  # IDs are "0/Button", "1/Button", "2/Button"
    imgui.pop_id()

Begin/End Pairs

Many ImGui functions come in pairs:

Python
C++
if imgui.begin_menu("File"):
    if imgui.menu_item_simple("Open"):
        open_file()
    # you should call `end_*` after `begin_*`, if the `begin_*` returned `True`.
    imgui.end_menu()

# Note: begin() is an exception, always call end(), even if begin() returned False
# begin() returns two values: (expanded, opened); opened is the state of the close button, if any
# (begin_simple("My Window") returns expanded alone, for a window without a close button)
expanded, _ = imgui.begin("My Window")
if expanded:
    imgui.text("Content here")
imgui.end()  # Always call end!

Common Patterns

App State Management

Keep your application state outside the GUI function:

# Good: State in a class or module-level variables
class AppState:
    counter = 0
    name = ""

state = AppState()

def gui():
    if imgui.button("Increment"):
        state.counter += 1
    _, state.name = imgui.input_text("Name", state.name)

Conditional Widgets

Remember that ImGui widgets only exist when rendered:

# Widget only exists when show_advanced is True
if show_advanced:
    _, advanced_value = imgui.slider_float("Advanced", advanced_value, 0, 1)

Layout with same_line

Use same_line() to place widgets horizontally:

imgui.button("One")
imgui.same_line()
imgui.button("Two")
imgui.same_line()
imgui.button("Three")

Horizontal and vertical layouts (StackLayout)

The bundle’s Dear ImGui includes StackLayout (by thedmd): begin_horizontal() / end_horizontal(), begin_vertical() / end_vertical(), and spring(), which takes the free space.

Prefer it to same_line():

Python
C++
imgui.begin_horizontal("toolbar", imgui.ImVec2(imgui.get_content_region_avail().x, 0.0))
imgui.button("Left")
imgui.button("Also Left")
imgui.spring()  # pushes the next items to the right
imgui.text("Right-aligned")
imgui.end_horizontal()

DPI-Aware Sizing (Basic)

Avoid hardcoded pixel sizes for portable UIs. Use sizes relative to the font:

font_size = imgui.get_font_size()
imgui.button("Click", imgui.ImVec2(font_size * 8, font_size * 2))

Documentation

See Also