This example is based on the addition of ImCoolBar, which was added in Oct 2023.
Step 1: Reference the new library¶
Step 1-a: Add needed folders, files and submodules inside external/¶
Add the library as a submodule in external/lib_name/lib_name¶
If the library can be included without adaptations for inclusion inside ImGui Bundle, you can add it directly as a submodule.
mkdir external/ImCoolBar
git submodule add https://github.com/aiekick/ImCoolBar.git external/ImCoolBar/ImCoolBarHowever, if it requires adaptations, you need to create a fork (it was the case for ImCoolBar): So, the following actions were done separately:
ImCoolBar was cloned into github
.com /pthom /ImCoolBar .git a branch imgui_bundle was created and pushed to github. It will contain the adaptations and bug corrections for imgui_bundle.
Then, we add this fork as a submodule.
git submodule add https://github.com/pthom/ImCoolBar.git external/ImCoolBar/ImCoolBar
cd external/ImCoolBar/ImCoolBar
git checkout imgui_bundle
cd -Create the folder external/lib_name/bindings/¶
Copy the folder external/bindings_generation/bindings_generator_template into external/lib_name/bindings/
cp -r external/bindings_generation/bindings_generator_template external/ImCoolBar/bindingsRename files in external/lib_name/bindings¶
After having copied the template files, we need to rename them. In the example of ImCoolbar, we will rename them as follows:
mv external/ImCoolBar/bindings/generate_LIBNAME.py external/ImCoolBar/bindings/generate_imcoolbar.py
mv external/ImCoolBar/bindings/pybind_LIBNAME.cpp external/ImCoolBar/bindings/pybind_imcoolbar.cpp
# im_cool_bar will be the final name of the python module: imgui_bundle.im_cool_bar
mv external/ImCoolBar/bindings/LIBNAME.pyi external/ImCoolBar/bindings/im_cool_bar.pyiMove external/ImCoolBar/bindings/im_cool_bar.pyi to bindings/imgui_bundle/¶
The stub file (*.pyi) must be inside bindings/imgui_bundle. In order to facilitate development, we will create a symlink to it inside external/ImCoolBar/bindings/
mv external/ImCoolBar/bindings/im_cool_bar.pyi bindings/imgui_bundle/im_cool_bar.pyi
cd external/ImCoolBar/bindings/
ln -s ../../../bindings/imgui_bundle/im_cool_bar.pyi .
cd -Final folder structure¶
We end up with the following structure:
external/ImCoolBar/
├── ImCoolBar/ # Note that the submodule is inside
│ ├── CMakeLists.txt # external/ImCoolBar/ImCoolBar/ !!!
│ ├── ImCoolbar.cpp
│ ├── ImCoolbar.h
│ ├── LICENSE
│ └── README.md
└── bindings/
├── im_cool_bar.pyi # We will edit and rename those files later
├── generate_imcoolbar.py -> symlink to ../../../bindings/imgui_bundle/im_cool_bar.pyi
└── pybind_imcoolbar.cppStep 1-b: Update python generator manager¶
Update external/bindings_generation/all_external_libraries.py
Add a function that returns info about this new library:
def lib_imcoolbar() -> ExternalLibrary:
return ExternalLibrary(
name="ImCoolBar",
official_git_url="https://github.com/aiekick/ImCoolBar.git",
official_branch="master",
fork_git_url="https://github.com/pthom/ImCoolBar.git",
fork_branch="imgui_bundle"
)ALL_LIBS = [
lib_imgui(), # must be first as it declare bindings used by the next ones
# ...
lib_imcoolbar(), # Add the lib here
# ...Step 1-c: Update the C++ sources to include the new lib binding generation¶
In external/CMakeLists.txt: Add a cmake directive to compile the new library.
# If the library is "simple" to compile you can use `add_simple_external_library_with_sources`
add_simple_external_library_with_sources(imcoolbar ImCoolBar)In external/bindings_generation/cpp/all_pybind_files.cmake:
add external/ImCoolBar/bindings/pybind_imcoolbar.cpp
In external/bindings_generation/cpp/pybind_imgui_bundle.cpp:
Add the bindings
// ... Near the start of the file, add a new function declaration
void py_init_module_imgui_command_palette(py::module& m);
void py_init_module_implot_internal(py::module& m);
void py_init_module_imcoolbar(py::module& m); // added this line
// ...
void py_init_module_imgui_bundle(py::module& m)
{
// ...
// At the end of py_init_module_imgui_bundle, register your new python module
auto module_imcooolbar = m.def_submodule("im_cool_bar"); // the python module will be known as imgui_bundle.im_cool_bar
py_init_module_imcoolbar(module_imcooolbar);Now, run cmake.
Step 1-d: Edit and adapt the generation scripts¶
Edit the 3 files inside external/ImCoolBar/bindings and replace occurrences of LIBNAME with appropriate values.
Step 1-e: Edit and adapt the imgui_bundle init scripts¶
In bindings/imgui_bundle/init.py, this line was added:
from imgui_bundle._imgui_bundle import im_cool_bar as im_cool_barIn bindings/imgui_bundle/init.pyi, this line was added:
from . import im_cool_bar as im_cool_barStep 2: fine tune the generation options and write a demo¶
Step 2-a: Edit and run external/ImCoolBar/bindings/generate_imcoolbar.py:¶
Edit and re-run it until the generated code fits the expected needs.
In the case of ImCoolBar, two simple changes were made:
def main():
# ...
# ...
# Configure options
options = litgen.LitgenOptions()
options.namespaces_root = ["ImGui"]
options.srcmlcpp_options.functions_api_prefixes = "IMGUI_API"Each time you run the code generation, look at external/ImCoolBar/bindings/im_cool_bar.pyi and external/ImCoolBar/bindings/pybind_imcoolbar.cpp to see if they seem OK. Also run a compilation.
Step 2-b: Fix syntax issues in external/ImCoolBar/bindings/im_cool_bar.pyi:¶
You can add some code before the autogenerated code to fix the syntax issues. For example, this was added:
import enum
from imgui_bundle.imgui import ImVec2, WindowFlags, WindowFlags_
ImCoolBarFlags = int
ImGuiWindowFlags = WindowFlags
ImGuiWindowFlags_None = WindowFlags_.none
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# <litgen_stub> // Autogenerated code below! Do not edit!
#################### <generated_from:ImCoolbar.h> ####################Step 2-c: Write a nice looking demo¶
It should demo the library, and act as a tutorial, in python and C++.
Step 3: If needed, fork and adapt the library¶
Some libraries require modifications to work properly with Python bindings. In such cases, you need to fork the library and make adaptations. This section describes common patterns for adapting C++ APIs for Python compatibility.
Step 3-a: Python API compatibility macros¶
ImGui Bundle defines two preprocessor macros for conditional compilation when building Python bindings:
IMGUI_BUNDLE_PYTHON_API: Code inside this block is compiled only for Python bindings. Use this to provide Python-friendly alternatives to C++ APIs.IMGUI_BUNDLE_PYTHON_UNSUPPORTED_API: Code inside this block is excluded from Python bindings. Use this to hide APIs that cannot be wrapped.
Example: Replacing pointer+size with std::vector
Some C++ APIs use pointer + count patterns that don’t translate well to Python:
#ifdef IMGUI_BUNDLE_PYTHON_UNSUPPORTED_API
// This API is not usable in Python (the combination ImVec2* + int is not easily wrapped)
IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);
#endif
#ifdef IMGUI_BUNDLE_PYTHON_API
// So, we replace it with a more Python-friendly version using std::vector
IMGUI_API void AddPolyline(const std::vector<ImVec2>& points, ImU32 col, ImDrawFlags flags, float thickness);
#endifStep 3-b: Handling function pointer callbacks¶
Python bindings (via nanobind) cannot directly bind C-style function pointers. You need to provide std::function alternatives when building for Python.
In the library header (e.g., im_anim.h):
// Simple callback with no user data
#ifdef IMGUI_BUNDLE_PYTHON_API
using iam_ease_fn = std::function<float(float)>;
#else
typedef float (*iam_ease_fn)(float t);
#endif
// Callbacks with void* user data - exclude user data for Python
// (Python closures capture context naturally)
#ifdef IMGUI_BUNDLE_PYTHON_API
using iam_float_resolver = std::function<float()>;
using iam_clip_callback = std::function<void(ImGuiID inst_id)>;
#else
typedef float (*iam_float_resolver)(void* user);
typedef void (*iam_clip_callback)(ImGuiID inst_id, void* user_data);
#endifExclude void parameters in the generator script:*
C-style callbacks often pass void* user_data for context. In Python, closures capture context naturally, so these parameters should be excluded.
from codemanip import code_utils
options = litgen.LitgenOptions()
options.use_nanobind()
# Exclude void* parameters from function signatures
options.fn_params_exclude_types__regex = code_utils.join_string_by_pipe_char([
r"void\s*\*", # void* user data
])Add callback type aliases to stub files:
The auto-generated stub file won’t include nice type aliases for callbacks. Add them manually before the autogenerated section:
from typing import Callable
from imgui_bundle.imgui import ImVec2, ImVec4
# Callback type aliases (add before autogenerated code)
ease_fn = Callable[[float], float]
# Resolver callbacks (return dynamic target values)
float_resolver = Callable[[], float]
vec2_resolver = Callable[[], ImVec2]
color_resolver = Callable[[], ImVec4]
# Event callbacks
clip_callback = Callable[[int], None] # inst_id
marker_callback = Callable[[int, int, float], None] # inst_id, marker_id, time
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# <litgen_stub> // Autogenerated code below! Do not edit!Summary of library adaptation patterns¶
Unsupported APIs: Hide with
#ifdef IMGUI_BUNDLE_PYTHON_UNSUPPORTED_APIPython-friendly alternatives: Provide with
#ifdef IMGUI_BUNDLE_PYTHON_APIFunction pointers: Replace with
std::functionunderIMGUI_BUNDLE_PYTHON_APIvoid user data*: Exclude from Python callbacks (closures capture context)
Stub files: Add
Callabletype aliases for documentation