# Part of ImGui Bundle - MIT License - Copyright (c) 2022-2026 Pascal Thomet - https://github.com/pthom/imgui_bundle import os import sys from imgui_bundle._imgui_bundle import __bundle_submodules_available__, __bundle_submodules_disabled__, __bundle_pyodide__ # type: ignore from imgui_bundle._imgui_bundle import __version__, __build_number__, compilation_time from types import ModuleType from typing import Union, Tuple, List, overload def has_submodule(submodule_name: str) -> bool: return submodule_name in __bundle_submodules_available__ def _publish(name: str, module: ModuleType) -> None: """Expose a native submodule under `imgui_bundle.` in sys.modules. Enables `from imgui_bundle. import ...` in addition to the existing `from imgui_bundle import `. Without this, the native module's fully-qualified name stays `imgui_bundle._imgui_bundle.`, which is what `sys.modules` keys on — the public path isn't registered. """ sys.modules[f"imgui_bundle.{name}"] = module def info() -> str: """Return information about imgui_bundle: version, compilation time, and available submodules. Returns: str: Formatted string with package information Example: >>> import imgui_bundle >>> print(imgui_bundle.info()) ImGui Bundle v1.92.6 Compiled on Jan 26 2026 at 15:49:15 Available submodules (16): imgui, imgui.internal, imgui.backends, ... """ comp_time = compilation_time() # Format submodules list submodules_str = ", ".join(__bundle_submodules_available__) info_str = f"ImGui Bundle v{__version__} buil {__build_number__}\n" info_str += f"{comp_time}\n" info_str += f"Available submodules ({len(__bundle_submodules_available__)}): {submodules_str}\n" if len(__bundle_submodules_disabled__) > 0: disabled_str = ", ".join(__bundle_submodules_disabled__) info_str += f"Disabled submodules ({len(__bundle_submodules_disabled__)}): {disabled_str}" return info_str def _is_pydantic_v2_available() -> bool: from importlib import metadata try: version_str: str = metadata.version("pydantic") except metadata.PackageNotFoundError: return False major: int = int(version_str.split(".")[0]) return major >= 2 __all__ = [ "__version__", "__build_number__", "compilation_time", "info", "has_submodule", "__bundle_submodules_available__", "__bundle_submodules_disabled__", "__bundle_pyodide__" ] # # Import native submodules # if has_submodule("imgui"): from imgui_bundle._imgui_bundle import imgui as imgui _publish("imgui", imgui) # Let Python resolve `imgui_bundle.imgui.` against the source # dir (e.g. test_engine_checks.py), since the C++ submodule shadows it. imgui.__path__ = [os.path.join(os.path.dirname(__file__), "imgui")] _publish("imgui.internal", imgui.internal) _publish("imgui.backends", imgui.backends) if has_submodule("imgui.test_engine"): _publish("imgui.test_engine", imgui.test_engine) # Catch Python exceptions in test_func/gui_func/teardown_func so they # become logged test failures instead of nanobind::python_error -> # std::terminate (which would crash the whole process). from imgui_bundle import _imgui_test_engine_python_safety # noqa: F401 _imgui_test_engine_python_safety.install(imgui.test_engine) from imgui_bundle._imgui_bundle.imgui import ImVec2, ImVec4, ImColor, FLT_MIN, FLT_MAX # noqa: F401 from imgui_bundle.im_col32 import IM_COL32 # noqa: F401, E402 from imgui_bundle import imgui_ctx as imgui_ctx # noqa: E402 ImVec2Like = Union[ImVec2, Tuple[int | float, int | float], List[int | float]] ImVec4Like = Union[ImVec4, Tuple[int | float, int | float, int | float, int | float], List[int | float]] imgui.ImVec2Like = ImVec2Like imgui.ImVec4Like = ImVec4Like __all__.extend([ "imgui", "ImVec2", "ImVec2Like", "ImVec4Like", "ImVec4", "ImColor", "FLT_MIN", "FLT_MAX", "IM_COL32", "imgui_ctx", ]) # Em sizing utilities (DPI-independent sizing) def em_size(v: float = 1.0) -> float: """Returns a size in pixels corresponding to `v` em units. 1 em = current font size (ImGui::GetFontSize()). Use this for DPI-independent sizing. """ return imgui.get_font_size() * v @overload def em_to_vec2(x: float, y: float) -> ImVec2: ... @overload def em_to_vec2(v: ImVec2Like) -> ImVec2: ... def em_to_vec2(x, y=None) -> ImVec2: """Returns an ImVec2 sized in em units (multiples of font size). Can be called as: em_to_vec2(3.0, 2.0) -> ImVec2 of 3em x 2em em_to_vec2((3.0, 2.0)) -> same, from tuple em_to_vec2(ImVec2(3.0, 2.0)) -> same, from ImVec2 """ font_size = imgui.get_font_size() if y is None: return ImVec2(font_size * x[0], font_size * x[1]) return ImVec2(font_size * x, font_size * y) __all__.extend(["em_size", "em_to_vec2"]) # Patch after imgui v1.90.9, where # the enum ImGuiDir_ was renamed to ImGuiDir and ImGuiSortDirection_ was renamed to ImGuiSortDirection # this enables old python to continue to work imgui.Dir_ = imgui.Dir imgui.SortDirection_ = imgui.SortDirection # Pydantic types are lazily imported via __getattr__ below, # so that pydantic can be installed after imgui_bundle is first imported # (e.g. in Pyodide where packages are installed incrementally). __all__.extend([ "imgui_pydantic", "ImVec4_Pydantic", "ImVec2_Pydantic", "ImColor_Pydantic", ]) if has_submodule("hello_imgui"): from imgui_bundle._imgui_bundle import hello_imgui as hello_imgui _publish("hello_imgui", hello_imgui) __all__.extend(["hello_imgui"]) if has_submodule("implot"): from imgui_bundle._imgui_bundle import implot as implot _publish("implot", implot) __all__.extend(["implot"]) # Flag types for ImPlot implot.LineFlags = int # see implot.LineFlags_ implot.ScatterFlags = int # see implot.ScatterFlags_ implot.StairsFlags = int # see implot.StairsFlags_ implot.ShadedFlags = int # see implot.ShadedFlags_ implot.BarsFlags = int # see implot.BarsFlags_ implot.PieChartFlags = int # see implot.PieChartFlags_ implot.HistogramFlags = int # see implot.HistogramFlags_ if has_submodule("implot3d"): from imgui_bundle._imgui_bundle import implot3d as implot3d _publish("implot3d", implot3d) __all__.extend(["implot3d"]) if has_submodule("imgui_color_text_edit"): from imgui_bundle._imgui_bundle import imgui_color_text_edit as imgui_color_text_edit _publish("imgui_color_text_edit", imgui_color_text_edit) __all__.extend(["imgui_color_text_edit"]) if has_submodule("imgui_node_editor"): from imgui_bundle._imgui_bundle import imgui_node_editor as imgui_node_editor _publish("imgui_node_editor", imgui_node_editor) from imgui_bundle import imgui_node_editor_ctx as imgui_node_editor_ctx # noqa: E402 __all__.extend(["imgui_node_editor", "imgui_node_editor_ctx"]) if has_submodule("imgui_knobs"): from imgui_bundle._imgui_bundle import imgui_knobs as imgui_knobs _publish("imgui_knobs", imgui_knobs) __all__.extend(["imgui_knobs"]) if has_submodule("im_file_dialog"): from imgui_bundle._imgui_bundle import im_file_dialog as im_file_dialog _publish("im_file_dialog", im_file_dialog) __all__.extend(["im_file_dialog"]) if has_submodule("imspinner"): from imgui_bundle._imgui_bundle import imspinner as imspinner _publish("imspinner", imspinner) __all__.extend(["imspinner"]) if has_submodule("imgui_md"): from imgui_bundle._imgui_bundle import imgui_md as imgui_md _publish("imgui_md", imgui_md) __all__.extend(["imgui_md"]) # Register a hook so that initialize_markdown() automatically sets up URL image download support def _on_initialize_markdown(options): if options.callbacks.on_download_data is None: from imgui_bundle._imgui_md_image_loader import _get_download_function options.callbacks.on_download_data = _get_download_function() imgui_md._set_on_initialize_markdown_callback(_on_initialize_markdown) if has_submodule("immvision"): from imgui_bundle._imgui_bundle import immvision as immvision _publish("immvision", immvision) __all__.extend(["immvision"]) if has_submodule("imguizmo"): from imgui_bundle._imgui_bundle import imguizmo as imguizmo _publish("imguizmo", imguizmo) __all__.extend(["imguizmo"]) if has_submodule("imgui_tex_inspect"): from imgui_bundle._imgui_bundle import imgui_tex_inspect as imgui_tex_inspect _publish("imgui_tex_inspect", imgui_tex_inspect) __all__.extend(["imgui_tex_inspect"]) if has_submodule("imgui_toggle"): from imgui_bundle._imgui_bundle import imgui_toggle as imgui_toggle _publish("imgui_toggle", imgui_toggle) __all__.extend(["imgui_toggle"]) if has_submodule("portable_file_dialogs"): from imgui_bundle._imgui_bundle import portable_file_dialogs as portable_file_dialogs _publish("portable_file_dialogs", portable_file_dialogs) __all__.extend(["portable_file_dialogs"]) if has_submodule("imgui_command_palette"): from imgui_bundle._imgui_bundle import imgui_command_palette as imgui_command_palette _publish("imgui_command_palette", imgui_command_palette) __all__.extend(["imgui_command_palette"]) if has_submodule("imcoolbar"): from imgui_bundle._imgui_bundle import im_cool_bar as im_cool_bar _publish("im_cool_bar", im_cool_bar) __all__.extend(["im_cool_bar"]) if has_submodule("nanovg"): from imgui_bundle._imgui_bundle import nanovg as nanovg _publish("nanovg", nanovg) __all__.extend(["nanovg"]) if has_submodule("im_anim"): from imgui_bundle._imgui_bundle import im_anim as im_anim _publish("im_anim", im_anim) __all__.extend(["im_anim"]) if has_submodule("imgui_explorer"): from imgui_bundle._imgui_bundle import imgui_explorer as imgui_explorer _publish("imgui_explorer", imgui_explorer) __all__.extend(["imgui_explorer"]) if has_submodule("imgui_microtex"): from imgui_bundle._imgui_bundle import imgui_microtex as imgui_microtex _publish("imgui_microtex", imgui_microtex) __all__.extend(["imgui_microtex"]) if has_submodule("webgl"): from imgui_bundle._imgui_bundle import webgl as webgl _publish("webgl", webgl) __all__.extend(["webgl"]) if has_submodule("immapp_cpp"): # immapp is a Python wrapper around immapp_cpp from imgui_bundle import immapp as immapp # Note: to enable font awesome 6: # runner_params.callbacks.default_icon_font = hello_imgui.DefaultIconFont.font_awesome6 from imgui_bundle.immapp import icons_fontawesome_4 as icons_fontawesome_4 from imgui_bundle.immapp import icons_fontawesome_4 as icons_fontawesome # v4 # noqa: F401 from imgui_bundle.immapp import icons_fontawesome_6 as icons_fontawesome_6 __all__.extend(["immapp", "icons_fontawesome_4", "icons_fontawesome", "icons_fontawesome_6"]) # # Import Python submodules # if has_submodule("immvision"): from imgui_bundle import imgui_fig as imgui_fig __all__.extend(["imgui_fig"]) # Glfw setup: # By importing imgui_bundle.glfw_utils, we make sure that glfw provided by pip will use our glfw dynamic library. # (imgui_bundle.glfw_utils will call _set_glfw_pip_search_path automatically) if has_submodule("with_glfw"): from imgui_bundle._glfw_set_search_path import _glfw_set_search_path _glfw_set_search_path() from imgui_bundle import glfw_utils as glfw_utils # noqa: E402 # # Pyodide: patch hello_imgui.run and immapp.run to work with Pyodide # if __bundle_pyodide__ and has_submodule("hello_imgui") and has_submodule("immapp_cpp"): from imgui_bundle.pyodide_patch_runners import pyodide_do_patch_runners pyodide_do_patch_runners() # Jupyter notebook: patch hello_imgui.run and immapp.run to work with Jupyter notebook # run will display a screenshot of the final app state in the notebook output # Note: the immapp.nb submodule provides more complete notebook support (async support, etc.) if has_submodule("hello_imgui") and has_submodule("immapp_cpp") and not __bundle_pyodide__: from imgui_bundle.notebook_patch_runners import notebook_do_patch_runners_if_needed # noqa: E402 notebook_do_patch_runners_if_needed() from imgui_bundle._patch_runners_add_save_screenshot_param import patch_runners_add_save_screenshot_param # noqa: E402 patch_runners_add_save_screenshot_param() # # Add async support to hello_imgui # if has_submodule("hello_imgui"): from imgui_bundle.hello_imgui_run_async import run_async as _hello_imgui_run_async hello_imgui.run_async = _hello_imgui_run_async # type: ignore # Add notebook convenience API from imgui_bundle import hello_imgui_nb as _hello_imgui_nb_module hello_imgui.nb = _hello_imgui_nb_module # type: ignore THIS_DIR = os.path.dirname(__file__) hello_imgui.override_assets_folder(THIS_DIR + "/assets") def register_demos_assets_folder() -> None: """Add demos_assets/ to hello_imgui assets search path. Call this from demos that need additional images (house.jpg, bear_transparent.png, etc.) beyond the default assets (world.png, fonts).""" from imgui_bundle import __bundle_pyodide__ import os as _os import logging as _logging _log = _logging.getLogger("imgui_bundle") if __bundle_pyodide__: _log.warning("register_demos_assets_folder will not work in Pyodide") return _demos_assets = _os.path.join(_os.path.dirname(__file__), "demos_assets") _demos_assets = _os.path.normpath(_demos_assets) if _os.path.isdir(_demos_assets): from imgui_bundle import hello_imgui as _hello_imgui _hello_imgui.add_assets_search_path(_demos_assets) _log.debug("Registered demos assets folder: %s", _demos_assets) else: _log.warning("demos_assets/ not found at %s", _demos_assets) __all__.extend(["register_demos_assets_folder"]) # Lazy import for pydantic types: deferred so that pydantic can be installed # after imgui_bundle is first imported (e.g. in Pyodide). _PYDANTIC_NAMES = {"ImVec2_Pydantic", "ImVec4_Pydantic", "ImColor_Pydantic", "imgui_pydantic"} def __getattr__(name: str): if name in _PYDANTIC_NAMES: if not _is_pydantic_v2_available(): raise ImportError( f"'{name}' requires pydantic v2+. Install it with: pip install pydantic" ) import importlib _mod = importlib.import_module("imgui_bundle.imgui_pydantic") # Cache into module globals so __getattr__ is not called again globals()["imgui_pydantic"] = _mod globals()["ImVec2_Pydantic"] = _mod.ImVec2_Pydantic globals()["ImVec4_Pydantic"] = _mod.ImVec4_Pydantic globals()["ImColor_Pydantic"] = _mod.ImColor_Pydantic return globals()[name] raise AttributeError(f"module 'imgui_bundle' has no attribute {name!r}")