GithubHelp home page GithubHelp logo

pthom / imgui_bundle Goto Github PK

View Code? Open in Web Editor NEW
506.0 13.0 48.0 33.87 MB

Dear ImGui Bundle: easily create ImGui applications in Python and C++. Batteries included!

Home Page: https://pthom.github.io/imgui_bundle/

License: MIT License

CMake 1.66% Python 72.38% C++ 8.82% Shell 0.09% Jupyter Notebook 15.06% C 1.05% HTML 0.48% JavaScript 0.41% Dockerfile 0.04%
cpp imgui python3 creative-coding immediate-gui jupyter-notebook research-and-development

imgui_bundle's Introduction

Dear ImGui Bundle documentation

Tip
For a more readable version of this document, click on doc
CI status
OpenGL renderer Python bindings Alternative renderers Mobile

badge badge

badge badge badge

badge badge badge

badge badge

abc

Dear ImGui Bundle: easily create ImGui applications in Python and C++. Batteries included!

Tip
Click on the logo for a complete interactive demonstration!

sources doc manual

Introduction

Dear ImGui Bundle

Dear ImGui Bundle is a comprehensive bundle for Dear ImGui, featuring various powerful libraries from its ecosystem. Designed to facilitate the creation of applications in C++ and Python across Windows, macOS, Linux, iOS, Android, and emscripten (Web apps), it is ideal for application developers, and researchers eager to dive into GUI development with ease and efficiency. This bundle simplifies the process, allowing you to focus on the creative aspects of your projects.

Interactive manual & demo in one click!

Click on the animated demonstration below to launch the fully interactive demonstration.

Demo
Figure 1. Dear ImGui Bundle interactive demo
Tip
This demonstration is also an interactive manual, similar to the online ImGui Manual

Batteries included

Dear ImGui Bundle includes the following libraries, which are available in C++ and in Python:

Dear ImGui : Bloat-free Graphical User interface for C++ with minimal dependencies

demo widgets imgui

ImGui Test Engine: Dear ImGui Tests & Automation Engine

demo testengine

Hello ImGui: cross-platform Gui apps with the simplicity of a "Hello World" app

demo docking demo custom background

ImPlot: Immediate Mode Plotting

battery implot

ImGuizmo: Immediate mode 3D gizmo for scene editing and other controls based on Dear ImGui

demo gizmo

ImGuiColorTextEdit: Colorizing text editor for ImGui

demo widgets editor

imgui-node-editor: Node Editor built using Dear ImGui

demo node editor

imgui_md: Markdown renderer for Dear ImGui using MD4C parser

demo widgets md

ImmVision: Immediate image debugger and insights

demo immvision process 1 demo immvision process 2

NanoVG: Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations

nanovg full demo

imgui_tex_inspect: A texture inspector tool for Dear ImGui

demo imgui tex inspector

ImFileDialog: A file dialog library for Dear ImGui

demo widgets imfiledialog

portable-file-dialogs OS native file dialogs library (C++11, single-header)

demo widgets portablefiledialogs

imgui-knobs: Knobs widgets for ImGui

demo widgets knobs

imspinner: Set of nice spinners for imgui

demo widgets spinners

imgui_toggle: A toggle switch widget for Dear ImGui

demo widgets toggle

ImCoolBar: A Cool bar for Dear ImGui

demo widgets coolbar

imgui-command-palette: A Sublime Text or VSCode style command palette in ImGui

demo widgets command palette

A big thank you to their authors for their awesome work!

Easily port your code between python and C++

The python bindings are autogenerated via an advanced generator (so that keeping them up to date is easy), and closely mirror the original C++ API, with fully typed bindings.

The original code documentation is meticulously kept inside the python stubs. See for example the documentation for imgui , implot, and hello imgui

Thanks to this, code completion in your favorite python IDE works like a charm, and porting code between Python and C++ becomes easy.

Tip
GPT can help you translate between C++ and Python: see this conversation where GPT4 was used to translate code and summarize the differences between the C++ and Python APIs.
Click to see an example

heart

Python

import time
import numpy as np

from imgui_bundle import implot, imgui_knobs, imgui, immapp, hello_imgui

# Fill x and y whose plot is a heart
vals = np.arange(0, np.pi * 2, 0.01)
x = np.power(np.sin(vals), 3) * 16
y = 13 * np.cos(vals) - 5 * np.cos(2 * vals) - 2 * np.cos(3 * vals) - np.cos(4 * vals)
# Heart pulse rate and time tracking
phase = 0.0
t0 = time.time() + 0.2
heart_pulse_rate = 80


def gui():
    global heart_pulse_rate, phase, t0, x, y
    # Make sure that the animation is smooth
    hello_imgui.get_runner_params().fps_idling.enable_idling = False

    t = time.time()
    phase += (t - t0) * heart_pulse_rate / (np.pi * 2)
    k = 0.8 + 0.1 * np.cos(phase)
    t0 = t

    imgui.text("Bloat free code")
    implot.begin_plot("Heart", immapp.em_to_vec2(21, 21))
    implot.plot_line("", x * k, y * k)
    implot.end_plot()

    _, heart_pulse_rate = imgui_knobs.knob("Pulse", heart_pulse_rate, 30, 180)


if __name__ == "__main__":
    immapp.run(gui, window_size=(300, 450), window_title="Hello!", with_implot=True, fps_idle=0)  # type: ignore

C++

#ifdef IMGUI_BUNDLE_WITH_IMPLOT
#include "imgui.h"
#include "implot/implot.h"
#include "imgui-knobs/imgui-knobs.h"
#include "immapp/immapp.h"

#include <cmath>

std::vector<double> VectorTimesK(const std::vector<double>& values, double k)
{
    std::vector<double> r(values.size(), 0.);
    for (size_t i = 0; i < values.size(); ++i)
        r[i] = k * values[i];
    return r;
}

int main(int , char *[]) {
    // Fill x and y whose plot is a heart
    double pi = 3.1415926535;
    std::vector<double>  x, y; {
        for (double t = 0.; t < pi * 2.; t += 0.01) {
            x.push_back(pow(sin(t), 3.) * 16.);
            y.push_back(13. * cos(t) - 5 * cos(2. * t) - 2 * cos(3. * t) - cos(4. * t));
        }
    }
    // Heart pulse rate and time tracking
    double phase = 0., t0 = ImmApp::ClockSeconds() + 0.2;
    float heart_pulse_rate = 80.;

    auto gui = [&]() {
        // Make sure that the animation is smooth
        HelloImGui::GetRunnerParams()->fpsIdling.enableIdling = false;

        double t = ImmApp::ClockSeconds();
        phase += (t - t0) * (double)heart_pulse_rate / (pi * 2.);
        double k = 0.8 + 0.1 * cos(phase);
        t0 = t;

        ImGui::Text("Bloat free code");
        auto xk = VectorTimesK(x, k), yk = VectorTimesK(y, k);
        ImPlot::BeginPlot("Heart", ImmApp::EmToVec2(21, 21));
        ImPlot::PlotLine("", xk.data(), yk.data(), (int)xk.size());
        ImPlot::EndPlot();

        ImGuiKnobs::Knob("Pulse", &heart_pulse_rate, 30., 180.);
    };

    ImmApp::Run(
        gui, "Hello!",
        /*windowSizeAuto=*/false , /*windowRestorePreviousGeometry==*/false, /*windowSize=*/{300, 450},
        /*fpsIdle=*/ 25.f, /*withImplot=*/true);
    return 0;
}

#else // #ifdef IMGUI_BUNDLE_WITH_IMPLOT
#include <cstdio>
int main(int , char *[]) { printf("This demo requires ImPlot\n"); return 0; }
#endif

Support the project

Dear ImGui Bundle is a free and open-source project, and its development and maintenance require considerable efforts.

If you find it valuable for your work – especially in a commercial enterprise or a research setting – please consider supporting its development by making a donation. Your contributions are greatly appreciated!

For commercial users seeking tailored support or specific enhancements, please contact the author by email.

Contribute

Quality contributions are always welcome! If you’re interested in contributing to the project, whether through code, ideas, or feedback, please refer to the development documentation.

License

Dear ImGui Bundle is licensed under the MIT License

Build and install instructions

Install for Python

Install from pypi

pip install imgui-bundle
pip install opencv-contrib-python # (1)
  1. in order to run the immvision module, install opencv-python or opencv-contrib-python

Note: under windows, you might need to install msvc redist.

Install from source:

git clone https://github.com/pthom/imgui_bundle.git
cd imgui_bundle
git submodule update --init --recursive # (1)
pip install -v . # (2)
pip install opencv-contrib-python
  1. Since there are lots of submodules, this might take a few minutes

  2. The build process might take up to 5 minutes

Run the python demo

Simply run demo_imgui_bundle.

The source for the demos can be found inside bindings/imgui_bundle/demos_python.

Tip
Consider demo_imgui_bundle as an always available manual for Dear ImGui Bundle with lots of examples and related code source.

Install for C++

Integrate Dear ImGui Bundle in your own project in 5 minutes

The easiest way to use Dear ImGui Bundle in an external project is to use the template available at https://github.com/pthom/imgui_bundle_template.

This template includes everything you need to set up your own project.

Build from source

If you choose to clone this repo, follow these instructions:

git clone https://github.com/pthom/imgui_bundle.git
cd imgui_bundle
git submodule update --init --recursive # (1)
mkdir build
cd build
cmake .. -DIMMVISION_FETCH_OPENCV=ON # (2)
make -j
  1. Since there are lots of submodules, this might take a few minutes

  2. The flag -DIMMVISION_FETCH_OPENCV=ON is optional. If set, a minimal version of OpenCV will be downloaded a compiled at this stage (this might require a few minutes)

The immvision module will only be built if OpenCV can be found. Otherwise, it will be ignored, and no error will be emitted.

If you have an existing OpenCV install, set its path via:

cmake .. -DOpenCV_DIR=/.../path/to/OpenCVConfig.cmake

Run the C++ demo

If you built ImGuiBundle from source, Simply run build/bin/demo_imgui_bundle.

The source for the demos can be found inside bindings/imgui_bundle/demos_cpp.

Tip
Consider demo_imgui_bundle as a manual with lots of examples and related code source. It is always available online

Quick Start & Examples

First, install Dear ImGui Bundle following the Build and install instructions.

Then study the examples below.

Hello, World

demo hello
Figure 2. Hello World

Hello, World in C++

#include "immapp/immapp.h"
#include "imgui.h"

void Gui()
{
    ImGui::Text("Hello, world!");
}

int main(int, char **)
{
    ImmApp::Run(
        Gui,
        "Hello!",
        true // window_size_auto
        // Uncomment the next line to restore window position and size from previous run
        // , true // windowRestorePreviousGeometry
    );

    return 0;
}
Build with cmake, using imgui_bundle_add_app

imgui_bundle_add_app is a cmake command, close to add_executable, which will:

  • automatically link your app to the required libraries (imgui_bundle, OpenGl, glad, etc)

  • embed the assets (for desktop, mobile, and emscripten apps)

  • add an icon for your app (on desktop and mobile platforms)

  • perform additional customization (app icon and name on mobile platforms, etc)

Option 1: using imgui_bundle as a submodule

First, add imgui_bundle as a submodule:

git submodule add https://github.com/pthom/imgui_bundle.git
cd imgui_bundle
git submodule update --init --recursive

Then, write a simple CMakeLists file where you add imgui_bundle, then call imgui_bundle_add_app to create your application.

cmake_minimum_required(VERSION 3.20)
project(imgui_bundle_hello)
set(CMAKE_CXX_STANDARD 17)

add_subdirectory(imgui_bundle)
imgui_bundle_add_app(hello_world hello_world.cpp)
Option 2 : Fetch imgui_bundle during compilation
cmake_minimum_required(VERSION 3.12)
project(helloworld_with_helloimgui)
set(CMAKE_CXX_STANDARD 17)

include(FetchContent)
Set(FETCHCONTENT_QUIET FALSE)
FetchContent_Declare(imgui_bundle GIT_REPOSITORY https://github.com/pthom/imgui_bundle.git GIT_TAG main)
FetchContent_MakeAvailable(imgui_bundle)
# set(IMMVISION_FETCH_OPENCV ON) # optional, if you wish to build ImmVision

# Build your app
imgui_bundle_add_app(hello_world hello_world.cpp)
Note
This cmake file is part of a quick start template available at https://github.com/pthom/imgui_bundle_template. Refer to it if you wish to customize the application icon.

Hello, World in Python

from imgui_bundle import imgui, immapp


def gui():
    imgui.text("Hello, world!")


immapp.run(
    gui_function=gui,  # The Gui function to run
    window_title="Hello!",  # the window title
    window_size_auto=True,  # Auto size the application window given its widgets
    # Uncomment the next line to restore window position and size from previous run
    # window_restore_previous_geometry==True
)

About assets

HelloImGui and ImmApp applications rely on the presence of an assets folder. The typical layout of an assets folder looks like this:

assets/
    +-- app_settings/                # Application settings
    |    +-- icon.png                # This will be the app icon, it should be square
    |    |                           # and at least 256x256. It will  be converted
    |    |                           # to the right format, for each platform (except Android)
    |    +-- apple/
    |    |         +-- Info.plist    # macOS and iOS app settings
    |    |                           # (or Info.ios.plist + Info.macos.plist)
    |    |
    |    +-- android/                # Android app settings: any file placed here will be deployed
    |    |   |-- AndroidManifest.xml # (Optional manifest, HelloImGui will generate one if missing)
    |    |   +-- res/
    |    |       +-- mipmap-xxxhdpi/ # Optional icons for different resolutions
    |    |           +-- ...         # Use Android Studio to generate them:
    |    |                           # right click on res/ => New > Image Asset
    |    +-- emscripten/
    |      |-- shell.emscripten.html # Emscripten shell file
    |      |                         #   (this file will be cmake "configured"
    |      |                         #    to add the name and favicon)
    |      +-- custom.js             # Any custom file here will be deployed
    |                                #   in the emscripten build folder

    +-- fonts/
    |    +-- DroidSans.ttf            # Default fonts used by HelloImGui to
    |    +-- fontawesome-webfont.ttf  # improve text rendering (esp. on High DPI)
    |    |                            # if absent, a default LowRes font is used.
    |    |
    |    +-- Roboto/                  # Optional: fonts for markdown
    |         +-- LICENSE.txt
    |         +-- Roboto-Bold.ttf
    |         +-- Roboto-BoldItalic.ttf
    |         +-- Roboto-Regular.ttf
    |         +-- Roboto-RegularItalic.ttf
    |         +-- SourceCodePro-Regular.ttf
    +-- images/
         +-- markdown_broken_image.png  # Optional: used for markdown
         +-- world.png                  # Add anything in the assets folder!

You can change the assets folder:

  • during the execution, via HelloImGui::SetAssetsFolder (C++) or hello_imgui.set_assets_folder (python).

  • at compile time, via imgui_bundle_add_app(app_name file.cpp ASSETS_LOCATION "path/to/assets") (C++ only)

Where to find the default assets

Look at the folder imgui_bundle/bindings/imgui_bundle/assets to see their content.

Where to place your assets folder

Python

Place it into your execution folder (C++ and python), or call hello_imgui.set_assets_folder() at startup.

C++

Place the assets/ folder besides your CMakeLists.txt, and it will be deployed into the execution folder automatically ().

When using cmake, you can also specify a custom assets folder via imgui_bundle_add_app(app_name file.cpp ASSETS_LOCATION "path/to/assets")

You can also call HelloImGui::SetAssetsFolder at startup.

App icon

The app icon is defined by the file icon.png in the assets/app_settings folder. It should be square and at least 256x256 (but 512x512 is preferred).

C++

With C++, icon.png will define the application icon as well as the window icon. It will be converted to the right format for each platform by CMake (via imgui_bundle_add_app).

Python

With Python, icon.png will define the window icon, on platforms that supports this (i.e. Windows and Linux, but not macOS).

It will not define the application icon.

If you wish to ship an application with a given icon, you should use a tool like pyinstaller to create a standalone executable. See the pyinstaller documentation for more information.

See this demo for an example showing how to package a python application.

App settings

macOS and iOS, (C++ only)

The app settings are defined by the file Info.plist in the assets/app_settings/apple folder.

You can copy and edit this example by adding your own settings (replace ${HELLO_IMGUI_BUNDLE_XXX} by your own values).

You can also specify different settings for macOS and iOS via Info.macos.plist and Info.ios.plist

Demo using assets & add-ons

demo assets addons
Figure 3. Demo assets and add-ons usage

This demonstration showcases how to:

  • Load and use assets (fonts, images, icons, etc.)

  • Use ImPlot to display various types of plots

  • Use markdown to display formatted messages

This demonstration source code is heavily documented and should be self-explanatory.

Click to see its source code in C++
#include "hello_imgui/hello_imgui.h"
#include "immapp/immapp.h"
#include "imgui_md_wrapper/imgui_md_wrapper.h"
#ifdef IMGUI_BUNDLE_WITH_IMPLOT
#include "implot/implot.h"
#endif
#include "immapp/code_utils.h"
#include "demo_utils/api_demos.h"
#include <vector>
#include <map>


// This function displays the help messages that are displayed in this demo application
void ShowDoc(const std::string& whichDoc);


// Your global application state, that will be edited during the execution
struct AppState
{
    // you can edit the ImPlot pie chart values
    std::vector<float> PlotData = {0.15f, 0.30f, 0.2f, 0.05f};

    // You can edit a demo markdown string
    char MarkdownInput[4000] = "*Welcome to the interactive markdown demo!* Try writing some markdown content here.";

    //
    // Note about AppState:
    // Inside ImGui demo code, you will often see static variables, such as in this example
    // ```cpp
    //     static int value = 10;
    //     bool changed = ImGui::SliderInt("Value", &value, 0, 10);  // edit this variable between 0 and 10
    // ```
    // In this example, `value` is a static variable whose state is preserved:
    // it merely acts as a global variable, whose scope is limited to this function.
    // Global variables should be avoided, and storing the Application State like this is preferable in production code.
    //
};


// A demo showcasing the assets usage in HelloImGui and ImmApp
void DemoAssets(AppState& appState)
{
    ImGuiMd::Render("# Demo Assets");
    ImGui::Text("Here are some icons from Font Awesome: ");
    ImGui::SameLine(); ImGui::SetCursorPosX(HelloImGui::EmSize(40.f));
    ImGui::Text(ICON_FA_INFO " " ICON_FA_EXCLAMATION_TRIANGLE " " ICON_FA_SAVE);


    ImGui::Text("Here is an image that was loaded from the assets: ");
    ImGui::SameLine(); ImGui::SetCursorPosX(HelloImGui::EmSize(40.f));

    // Prefer to specify sizes using the "em" unit: see https://en.wikipedia.org/wiki/Em_(typography)
    //     Below, imageSize is equivalent to the size of 3 lines of text
    ImVec2 imageSize = HelloImGui::EmToVec2(3.f, 3.f);
    HelloImGui::ImageFromAsset("images/world.png", imageSize);

    ImGuiMd::Render("**Read the [documentation about assets](https://pthom.github.io/imgui_bundle/quickstart.html#quickstart_about_assets)**");

    ShowDoc("AssetsDoc");
}


// A demo about the usage of the markdown renderer
void DemoMarkdown(AppState& appState)
{
    std::string markdownDemo = R"(
        # Demo markdown usage

        Let's ask GPT4 to give us some fun programming fortunes in markdown format:

        1. **Bug Hunt**: In the world of software, the best debugger was, is, and will always be a _good night's sleep_.

        2. **Pythonic Wisdom**:
            > They say if you can't explain something simply, you don't understand it well enough. Well, here's my Python code for simplicity:
            ```python
            def explain(thing):
                return "It's just a " + thing + ". Nothing fancy!"
            ```
        )";
    ImGuiMd::RenderUnindented(markdownDemo);

    // Interactive demo
    ImGui::Separator();
    ImGuiMd::Render("*Try it yourself*");
    ImGui::SameLine(HelloImGui::EmSize(30.f));
    if (ImGui::SmallButton("Edit the fortune markdown"))
        strcpy(appState.MarkdownInput, CodeUtils::UnindentMarkdown(markdownDemo).c_str());
    ImGui::InputTextMultiline("##Markdown Input", appState.MarkdownInput, sizeof(appState.MarkdownInput), HelloImGui::EmToVec2(40.f, 5.f));
    ImGuiMd::RenderUnindented(appState.MarkdownInput);
    ImGui::Separator();

    ShowDoc("MarkdownDoc");
}


#ifdef IMGUI_BUNDLE_WITH_IMPLOT
// A demo showcasing the usage of ImPlot
void DemoPlot(AppState& appState)
{
    ImGuiMd::Render("# Demo ImPlot");

    static const char* data_labels[]    = {"Frogs", "Hogs", "Dogs", "Logs"};

    ImGui::Text("Edit Pie Chart values");
    ImGui::SetNextItemWidth(250);
    ImGui::DragFloat4("Pie Data", appState.PlotData.data(), 0.01f, 0, 1);

    // Prefer to specify sizes using the "em" unit: see https://en.wikipedia.org/wiki/Em_(typography)
    //     Below, plotSize is equivalent to the size of 1 lines of text
    ImVec2 plotSize = ImmApp::EmToVec2(15.f, 15.f);

    if (ImPlot::BeginPlot("Pie Chart", plotSize))
    {
        ImPlot::SetupAxes("", "", ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
        ImPlot::PlotPieChart(
            data_labels,
            appState.PlotData.data(), appState.PlotData.size(), // data and count
            0.5, 0.5, // pie center position in the plot(x, y). Here, it is centered
            0.35,      // pie radius relative to plotSize
            "%.2f",   // fmt
            90        // angle
            );
            ImPlot::EndPlot();
    }

    ShowDoc("PlotDoc");
}
#else
void DemoPlot(AppState& appState) {}
#endif


// Our main function
int main(int, char**)
{
    // This call is specific to the ImGui Bundle interactive manual. In a standard application, you could write:
    //         HelloImGui::SetAssetsFolder("my_assets"); // (By default, HelloImGui will search inside "assets")
    ChdirBesideAssetsFolder();

    AppState appState;         // Our global appState

    // This is our GUI function:
    //     it will display the widgets
    //     it captures the appState, since it can modify it
    auto gui = [&appState]()
    {
        DemoAssets(appState);
        ImGui::NewLine();
        DemoMarkdown(appState);
        ImGui::NewLine();
        DemoPlot(appState);
    };

    // Then, we start our application:
    //     First, we set some RunnerParams, with simple settings
    HelloImGui::SimpleRunnerParams runnerParams;
    runnerParams.windowSize = {1000, 1000};
    //     Here we set our GUI function
    runnerParams.guiFunction = gui;
    //     Then, we need to activate two addons: ImPlot and Markdown
    ImmApp::AddOnsParams addons;
    addons.withImplot = true;
    addons.withMarkdown = true;
    //     And we are ready to go!
    ImmApp::Run(runnerParams, addons);

    return 0;
}

///////////////////////////////////////////////////////////////////////////////
// End of demo code
///////////////////////////////////////////////////////////////////////////////


//
// Note: the code below only displays the help messages
//

std::string GetDoc(const std::string& whichDoc)
{
    static std::map<std::string, std::string> docs =
        {
            {
                "AssetsDoc",
                R"(
                    The icons and image were shown via this code:

                    C++
                    ```cpp
                    ImGui::Text(ICON_FA_INFO " " ICON_FA_EXCLAMATION_TRIANGLE " " ICON_FA_SAVE);
                    ImVec2 imageSize = HelloImGui::EmToVec2(3.f, 3.f);
                    HelloImGui::ImageFromAsset("images/world.png", imageSize);
                    ```

                    Python
                    ```python
                    imgui.text(icons_fontawesome.ICON_FA_INFO + " " + icons_fontawesome.ICON_FA_EXCLAMATION_TRIANGLE + " " + icons_fontawesome.ICON_FA_SAVE)
                    image_size = hello_imgui.em_to_vec2(3.0, 3.0)
                    hello_imgui.image_from_asset("images/world.png", image_size)
                    ```

                    *Note: In this code, imageSize is equivalent to the size of 3 lines of text, using the [em unit](https://en.wikipedia.org/wiki/Em_(typography))*
                )"
            },
            {
                "MarkdownDoc",
                R"(
                This markdown string was rendered by calling either:

                C++
                ```cpp
                ImGuiMd::Render(markdown_string);            // render a markdown string
                ImGuiMd::RenderUnindented(markdown_string);  // remove top-most indentation before rendering
                ```

                Python
                ```python
                imgui_md.render(markdown_string);            # render a markdown string
                imgui_md.render_unindented(markdown_string); # remove top-most indentation before rendering
                ```

                This markdown renderer is based on [imgui_md](https://github.com/mekhontsev/imgui_md), by Dmitry Mekhontsev.
                It supports the most common markdown features: emphasis, link, code blocks, etc.
                )"
            },
            {
                "PlotDoc",
                R"(
                By using ImPlot, you can display lots of different plots. See [online demo](https://traineq.org/implot_demo/src/implot_demo.html) which demonstrates lots of plot types (LinePlot, ScatterPlot, Histogram, Error Bars, Heatmaps, etc.)

                Note: in order to use ImPlot, you need to "activate" this add-on, like this:

                C++
                ```cpp
                ImmApp::AddOnsParams addons { .withImplot = true };
                ImmApp::Run(runnerParams, addons);
                ```

                Python:
                ```python
                addons = immapp.AddOnsParams(with_implot=True)
                immapp.run(runner_params, addons);
                ```
                )"
            },
        };

    return docs.at(whichDoc);
}


void ShowDoc(const std::string& whichDoc)
{
    static std::map<std::string, bool> is_doc_visible;
    if (is_doc_visible.find(whichDoc) == is_doc_visible.end())
        is_doc_visible[whichDoc] = false;

    ImGui::PushID(whichDoc.c_str());
    ImGui::Checkbox("More info", &is_doc_visible[whichDoc]);

    if (is_doc_visible[whichDoc])
    {
        ImGuiMd::RenderUnindented(GetDoc(whichDoc));
        ImGui::Dummy(HelloImGui::EmToVec2(1.f, 6.f));
        ImGui::Separator();
    }
    ImGui::PopID();
}
Click to see its source code in Python
from imgui_bundle import imgui, implot, immapp, hello_imgui, imgui_md, icons_fontawesome
from imgui_bundle.demos_python import demo_utils

import numpy as np
from typing import Dict, List
from dataclasses import dataclass, field


def show_doc(which_doc: str):
    """This function displays the help messages that are displayed in this demo application
    (implemented later in this file)"""
    ...


@dataclass
class AppState:
    """Your global application state, that will be edited during the execution."""

    # you can edit the ImPlot pie chart values
    plot_data: List[float] = field(default_factory=lambda: [0.15, 0.30, 0.2, 0.05])

    # You can edit a demo markdown string
    markdown_input: str = "*Welcome to the interactive markdown demo!* Try writing some markdown content here."

    #
    # Note about AppState:
    # Inside ImGui demo code, you will often see static variables, such as in this example
    #     static int value = 10;
    #     bool changed = ImGui::SliderInt("Value", &value, 0, 10);  // edit this variable between 0 and 10
    # In this example, `value` is a static variable whose state is preserved:
    # it merely acts as a global variable, whose scope is limited to this function.
    # Global variables should be avoided, and storing the Application State like this is preferable in production code.


def demo_assets(app_state: AppState):
    """A demo showcasing the assets usage in HelloImGui and ImmApp"""
    imgui_md.render("# Demo Assets")

    imgui.text("Here are some icons from Font Awesome: ")
    imgui.same_line()
    imgui.set_cursor_pos_x(hello_imgui.em_size(40.0))
    imgui.text(
        icons_fontawesome.ICON_FA_INFO
        + " "
        + icons_fontawesome.ICON_FA_EXCLAMATION_TRIANGLE
        + " "
        + icons_fontawesome.ICON_FA_SAVE
    )

    imgui.text("Here is an image that was loaded from the assets: ")
    imgui.same_line()
    imgui.set_cursor_pos_x(hello_imgui.em_size(40.0))

    # Prefer to specify sizes using the "em" unit: see https://en.wikipedia.org/wiki/Em_(typography)
    # Below, image_size is equivalent to the size of 3 lines of text
    image_size = hello_imgui.em_to_vec2(3.0, 3.0)
    hello_imgui.image_from_asset("images/world.png", image_size)

    imgui_md.render(
        "**Read the [documentation about assets](https://pthom.github.io/imgui_bundle/quickstart.html#quickstart_about_assets)**"
    )
    show_doc("AssetsDoc")


def demo_markdown(app_state: AppState):
    """A demo about the usage of the markdown renderer"""
    markdown_demo = """
        # Demo markdown usage

        Let's ask GPT4 to give us some fun programming fortunes in markdown format:

        1. **Bug Hunt**: In the world of software, the best debugger was, is, and will always be a _good night's sleep_.

        2. **Pythonic Wisdom**:
            > They say if you can't explain something simply, you don't understand it well enough. Well, here's my Python code for simplicity:
            ```python
            def explain(thing):
                return "It's just a " + thing + ". Nothing fancy!"
            ```
    """
    imgui_md.render_unindented(markdown_demo)

    # Interactive demo
    imgui.separator()
    imgui_md.render("*Try it yourself*")
    imgui.same_line(hello_imgui.em_size(30.0))
    if imgui.small_button("Edit the fortune markdown"):
        app_state.markdown_input = immapp.code_utils.unindent_markdown(markdown_demo)
    _, app_state.markdown_input = imgui.input_text_multiline(
        "##Markdown Input", app_state.markdown_input, hello_imgui.em_to_vec2(40.0, 5.0)
    )
    imgui_md.render_unindented(app_state.markdown_input)
    imgui.separator()

    show_doc("MarkdownDoc")


def demo_plot(app_state: AppState):
    """A demo showcasing the usage of ImPlot"""
    imgui_md.render("# Demo ImPlot")

    data_labels = ["Frogs", "Hogs", "Dogs", "Logs"]

    imgui.text("Edit Pie Chart values")
    imgui.set_next_item_width(250)
    _, app_state.plot_data = imgui.drag_float4(
        "Pie Data", app_state.plot_data, 0.01, 0, 1
    )

    # Prefer to specify sizes using the "em" unit: see https://en.wikipedia.org/wiki/Em_(typography)
    # Below, plot_size is equivalent to the size of 15 lines of text
    plot_size = hello_imgui.em_to_vec2(15.0, 15.0)

    if implot.begin_plot("Pie Chart", plot_size):
        implot.setup_axes(
            "",
            "",
            implot.AxisFlags_.no_decorations.value,
            implot.AxisFlags_.no_decorations.value,
        )
        implot.plot_pie_chart(
            data_labels, np.array(app_state.plot_data), 0.5, 0.5, 0.35, "%.2f", 90
        )
        implot.end_plot()

    show_doc("PlotDoc")


def main():
    # This call is specific to the ImGui Bundle interactive manual. In a standard application, you could write:
    #         hello_imgui.set_assets_folder("my_assets")  # (By default, HelloImGui will search inside "assets")
    demo_utils.set_hello_imgui_demo_assets_folder()

    app_state = AppState()  # Initialize our global appState

    # This is our GUI function:
    # it will display the widgets, and it can modify the app_state
    def gui():
        demo_assets(app_state)
        imgui.new_line()
        demo_markdown(app_state)
        imgui.new_line()
        demo_plot(app_state)

    # Then, we start our application:
    #     First, we set some RunnerParams, with simple settings
    runner_params = hello_imgui.SimpleRunnerParams()
    runner_params.window_size = (1000, 1000)
    runner_params.gui_function = gui
    #     We need to activate two addons: ImPlot and Markdown
    addons = immapp.AddOnsParams()
    addons.with_implot = True
    addons.with_markdown = True
    #     And we are ready to go!
    immapp.run(runner_params, addons)


# ///////////////////////////////////////////////////////////////////////////////
# // End of demo code
# ///////////////////////////////////////////////////////////////////////////////


# //
# // Note: the code below only displays the help messages
# //


def get_doc(which_doc: str) -> str:
    """Return the associated documentation string based on the key."""

    docs: Dict[str, str] = {
        "AssetsDoc": """
            The icons and image were shown via this code:

            C++
            ```cpp
            ImGui::Text(ICON_FA_INFO " " ICON_FA_EXCLAMATION_TRIANGLE " " ICON_FA_SAVE);
            ImVec2 imageSize = HelloImGui::EmToVec2(3.f, 3.f);
            HelloImGui::ImageFromAsset("images/world.png", imageSize);
            ```

            Python
            ```python
            imgui.text(icons_fontawesome.ICON_FA_INFO + " " + icons_fontawesome.ICON_FA_EXCLAMATION_TRIANGLE + " " + icons_fontawesome.ICON_FA_SAVE)
            image_size = hello_imgui.em_to_vec2(3.0, 3.0)
            hello_imgui.image_from_asset("images/world.png", image_size)
            ```

            *Note: In this code, imageSize is equivalent to the size of 3 lines of text, using the [em unit](https://en.wikipedia.org/wiki/Em_(typography))*
        """,
        "MarkdownDoc": """
            This markdown string was rendered by calling either:

            C++
            ```cpp
            ImGuiMd::Render(markdown_string);            // render a markdown string
            ImGuiMd::RenderUnindented(markdown_string);  // remove top-most indentation before rendering
            ```

            Python
            ```python
            imgui_md.render(markdown_string);            # render a markdown string
            imgui_md.render_unindented(markdown_string); # remove top-most indentation before rendering
            ```

            This markdown renderer is based on [imgui_md](https://github.com/mekhontsev/imgui_md), by Dmitry Mekhontsev.
            It supports the most common markdown features: emphasis, link, code blocks, etc.
        """,
        "PlotDoc": """
            By using ImPlot, you can display lots of different plots. See [online demo](https://traineq.org/implot_demo/src/implot_demo.html) which demonstrates lots of plot types (LinePlot, ScatterPlot, Histogram, Error Bars, Heatmaps, etc.)

            Note: in order to use ImPlot, you need to "activate" this add-on, like this:

            C++
            ```cpp
            ImmApp::AddOnsParams addons { .withImplot = true };
            ImmApp::Run(runnerParams, addons);
            ```

            Python:
            ```python
            addons = immapp.AddOnsParams(with_implot=True)
            immapp.run(runner_params, addons);
            ```
        """,
    }

    return docs[which_doc]


@immapp.static(is_doc_visible={})  # type: ignore # (ignore redef)
def show_doc(which_doc):  # noqa: F811
    # Access the 'static' variable
    is_doc_visible = show_doc.is_doc_visible

    # Check if the doc visibility entry exists, if not, add it
    if which_doc not in is_doc_visible:
        is_doc_visible[which_doc] = False

    imgui.push_id(which_doc)
    _, is_doc_visible[which_doc] = imgui.checkbox(
        "More info", is_doc_visible[which_doc]
    )

    if is_doc_visible[which_doc]:
        # The following are assumed to be valid calls within the context of your specific ImGui wrapper.
        # 'imgui_md' and 'get_doc' should correspond to your actual usage and imports.
        imgui_md.render_unindented(get_doc(which_doc))
        imgui.dummy(
            hello_imgui.em_to_vec2(1.0, 6.0)
        )  # Assumes 'hello_imgui' is available in your environment
        imgui.separator()

    imgui.pop_id()


if __name__ == "__main__":
    main()

Complex layouts with docking windows

demo docking
Figure 4. Complex docking layout
Tip
As shown in the screenshot, Dear ImGui Bundle provides a variety of predefined themes. In this demo, you can access them via the menu "View/Theme".

This demonstration showcases how to:

  • set up a complex docking layouts (with several possible layouts):

  • use the status bar

  • use default menus (App and view menu), and how to customize them

  • display a log window

  • load additional fonts

  • use a specific application state (instead of using static variables)

  • save some additional user settings within imgui ini file

Its source code is heavily documented and should be self-explanatory.

Click to see its source code in C++

C++

/*
A more complex app demo

It demonstrates how to:
- set up a complex docking layouts (with several possible layouts):
- use the status bar
- use default menus (App and view menu), and how to customize them
- display a log window
- load additional fonts, possibly colored, and with emojis
- use a specific application state (instead of using static variables)
- save some additional user settings within imgui ini file
- use borderless windows, that are movable and resizable
 */

#include "hello_imgui/hello_imgui.h"
#include "imgui.h"
#include "imgui/misc/cpp/imgui_stdlib.h"
#include "imgui_internal.h"
#include "demo_utils/api_demos.h"

#include <sstream>

// Poor man's fix for C++ late arrival in the unicode party:
//    - C++17: u8"my string" is of type const char*
//    - C++20: u8"my string" is of type const char8_t*
// However, ImGui text functions expect const char*.
#ifdef __cpp_char8_t
#define U8_TO_CHAR(x) reinterpret_cast<const char*>(x)
#else
#define U8_TO_CHAR(x) x
#endif
// And then, we need to tell gcc to stop validating format string (it gets confused by the u8"" string)
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wformat"
#endif


//////////////////////////////////////////////////////////////////////////
//    Our Application State
//////////////////////////////////////////////////////////////////////////
struct MyAppSettings
{
    std::string name = "Test";
    int value = 10;
};

struct AppState
{
    float f = 0.0f;
    int counter = 0;

    float rocket_launch_time = 0.f;
    float rocket_progress = 0.0f;

    enum class RocketState {
        Init,
        Preparing,
        Launched
    };
    RocketState rocket_state = RocketState::Init;

    MyAppSettings myAppSettings; // This values will be stored in the application settings
    ImFont* TitleFont = nullptr;
    ImFont* ColorFont = nullptr;
    ImFont* EmojiFont = nullptr;
    ImFont* LargeIconFont = nullptr;
};


//////////////////////////////////////////////////////////////////////////
//    Additional fonts handling
//////////////////////////////////////////////////////////////////////////
void LoadFonts(AppState& appState) // This is called by runnerParams.callbacks.LoadAdditionalFonts
{
    // First, load the default font (the default font should be loaded first)
    HelloImGui::ImGuiDefaultSettings::LoadDefaultFont_WithFontAwesomeIcons();
    // Then load the title font
    appState.TitleFont = HelloImGui::LoadFont("fonts/DroidSans.ttf", 18.f);

    HelloImGui::FontLoadingParams fontLoadingParamsEmoji;
    fontLoadingParamsEmoji.useFullGlyphRange = true;
    appState.EmojiFont = HelloImGui::LoadFont("fonts/NotoEmoji-Regular.ttf", 24.f, fontLoadingParamsEmoji);

    HelloImGui::FontLoadingParams fontLoadingParamsLargeIcon;
    fontLoadingParamsLargeIcon.useFullGlyphRange = true;
    appState.LargeIconFont = HelloImGui::LoadFont("fonts/fontawesome-webfont.ttf", 24.f, fontLoadingParamsLargeIcon);
#ifdef IMGUI_ENABLE_FREETYPE
    HelloImGui::FontLoadingParams fontLoadingParamsColor;
    fontLoadingParamsColor.loadColor = true;
    appState.ColorFont = HelloImGui::LoadFont("fonts/Playbox/Playbox-FREE.otf", 24.f, fontLoadingParamsColor);
#endif
}


//////////////////////////////////////////////////////////////////////////
//    Save additional settings in the ini file
//////////////////////////////////////////////////////////////////////////
// This demonstrates how to store additional info in the application settings
// Use this sparingly!
// This is provided as a convenience only, and it is not intended to store large quantities of text data.

// Warning, the save/load function below are quite simplistic!
std::string MyAppSettingsToString(const MyAppSettings& myAppSettings)
{
    std::stringstream ss;
    ss << myAppSettings.name << "\n";
    ss << myAppSettings.value;
    return ss.str();
}
MyAppSettings StringToMyAppSettings(const std::string& s)
{
    std::stringstream ss(s);
    MyAppSettings myAppSettings;
    ss >> myAppSettings.name;
    ss >> myAppSettings.value;
    return myAppSettings;
}

// Note: LoadUserSettings() and SaveUserSettings() will be called in the callbacks `PostInit` and `BeforeExit`:
//     runnerParams.callbacks.PostInit = [&appState]   { LoadMyAppSettings(appState);};
//     runnerParams.callbacks.BeforeExit = [&appState] { SaveMyAppSettings(appState);};
void LoadMyAppSettings(AppState& appState) //
{
    appState.myAppSettings = StringToMyAppSettings(HelloImGui::LoadUserPref("MyAppSettings"));
}
void SaveMyAppSettings(const AppState& appState)
{
    HelloImGui::SaveUserPref("MyAppSettings", MyAppSettingsToString(appState.myAppSettings));
}

//////////////////////////////////////////////////////////////////////////
//    Gui functions used in this demo
//////////////////////////////////////////////////////////////////////////

// Display a button that will hide the application window
void DemoHideWindow(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Hide app window"); ImGui::PopFont();
    static double lastHideTime = -1.;
    if (ImGui::Button("Hide"))
    {
        lastHideTime =  ImGui::GetTime();
        HelloImGui::GetRunnerParams()->appWindowParams.hidden = true;
    }
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("By clicking this button, you can hide the window for 3 seconds.");
    if (lastHideTime > 0.)
    {
        double now = ImGui::GetTime();
        if (now - lastHideTime > 3.)
        {
            lastHideTime = -1.;
            HelloImGui::GetRunnerParams()->appWindowParams.hidden = false;
        }
    }
}

// Display a button that will show an additional window
void DemoShowAdditionalWindow(AppState& appState)
{
    // Notes:
    //     - it is *not* possible to modify the content of the vector runnerParams.dockingParams.dockableWindows
    //       from the code inside a window's `GuiFunction` (since this GuiFunction will be called while iterating on this vector!)
    //     - there are two ways to dynamically add windows:
    //           * either make them initially invisible, and exclude them from the view menu (such as shown here)
    //           * or modify runnerParams.dockingParams.dockableWindows inside the callback RunnerCallbacks.PreNewFrame
    const char* windowName = "Additional Window";
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Dynamically add window"); ImGui::PopFont();
    if (ImGui::Button("Show additional window"))
    {
        auto additionalWindowPtr = HelloImGui::GetRunnerParams()->dockingParams.dockableWindowOfName(windowName);
        if (additionalWindowPtr)
        {
            // additionalWindowPtr->includeInViewMenu = true;
            additionalWindowPtr->isVisible = true;
        }
    }
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("By clicking this button, you can show an additional window");
}

void DemoLogs(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Log Demo"); ImGui::PopFont();

    ImGui::BeginGroup();
    // Edit a float using a slider from 0.0f to 1.0f
    bool changed = ImGui::SliderFloat("float", &appState.f, 0.0f, 1.0f);
    if (changed)
        HelloImGui::Log(HelloImGui::LogLevel::Warning, "state.f was changed to %f", appState.f);

    // Buttons return true when clicked (most widgets return true when edited/activated)
    if (ImGui::Button("Button"))
    {
        appState.counter++;
        HelloImGui::Log(HelloImGui::LogLevel::Info, "Button was pressed");
    }

    ImGui::SameLine();
    ImGui::Text("counter = %d", appState.counter);
    ImGui::EndGroup();
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("These widgets will interact with the log window");
}

void DemoUserSettings(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("User settings"); ImGui::PopFont();
    ImGui::BeginGroup();
    ImGui::SetNextItemWidth(HelloImGui::EmSize(7.f));
    ImGui::InputText("Name", &appState.myAppSettings.name);
    ImGui::SetNextItemWidth(HelloImGui::EmSize(7.f));
    ImGui::SliderInt("Value", &appState.myAppSettings.value, 0, 100);
    ImGui::EndGroup();
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("The values below are stored in the application settings ini file and restored at startup");
}

void DemoRocket(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Status Bar Demo"); ImGui::PopFont();
    ImGui::BeginGroup();
    if (appState.rocket_state == AppState::RocketState::Init)
    {
        if (ImGui::Button(ICON_FA_ROCKET" Launch rocket"))
        {
            appState.rocket_launch_time = (float)ImGui::GetTime();
            appState.rocket_state = AppState::RocketState::Preparing;
            HelloImGui::Log(HelloImGui::LogLevel::Warning, "Rocket is being prepared");
        }
    }
    else if (appState.rocket_state == AppState::RocketState::Preparing)
    {
        ImGui::Text("Please Wait");
        appState.rocket_progress = (float)(ImGui::GetTime() - appState.rocket_launch_time) / 3.f;
        if (appState.rocket_progress >= 1.0f)
        {
            appState.rocket_state = AppState::RocketState::Launched;
            HelloImGui::Log(HelloImGui::LogLevel::Warning, "Rocket was launched");
        }
    }
    else if (appState.rocket_state == AppState::RocketState::Launched)
    {
        ImGui::Text(ICON_FA_ROCKET " Rocket launched");
        if (ImGui::Button("Reset Rocket"))
        {
            appState.rocket_state = AppState::RocketState::Init;
            appState.rocket_progress = 0.f;
        }
    }
    ImGui::EndGroup();
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("Look at the status bar after clicking");
}

void DemoDockingFlags(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Main dock space node flags"); ImGui::PopFont();
    ImGui::TextWrapped(R"(
This will edit the ImGuiDockNodeFlags for "MainDockSpace".
Most flags are inherited by children dock spaces.
    )");
    struct DockFlagWithInfo {
        ImGuiDockNodeFlags flag;
        std::string label;
        std::string tip;
    };
    std::vector<DockFlagWithInfo> all_flags = {
        {ImGuiDockNodeFlags_NoSplit, "NoSplit", "prevent Dock Nodes from being split"},
        {ImGuiDockNodeFlags_NoResize, "NoResize", "prevent Dock Nodes from being resized"},
        {ImGuiDockNodeFlags_AutoHideTabBar, "AutoHideTabBar",
         "show tab bar only if multiple windows\n"
         "You will need to restore the layout after changing (Menu \"View/Restore Layout\")"},
        {ImGuiDockNodeFlags_NoDockingInCentralNode, "NoDockingInCentralNode",
         "prevent docking in central node\n"
         "(only works with the main dock space)"},
        // {ImGuiDockNodeFlags_PassthruCentralNode, "PassthruCentralNode", "advanced"},
    };
    auto & mainDockSpaceNodeFlags = HelloImGui::GetRunnerParams()->dockingParams.mainDockSpaceNodeFlags;
    for (const auto& flag: all_flags)
    {
        ImGui::CheckboxFlags(flag.label.c_str(), &mainDockSpaceNodeFlags, flag.flag);
        if (ImGui::IsItemHovered())
            ImGui::SetTooltip("%s", flag.tip.c_str());
    }
}

void GuiWindowLayoutCustomization(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Switch between layouts"); ImGui::PopFont();
    ImGui::Text("with the menu \"View/Layouts\"");
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("Each layout remembers separately the modifications applied by the user, \nand the selected layout is restored at startup");
    ImGui::Separator();
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Change the theme"); ImGui::PopFont();
    ImGui::Text("with the menu \"View/Theme\"");
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("The selected theme is remembered and restored at startup");
    ImGui::Separator();
    DemoDockingFlags(appState);
    ImGui::Separator();
}

void DemoAssets(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Image From Asset"); ImGui::PopFont();
    HelloImGui::BeginGroupColumn();
    ImGui::Dummy(HelloImGui::EmToVec2(0.f, 0.45f));
    ImGui::Text("Hello");
    HelloImGui::EndGroupColumn();
    HelloImGui::ImageFromAsset("images/world.png", HelloImGui::EmToVec2(2.5f, 2.5f));
}

void DemoFonts(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Fonts"); ImGui::PopFont();

    ImGui::TextWrapped("Mix icons " ICON_FA_SMILE " and text " ICON_FA_ROCKET "");
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("Example with Font Awesome Icons");

    ImGui::Text("Emojis");

    ImGui::BeginGroup();
    {
        ImGui::PushFont(appState.EmojiFont);
        // ✌️ (Victory Hand Emoji)
        ImGui::Text(U8_TO_CHAR(u8"\U0000270C\U0000FE0F"));
        ImGui::SameLine();

        // ❤️ (Red Heart Emoji)
        ImGui::Text(U8_TO_CHAR(u8"\U00002764\U0000FE0F"));
        ImGui::SameLine();

#ifdef IMGUI_USE_WCHAR32
        // 🌴 (Palm Tree Emoji)
        ImGui::Text(U8_TO_CHAR(u8"\U0001F334"));
        ImGui::SameLine();

        // 🚀 (Rocket Emoji)
        ImGui::Text(U8_TO_CHAR(u8"\U0001F680"));
        ImGui::SameLine();
#endif

        ImGui::PopFont();
    }
    ImGui::EndGroup();
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("Example with NotoEmoji font");

#ifdef IMGUI_ENABLE_FREETYPE
    ImGui::Text("Colored Fonts");
    ImGui::PushFont(appState.ColorFont);
    ImGui::Text("C O L O R !");
    ImGui::PopFont();
    if (ImGui::IsItemHovered())
        ImGui::SetTooltip("Example with Playbox-FREE.otf font");
#endif
}

void DemoThemes(AppState& appState)
{
    ImGui::PushFont(appState.TitleFont); ImGui::Text("Themes"); ImGui::PopFont();
    auto& tweakedTheme = HelloImGui::GetRunnerParams()->imGuiWindowParams.tweakedTheme;

    ImGui::BeginGroup();
    ImVec2 buttonSize = HelloImGui::EmToVec2(7.f, 0.f);
    if (ImGui::Button("Cherry", buttonSize))
    {
        tweakedTheme.Theme = ImGuiTheme::ImGuiTheme_Cherry;
        ImGuiTheme::ApplyTweakedTheme(tweakedTheme);
    }
    if (ImGui::Button("DarculaDarker", buttonSize))
    {
        tweakedTheme.Theme = ImGuiTheme::ImGuiTheme_DarculaDarker;
        ImGuiTheme::ApplyTweakedTheme(tweakedTheme);
    }
    ImGui::EndGroup();
    if (ImGui::IsItemHovered())
            ImGui::SetTooltip(
                "There are lots of other themes: look at the menu View/Theme\n"
                "The selected theme is remembered and restored at startup"
            );
}

// The Gui of the demo feature window
void GuiWindowDemoFeatures(AppState& appState)
{
    DemoFonts(appState);
    ImGui::Separator();
    DemoAssets(appState);
    ImGui::Separator();
    DemoLogs(appState);
    ImGui::Separator();
    DemoRocket(appState);
    ImGui::Separator();
    DemoUserSettings(appState);
    ImGui::Separator();
    DemoHideWindow(appState);
    ImGui::Separator();
    DemoShowAdditionalWindow(appState);
    ImGui::Separator();
    DemoThemes(appState);
    ImGui::Separator();
}

// The Gui of the status bar
void StatusBarGui(AppState& app_state)
{
    if (app_state.rocket_state == AppState::RocketState::Preparing)
    {
        ImGui::Text("Rocket completion: ");
        ImGui::SameLine();
        ImGui::ProgressBar(app_state.rocket_progress, HelloImGui::EmToVec2(7.0f, 1.0f));
    }
}

// The menu gui
void ShowMenuGui(HelloImGui::RunnerParams& runnerParams)
{
    HelloImGui::ShowAppMenu(runnerParams);
    HelloImGui::ShowViewMenu(runnerParams);

    if (ImGui::BeginMenu("My Menu"))
    {
        bool clicked = ImGui::MenuItem("Test me", "", false);
        if (clicked)
        {
            HelloImGui::Log(HelloImGui::LogLevel::Warning, "It works");
        }
        ImGui::EndMenu();
    }
}

void ShowAppMenuItems()
{
    if (ImGui::MenuItem("A Custom app menu item"))
        HelloImGui::Log(HelloImGui::LogLevel::Info, "Clicked on A Custom app menu item");
}

void ShowTopToolbar(AppState& appState)
{
    ImGui::PushFont(appState.LargeIconFont);
    if (ImGui::Button(ICON_FA_POWER_OFF))
        HelloImGui::GetRunnerParams()->appShallExit = true;

    ImGui::SameLine(ImGui::GetWindowWidth() - HelloImGui::EmSize(7.f));
    if (ImGui::Button(ICON_FA_HOME))
        HelloImGui::Log(HelloImGui::LogLevel::Info, "Clicked on Home in the top toolbar");
    ImGui::SameLine();
    if (ImGui::Button(ICON_FA_SAVE))
        HelloImGui::Log(HelloImGui::LogLevel::Info, "Clicked on Save in the top toolbar");
    ImGui::SameLine();
    if (ImGui::Button(ICON_FA_ADDRESS_BOOK))
        HelloImGui::Log(HelloImGui::LogLevel::Info, "Clicked on Address Book in the top toolbar");

    ImGui::SameLine(ImGui::GetWindowWidth() - HelloImGui::EmSize(2.f));
    ImGui::Text(ICON_FA_BATTERY_THREE_QUARTERS);
    ImGui::PopFont();
}

void ShowRightToolbar(AppState& appState)
{
    ImGui::PushFont(appState.LargeIconFont);
    if (ImGui::Button(ICON_FA_ARROW_CIRCLE_LEFT))
        HelloImGui::Log(HelloImGui::LogLevel::Info, "Clicked on Circle left in the right toolbar");

    if (ImGui::Button(ICON_FA_ARROW_CIRCLE_RIGHT))
        HelloImGui::Log(HelloImGui::LogLevel::Info, "Clicked on Circle right in the right toolbar");
    ImGui::PopFont();
}

//////////////////////////////////////////////////////////////////////////
//    Docking Layouts and Docking windows
//////////////////////////////////////////////////////////////////////////

//
// 1. Define the Docking splits (two versions are available)
//
std::vector<HelloImGui::DockingSplit> CreateDefaultDockingSplits()
{
    //    Define the default docking splits,
    //    i.e. the way the screen space is split in different target zones for the dockable windows
    //     We want to split "MainDockSpace" (which is provided automatically) into three zones, like this:
    //
    //    ___________________________________________
    //    |        |                                |
    //    | Command|                                |
    //    | Space  |    MainDockSpace               |
    //    |        |                                |
    //    |        |                                |
    //    |        |                                |
    //    -------------------------------------------
    //    |     MiscSpace                           |
    //    -------------------------------------------
    //

    // Then, add a space named "MiscSpace" whose height is 25% of the app height.
    // This will split the preexisting default dockspace "MainDockSpace" in two parts.
    HelloImGui::DockingSplit splitMainMisc;
    splitMainMisc.initialDock = "MainDockSpace";
    splitMainMisc.newDock = "MiscSpace";
    splitMainMisc.direction = ImGuiDir_Down;
    splitMainMisc.ratio = 0.25f;

    // Then, add a space to the left which occupies a column whose width is 25% of the app width
    HelloImGui::DockingSplit splitMainCommand;
    splitMainCommand.initialDock = "MainDockSpace";
    splitMainCommand.newDock = "CommandSpace";
    splitMainCommand.direction = ImGuiDir_Left;
    splitMainCommand.ratio = 0.25f;

    std::vector<HelloImGui::DockingSplit> splits {splitMainMisc, splitMainCommand};
    return splits;
}

std::vector<HelloImGui::DockingSplit> CreateAlternativeDockingSplits()
{
    //    Define alternative docking splits for the "Alternative Layout"
    //    ___________________________________________
    //    |                |                        |
    //    | Misc           |                        |
    //    | Space          |    MainDockSpace       |
    //    |                |                        |
    //    -------------------------------------------
    //    |                                         |
    //    |                                         |
    //    |     CommandSpace                        |
    //    |                                         |
    //    -------------------------------------------

    HelloImGui::DockingSplit splitMainCommand;
    splitMainCommand.initialDock = "MainDockSpace";
    splitMainCommand.newDock = "CommandSpace";
    splitMainCommand.direction = ImGuiDir_Down;
    splitMainCommand.ratio = 0.5f;

    HelloImGui::DockingSplit splitMainMisc;
    splitMainMisc.initialDock = "MainDockSpace";
    splitMainMisc.newDock = "MiscSpace";
    splitMainMisc.direction = ImGuiDir_Left;
    splitMainMisc.ratio = 0.5f;

    std::vector<HelloImGui::DockingSplit> splits {splitMainCommand, splitMainMisc};
    return splits;
}

//
// 2. Define the Dockable windows
//
std::vector<HelloImGui::DockableWindow> CreateDockableWindows(AppState& appState)
{
    // A window named "FeaturesDemo" will be placed in "CommandSpace". Its Gui is provided by "GuiWindowDemoFeatures"
    HelloImGui::DockableWindow featuresDemoWindow;
    featuresDemoWindow.label = "Features Demo";
    featuresDemoWindow.dockSpaceName = "CommandSpace";
    featuresDemoWindow.GuiFunction = [&] { GuiWindowDemoFeatures(appState); };

    // A layout customization window will be placed in "MainDockSpace". Its Gui is provided by "GuiWindowLayoutCustomization"
    HelloImGui::DockableWindow layoutCustomizationWindow;
    layoutCustomizationWindow.label = "Layout customization";
    layoutCustomizationWindow.dockSpaceName = "MainDockSpace";
    layoutCustomizationWindow.GuiFunction = [&appState]() { GuiWindowLayoutCustomization(appState); };

    // A Log window named "Logs" will be placed in "MiscSpace". It uses the HelloImGui logger gui
    HelloImGui::DockableWindow logsWindow;
    logsWindow.label = "Logs";
    logsWindow.dockSpaceName = "MiscSpace";
    logsWindow.GuiFunction = [] { HelloImGui::LogGui(); };

    // A Window named "Dear ImGui Demo" will be placed in "MainDockSpace"
    HelloImGui::DockableWindow dearImGuiDemoWindow;
    dearImGuiDemoWindow.label = "Dear ImGui Demo";
    dearImGuiDemoWindow.dockSpaceName = "MainDockSpace";
    dearImGuiDemoWindow.imGuiWindowFlags = ImGuiWindowFlags_MenuBar;
    dearImGuiDemoWindow.GuiFunction = [] { ImGui::ShowDemoWindow(); };

    // additionalWindow is initially not visible (and not mentioned in the view menu).
    // it will be opened only if the user chooses to display it
    HelloImGui::DockableWindow additionalWindow;
    additionalWindow.label = "Additional Window";
    additionalWindow.isVisible = false;               // this window is initially hidden,
    additionalWindow.includeInViewMenu = false;       // it is not shown in the view menu,
    additionalWindow.rememberIsVisible = false;       // its visibility is not saved in the settings file,
    additionalWindow.dockSpaceName = "MiscSpace";     // when shown, it will appear in MiscSpace.
    additionalWindow.GuiFunction = [] { ImGui::Text("This is the additional window"); };

    std::vector<HelloImGui::DockableWindow> dockableWindows {
        featuresDemoWindow,
        layoutCustomizationWindow,
        logsWindow,
        dearImGuiDemoWindow,
        additionalWindow,
    };
    return dockableWindows;
}

//
// 3. Define the layouts:
//        A layout is stored inside DockingParams, and stores the splits + the dockable windows.
//        Here, we provide the default layout, and two alternative layouts.
//
HelloImGui::DockingParams CreateDefaultLayout(AppState& appState)
{
    HelloImGui::DockingParams dockingParams;
    // dockingParams.layoutName = "Default"; // By default, the layout name is already "Default"
    dockingParams.dockingSplits = CreateDefaultDockingSplits();
    dockingParams.dockableWindows = CreateDockableWindows(appState);
    return dockingParams;
}

std::vector<HelloImGui::DockingParams> CreateAlternativeLayouts(AppState& appState)
{
    HelloImGui::DockingParams alternativeLayout;
    {
        alternativeLayout.layoutName = "Alternative Layout";
        alternativeLayout.dockingSplits = CreateAlternativeDockingSplits();
        alternativeLayout.dockableWindows = CreateDockableWindows(appState);
    }
    HelloImGui::DockingParams tabsLayout;
    {
        tabsLayout.layoutName = "Tabs Layout";
        tabsLayout.dockableWindows = CreateDockableWindows(appState);
        // Force all windows to be presented in the MainDockSpace
        for (auto& window: tabsLayout.dockableWindows)
            window.dockSpaceName = "MainDockSpace";
        // In "Tabs Layout", no split is created
        tabsLayout.dockingSplits = {};
    }
    return {alternativeLayout, tabsLayout};
}


//////////////////////////////////////////////////////////////////////////
//    main(): here, we simply fill RunnerParams, then run the application
//////////////////////////////////////////////////////////////////////////
int main(int, char**)
{
    ChdirBesideAssetsFolder();

    //#############################################################################################
    // Part 1: Define the application state, fill the status and menu bars, load additional font
    //#############################################################################################

    // Our application state
    AppState appState;

    // Hello ImGui params (they hold the settings as well as the Gui callbacks)
    HelloImGui::RunnerParams runnerParams;

    runnerParams.appWindowParams.windowTitle = "Docking Demo";
    runnerParams.imGuiWindowParams.menuAppTitle = "Docking Demo";
    runnerParams.appWindowParams.windowGeometry.size = {1000, 900};
    runnerParams.appWindowParams.restorePreviousGeometry = true;
    runnerParams.appWindowParams.borderless = true;
    runnerParams.appWindowParams.borderlessMovable = true;
    runnerParams.appWindowParams.borderlessResizable = true;
    runnerParams.appWindowParams.borderlessClosable = true;

    // Set LoadAdditionalFonts callback
    runnerParams.callbacks.LoadAdditionalFonts = [&appState]() { LoadFonts(appState); };

    //
    // Status bar
    //
    // We use the default status bar of Hello ImGui
    runnerParams.imGuiWindowParams.showStatusBar = true;
    // Add custom widgets in the status bar
    runnerParams.callbacks.ShowStatus = [&appState]() { StatusBarGui(appState); };
    // uncomment next line in order to hide the FPS in the status bar
    // runnerParams.imGuiWindowParams.showStatusFps = false;

    //
    // Menu bar
    //
    // Here, we fully customize the menu bar:
    // by setting `showMenuBar` to true, and `showMenu_App` and `showMenu_View` to false,
    // HelloImGui will display an empty menu bar, which we can fill with our own menu items via the callback `ShowMenus`
    runnerParams.imGuiWindowParams.showMenuBar = true;
    runnerParams.imGuiWindowParams.showMenu_App = false;
    runnerParams.imGuiWindowParams.showMenu_View = false;
    // Inside `ShowMenus`, we can call `HelloImGui::ShowViewMenu` and `HelloImGui::ShowAppMenu` if desired
    runnerParams.callbacks.ShowMenus = [&runnerParams]() {ShowMenuGui(runnerParams);};
    // Optional: add items to Hello ImGui default App menu
    runnerParams.callbacks.ShowAppMenuItems = ShowAppMenuItems;

    //
    // Top and bottom toolbars
    //
    // toolbar options
    HelloImGui::EdgeToolbarOptions edgeToolbarOptions;
    edgeToolbarOptions.sizeEm = 2.5f;
    edgeToolbarOptions.WindowBg = ImVec4(0.8f, 0.8f, 0.8f, 0.35f);
    // top toolbar
    runnerParams.callbacks.AddEdgeToolbar(
        HelloImGui::EdgeToolbarType::Top,
        [&appState]() { ShowTopToolbar(appState); },
        edgeToolbarOptions
    );
    // right toolbar
    edgeToolbarOptions.WindowBg.w = 0.4f;
    runnerParams.callbacks.AddEdgeToolbar(
        HelloImGui::EdgeToolbarType::Right,
        [&appState]() { ShowRightToolbar(appState); },
        edgeToolbarOptions
    );

    //
    // Load user settings at callbacks `PostInit` and save them at `BeforeExit`
    //
    runnerParams.callbacks.PostInit = [&appState]   { LoadMyAppSettings(appState);};
    runnerParams.callbacks.BeforeExit = [&appState] { SaveMyAppSettings(appState);};

    //
    // Change style
    //
    // 1. Change theme
    auto& tweakedTheme = runnerParams.imGuiWindowParams.tweakedTheme;
    tweakedTheme.Theme = ImGuiTheme::ImGuiTheme_MaterialFlat;
    tweakedTheme.Tweaks.Rounding = 10.f;
    // 2. Customize ImGui style at startup
    runnerParams.callbacks.SetupImGuiStyle = []() {
        // Reduce spacing between items ((8, 4) by default)
        ImGui::GetStyle().ItemSpacing = ImVec2(6.f, 4.f);
    };

    //#############################################################################################
    // Part 2: Define the application layout and windows
    //#############################################################################################

    // First, tell HelloImGui that we want full screen dock space (this will create "MainDockSpace")
    runnerParams.imGuiWindowParams.defaultImGuiWindowType = HelloImGui::DefaultImGuiWindowType::ProvideFullScreenDockSpace;
    // In this demo, we also demonstrate multiple viewports: you can drag windows outside out the main window in order to put their content into new native windows
    runnerParams.imGuiWindowParams.enableViewports = true;
    // Set the default layout (this contains the default DockingSplits and DockableWindows)
    runnerParams.dockingParams = CreateDefaultLayout(appState);
    // Add alternative layouts
    runnerParams.alternativeDockingLayouts = CreateAlternativeLayouts(appState);

    // uncomment the next line if you want to always start with the layout defined in the code
    //     (otherwise, modifications to the layout applied by the user layout will be remembered)
    // runnerParams.dockingParams.layoutCondition = HelloImGui::DockingLayoutCondition::ApplicationStart;

    //#############################################################################################
    // Part 3: Where to save the app settings
    //#############################################################################################
    // tag::app_settings[]
    // By default, HelloImGui will save the settings in the current folder.
    // This is convenient when developing, but not so much when deploying the app.
    // You can tell HelloImGui to save the settings in a specific folder: choose between
    //         CurrentFolder
    //         AppUserConfigFolder
    //         AppExecutableFolder
    //         HomeFolder
    //         TempFolder
    //         DocumentsFolder
    //
    // Note: AppUserConfigFolder is:
    //         AppData under Windows (Example: C:\Users\[Username]\AppData\Roaming)
    //         ~/.config under Linux
    //         "~/Library/Application Support" under macOS or iOS
    runnerParams.iniFolderType = HelloImGui::IniFolderType::AppUserConfigFolder;

    // runnerParams.iniFilename: this will be the name of the ini file in which the settings
    // will be stored.
    // In this example, the subdirectory Docking_Demo will be created under the folder defined
    // by runnerParams.iniFolderType.
    //
    // Note: if iniFilename is left empty, the name of the ini file will be derived
    // from appWindowParams.windowTitle
    runnerParams.iniFilename = "Docking_Demo/Docking_demo.ini";
    // end::app_settings[]


    //#############################################################################################
    // Part 4: Run the app
    //#############################################################################################
    HelloImGui::Run(runnerParams); // Note: with ImGuiBundle, it is also possible to use ImmApp::Run(...)


    return 0;
}
Click to see its source code in Python

Python:

# A more complex app demo
#
# It demonstrates how to:
# - set up a complex docking layouts (with several possible layouts):
# - load additional fonts, possibly colored, and with emojis
# - display a log window
# - use the status bar
# - use default menus (App and view menu), and how to customize them
# - use a specific application state (instead of using static variables)
# - save some additional user settings within imgui ini file
# - use borderless windows, that are movable and resizable


from enum import Enum
import time

from imgui_bundle import hello_imgui, icons_fontawesome, imgui, immapp, imgui_ctx, ImVec4
from imgui_bundle.demos_python import demo_utils
from typing import List


##########################################################################
#    Our Application State
##########################################################################
class MyAppSettings:
    name: str = "Test"
    value: int = 10


class RocketState(Enum):
    Init = 0
    Preparing = 1
    Launched = 2


# Struct that holds the application's state
class AppState:
    f: float
    counter: int
    rocket_progress: float
    my_app_settings: MyAppSettings
    rocket_state: RocketState
    rocket_launch_time: float

    title_font: imgui.ImFont
    color_font: imgui.ImFont
    emoji_font: imgui.ImFont
    large_icon_font: imgui.ImFont

    def __init__(self):
        self.f = 0
        self.counter = 0
        self.rocket_progress = 0.0
        self.rocket_launch_time = 0.0
        self.my_app_settings = MyAppSettings()
        self.rocket_state = RocketState.Init


##########################################################################
#    Additional fonts handling
##########################################################################
def load_fonts(app_state: AppState):  # This is called by runnerParams.callbacks.LoadAdditionalFonts
    # First, load the default font (the default font should be loaded first)
    hello_imgui.imgui_default_settings.load_default_font_with_font_awesome_icons()
    # Then load the title font
    app_state.title_font = hello_imgui.load_font("fonts/DroidSans.ttf", 18.0)

    font_loading_params_emoji = hello_imgui.FontLoadingParams()
    font_loading_params_emoji.use_full_glyph_range = True
    app_state.emoji_font = hello_imgui.load_font("fonts/NotoEmoji-Regular.ttf", 24., font_loading_params_emoji)

    font_loading_params_color = hello_imgui.FontLoadingParams()
    font_loading_params_color.load_color = True
    app_state.color_font = hello_imgui.load_font("fonts/Playbox/Playbox-FREE.otf", 24., font_loading_params_color)

    font_loading_params_large_icon = hello_imgui.FontLoadingParams()
    font_loading_params_large_icon.use_full_glyph_range = True
    app_state.large_icon_font = hello_imgui.load_font("fonts/fontawesome-webfont.ttf", 24., font_loading_params_large_icon)


##########################################################################
#    Save additional settings in the ini file
##########################################################################
# This demonstrates how to store additional info in the application settings
# Use this sparingly!
# This is provided as a convenience only, and it is not intended to store large quantities of text data.

# Warning, the save/load function below are quite simplistic!
def my_app_settings_to_string(settings: MyAppSettings) -> str:
    r = settings.name + "\n" + str(settings.value)
    return r


def string_to_my_app_settings(s: str) -> MyAppSettings:
    r = MyAppSettings()
    lines = s.splitlines(False)
    if len(lines) >= 2:
        r.name = lines[0]
        r.value = int(lines[1])
    return r


def load_my_app_settings(app_state: AppState):
    """
    Note: load_my_app_settings() and save_my_app_settings() will be called in the callbacks `post_init` & `before_exit`
         runner_params.callbacks.post_init = lambda: load_user_settings(app_state)
         runner_params.callbacks.before_exit = lambda: save_user_settings(app_state)
    """
    app_state.my_app_settings = string_to_my_app_settings(
        hello_imgui.load_user_pref("MyAppSettings")
    )


def save_my_app_settings(app_state: AppState):
    hello_imgui.save_user_pref(
        "MyAppSettings", my_app_settings_to_string(app_state.my_app_settings)
    )


##########################################################################
#    Gui functions used in this demo
##########################################################################
@immapp.static(last_hide_time=1)
def demo_hide_window(app_state: AppState):
    # Display a button that will hide the application window
    imgui.push_font(app_state.title_font)
    imgui.text("Hide app window")
    imgui.pop_font()

    if imgui.button("Hide"):
        demo_hide_window.last_hide_time = time.time()
        hello_imgui.get_runner_params().app_window_params.hidden = True
    if imgui.is_item_hovered():
        imgui.set_tooltip("By clicking this button, you can hide the window for 3 seconds.")
    if demo_hide_window.last_hide_time > 0.0:
        now = time.time()
        if now - demo_hide_window.last_hide_time > 3.0:
            demo_hide_window.last_hide_time = -1.0
            hello_imgui.get_runner_params().app_window_params.hidden = False


# Display a button that will show an additional window
def demo_show_additional_window(app_state: AppState):
    # Notes:
    #     - it is *not* possible to modify the content of the vector runnerParams.dockingParams.dockableWindows
    #       from the code inside a window's `GuiFunction` (since this GuiFunction will be called while iterating
    #       on this vector!)
    #     - there are two ways to dynamically add windows:
    #           * either make them initially invisible, and exclude them from the view menu (such as shown here)
    #           * or modify runnerParams.dockingParams.dockableWindows inside the callback RunnerCallbacks.PreNewFrame
    window_name = "Additional Window"

    imgui.push_font(app_state.title_font)
    imgui.text("Dynamically add window")
    imgui.pop_font()

    if imgui.button("Show additional window"):
        runner_params = hello_imgui.get_runner_params()
        additional_window_ptr = runner_params.docking_params.dockable_window_of_name(
            window_name
        )
        if additional_window_ptr:
            # additional_window_ptr.include_in_view_menu = True
            additional_window_ptr.is_visible = True
    if imgui.is_item_hovered():
        imgui.set_tooltip("By clicking this button, you can show an additional window")


def demo_basic_widgets(app_state: AppState):
    imgui.push_font(app_state.title_font)
    imgui.text("Basic widgets demo")
    imgui.pop_font()

    imgui.begin_group()
    # Edit a float using a slider from 0.0 to 1.0
    changed, app_state.f = imgui.slider_float("float", app_state.f, 0.0, 1.0)
    if changed:
        hello_imgui.log(
            hello_imgui.LogLevel.warning, f"state.f was changed to {app_state.f}"
        )

    # Buttons return true when clicked (most widgets return true when edited/activated)
    if imgui.button("Button"):
        app_state.counter += 1
        hello_imgui.log(hello_imgui.LogLevel.info, "Button was pressed")
    imgui.same_line()
    imgui.text(f"counter = {app_state.counter}")
    imgui.end_group()

    if imgui.is_item_hovered():
        imgui.set_tooltip("These widgets will interact with the log window")


def demo_user_settings(app_state: AppState):
    imgui.push_font(app_state.title_font)
    imgui.text("User settings")
    imgui.pop_font()

    imgui.begin_group()
    imgui.set_next_item_width(hello_imgui.em_size(7.0))
    _, app_state.my_app_settings.name = imgui.input_text(
        "Name", app_state.my_app_settings.name
    )
    imgui.set_next_item_width(hello_imgui.em_size(7.0))
    _, app_state.my_app_settings.value = imgui.slider_int(
        "Value", app_state.my_app_settings.value, 0, 100
    )
    imgui.end_group()
    if imgui.is_item_hovered():
        imgui.set_tooltip("The values below are stored in the application settings ini file and restored at startup")


def demo_rocket(app_state: AppState):
    imgui.push_font(app_state.title_font)
    imgui.text("Rocket demo")
    imgui.pop_font()

    imgui.begin_group()
    if app_state.rocket_state == RocketState.Init:
        if imgui.button(f"{icons_fontawesome.ICON_FA_ROCKET} Launch rocket"):
            app_state.rocket_launch_time = time.time()
            app_state.rocket_state = RocketState.Preparing
            hello_imgui.log(hello_imgui.LogLevel.warning, "Rocket is being prepared")
    elif app_state.rocket_state == RocketState.Preparing:
        imgui.text("Please Wait")
        app_state.rocket_progress = (time.time() - app_state.rocket_launch_time) / 3.0
        if app_state.rocket_progress >= 1.0:
            app_state.rocket_state = RocketState.Launched
            hello_imgui.log(hello_imgui.LogLevel.warning, "Rocket was launched")
    elif app_state.rocket_state == RocketState.Launched:
        imgui.text(f"{icons_fontawesome.ICON_FA_ROCKET} Rocket launched")
        if imgui.button("Reset Rocket"):
            app_state.rocket_state = RocketState.Init
            app_state.rocket_progress = 0.0
    imgui.end_group()
    if imgui.is_item_hovered():
        imgui.set_tooltip("Look at the status bar after clicking")


def demo_docking_flags(app_state: AppState):
    imgui.push_font(app_state.title_font)
    imgui.text("Main dock space node flags")
    imgui.pop_font()
    imgui.text_wrapped(
        """
This will edit the ImGuiDockNodeFlags for "MainDockSpace".
Most flags are inherited by children dock spaces.
        """
    )

    class DockFlagWithInfo:
        def __init__(self, flag, label, tip):
            self.flag = flag
            self.label = label
            self.tip = tip

    all_flags = [
        DockFlagWithInfo(
            imgui.DockNodeFlags_.no_docking_split,
            "NoSplit",
            "prevent Dock Nodes from being split",
        ),
        DockFlagWithInfo(
            imgui.DockNodeFlags_.no_resize,
            "NoResize",
            "prevent Dock Nodes from being resized",
        ),
        DockFlagWithInfo(
            imgui.DockNodeFlags_.auto_hide_tab_bar,
            "AutoHideTabBar",
            "show tab bar only if multiple windows\n"
            + 'You will need to restore the layout after changing (Menu "View/Restore Layout")',
        ),
        DockFlagWithInfo(
            imgui.DockNodeFlags_.no_docking_over_central_node,
            "NoDockingInCentralNode",
            "prevent docking in central node\n(only works with the main dock space)",
        ),
        # DockFlagWithInfo(imgui.DockNodeFlags_.passthru_central_node, "PassthruCentralNode", "advanced"),
    ]

    main_dock_space_node_flags = (
        hello_imgui.get_runner_params().docking_params.main_dock_space_node_flags
    )
    for flag_with_info in all_flags:
        _, main_dock_space_node_flags = imgui.checkbox_flags(
            flag_with_info.label, main_dock_space_node_flags, flag_with_info.flag
        )
        if imgui.is_item_hovered():
            imgui.set_tooltip("%s" % flag_with_info.tip)

    hello_imgui.get_runner_params().docking_params.main_dock_space_node_flags = (
        main_dock_space_node_flags
    )


def gui_window_layout_customization(app_state: AppState):
    imgui.push_font(app_state.title_font)
    imgui.text("Switch between layouts")
    imgui.pop_font()
    imgui.text('with the menu "View/Layouts"')
    if imgui.is_item_hovered():
        imgui.set_tooltip(
            "Each layout remembers separately the modifications applied by the user, \n"
            + "and the selected layout is restored at startup"
        )

    imgui.separator()

    imgui.push_font(app_state.title_font)
    imgui.text("Change the theme")
    imgui.pop_font()
    imgui.text('with the menu "View/Theme"')
    if imgui.is_item_hovered():
        imgui.set_tooltip("The selected theme is remembered and restored at startup")
    imgui.separator()

    demo_docking_flags(app_state)
    imgui.separator()


def demo_assets(app_state: AppState):
    imgui.push_font(app_state.title_font)
    imgui.text("Image From Assets")
    imgui.pop_font()
    hello_imgui.begin_group_column()
    imgui.dummy(hello_imgui.em_to_vec2(0.0, 0.45))
    imgui.text("Hello")
    hello_imgui.end_group_column()
    hello_imgui.image_from_asset("images/world.png", hello_imgui.em_to_vec2(2.5, 2.5))


def demo_fonts(app_state: AppState):
    imgui.push_font(app_state.title_font)
    imgui.text("Fonts")
    imgui.pop_font()

    imgui.text_wrapped("Mix icons " + icons_fontawesome.ICON_FA_SMILE + " and text " + icons_fontawesome.ICON_FA_ROCKET)
    if imgui.is_item_hovered():
        imgui.set_tooltip("Example with Font Awesome Icons")

    imgui.text("Emojis")

    with imgui_ctx.begin_group():
        imgui.push_font(app_state.emoji_font)
        # ✌️ (Victory Hand Emoji)
        imgui.text("\U0000270C\U0000FE0F")
        imgui.same_line()

        # ❤️ (Red Heart Emoji)
        imgui.text("\U00002764\U0000FE0F")
        imgui.same_line()

        # 🌴 (Palm Tree Emoji)
        imgui.text("\U0001F334")
        imgui.same_line()

        # 🚀 (Rocket Emoji)
        imgui.text("\U0001F680")
        imgui.pop_font()

    if imgui.is_item_hovered():
        imgui.set_tooltip("Example with NotoEmoji font")

    imgui.text("Colored Fonts")
    imgui.push_font(app_state.color_font)
    imgui.text("C O L O R !")
    imgui.pop_font()
    if imgui.is_item_hovered():
        imgui.set_tooltip("Example with Playbox-FREE.otf font")


def demo_themes(app_state: AppState):
    imgui.push_font(app_state.title_font)
    imgui.text("Themes")
    imgui.pop_font()

    tweaked_theme = hello_imgui.get_runner_params().imgui_window_params.tweaked_theme

    imgui.begin_group()
    button_size = hello_imgui.em_to_vec2(7.0, 0.0)
    if imgui.button("Cherry", button_size):
        tweaked_theme.theme = hello_imgui.ImGuiTheme_.cherry
        hello_imgui.apply_tweaked_theme(tweaked_theme)
    if imgui.button("DarculaDarker", button_size):
        tweaked_theme.theme = hello_imgui.ImGuiTheme_.darcula_darker
        hello_imgui.apply_tweaked_theme(tweaked_theme)
    imgui.end_group()
    if imgui.is_item_hovered():
        imgui.set_tooltip(
            "There are lots of other themes: look at the menu View/Theme\n"
            "The selected theme is remembered and restored at startup"
        )


def gui_window_demo_features(app_state: AppState):
    demo_fonts(app_state)
    imgui.separator()
    demo_assets(app_state)
    imgui.separator()
    demo_basic_widgets(app_state)
    imgui.separator()
    demo_rocket(app_state)
    imgui.separator()
    demo_user_settings(app_state)
    imgui.separator()
    demo_hide_window(app_state)
    imgui.separator()
    demo_show_additional_window(app_state)
    imgui.separator()
    demo_themes(app_state)
    imgui.separator()


def status_bar_gui(app_state: AppState):
    if app_state.rocket_state == RocketState.Preparing:
        imgui.text("Rocket completion: ")
        imgui.same_line()
        imgui.progress_bar(app_state.rocket_progress, hello_imgui.em_to_vec2(7.0, 1.0))  # type: ignore


def show_menu_gui(runner_params: hello_imgui.RunnerParams):
    hello_imgui.show_app_menu(runner_params)
    hello_imgui.show_view_menu(runner_params)
    if imgui.begin_menu("My Menu"):
        clicked, _ = imgui.menu_item("Test me", "", False)
        if clicked:
            hello_imgui.log(hello_imgui.LogLevel.warning, "It works")
        imgui.end_menu()


def show_app_menu_items():
    clicked, _ = imgui.menu_item("A Custom app menu item", "", False)
    if clicked:
        hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on A Custom app menu item")


def show_top_toolbar(app_state: AppState):
    imgui.push_font(app_state.large_icon_font)
    if imgui.button(icons_fontawesome.ICON_FA_POWER_OFF):
        hello_imgui.get_runner_params().app_shall_exit = True

    imgui.same_line(imgui.get_window_width() - hello_imgui.em_size(7.0))
    if imgui.button(icons_fontawesome.ICON_FA_HOME):
        hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Home in the top toolbar")
    imgui.same_line()
    if imgui.button(icons_fontawesome.ICON_FA_SAVE):
        hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Save in the top toolbar")
    imgui.same_line()
    if imgui.button(icons_fontawesome.ICON_FA_ADDRESS_BOOK):
        hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Address Book in the top toolbar")

    imgui.same_line(imgui.get_window_width() - hello_imgui.em_size(2.0))
    imgui.text(icons_fontawesome.ICON_FA_BATTERY_THREE_QUARTERS)
    imgui.pop_font()


def show_right_toolbar(app_state: AppState):
    imgui.push_font(app_state.large_icon_font)
    if imgui.button(icons_fontawesome.ICON_FA_ARROW_CIRCLE_LEFT):
        hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Circle left in the right toolbar")
    if imgui.button(icons_fontawesome.ICON_FA_ARROW_CIRCLE_RIGHT):
        hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Circle right in the right toolbar")
    imgui.pop_font()


##########################################################################
#    Docking Layouts and Docking windows
##########################################################################

#
# 1. Define the Docking splits (two versions are available)
#
def create_default_docking_splits() -> List[hello_imgui.DockingSplit]:
    # Define the default docking splits,
    # i.e. the way the screen space is split in different target zones for the dockable windows
    # We want to split "MainDockSpace" (which is provided automatically) into three zones, like this:
    #
    #    ___________________________________________
    #    |        |                                |
    #    | Command|                                |
    #    | Space  |    MainDockSpace               |
    #    |        |                                |
    #    |        |                                |
    #    |        |                                |
    #    -------------------------------------------
    #    |     MiscSpace                           |
    #    -------------------------------------------
    #

    # Uncomment the next line if you want to always start with this layout.
    # Otherwise, modifications to the layout applied by the user layout will be remembered.
    # runner_params.docking_params.layout_condition = hello_imgui.DockingLayoutCondition.ApplicationStart

    # Then, add a space named "MiscSpace" whose height is 25% of the app height.
    # This will split the preexisting default dockspace "MainDockSpace" in two parts.
    split_main_misc = hello_imgui.DockingSplit()
    split_main_misc.initial_dock = "MainDockSpace"
    split_main_misc.new_dock = "MiscSpace"
    split_main_misc.direction = imgui.Dir_.down
    split_main_misc.ratio = 0.25

    # Then, add a space to the left which occupies a column whose width is 25% of the app width
    split_main_command = hello_imgui.DockingSplit()
    split_main_command.initial_dock = "MainDockSpace"
    split_main_command.new_dock = "CommandSpace"
    split_main_command.direction = imgui.Dir_.left
    split_main_command.ratio = 0.25

    splits = [split_main_misc, split_main_command]
    return splits


def create_alternative_docking_splits() -> List[hello_imgui.DockingSplit]:
    # Define alternative docking splits for the "Alternative Layout"
    #    ___________________________________________
    #    |                |                        |
    #    | Misc           |                        |
    #    | Space          |    MainDockSpace       |
    #    |                |                        |
    #    -------------------------------------------
    #    |                                         |
    #    |                                         |
    #    |     CommandSpace                        |
    #    |                                         |
    #    -------------------------------------------

    split_main_command = hello_imgui.DockingSplit()
    split_main_command.initial_dock = "MainDockSpace"
    split_main_command.new_dock = "CommandSpace"
    split_main_command.direction = imgui.Dir_.down
    split_main_command.ratio = 0.5

    split_main_misc = hello_imgui.DockingSplit()
    split_main_misc.initial_dock = "MainDockSpace"
    split_main_misc.new_dock = "MiscSpace"
    split_main_misc.direction = imgui.Dir_.left
    split_main_misc.ratio = 0.5

    splits = [split_main_command, split_main_misc]
    return splits


#
# 2. Define the Dockable windows
#
def create_dockable_windows(app_state: AppState) -> List[hello_imgui.DockableWindow]:
    # A features demo window named "FeaturesDemo" will be placed in "CommandSpace".
    # Its Gui is provided by "gui_window_demo_features"
    features_demo_window = hello_imgui.DockableWindow()
    features_demo_window.label = "Features Demo"
    features_demo_window.dock_space_name = "CommandSpace"
    features_demo_window.gui_function = lambda: gui_window_demo_features(app_state)

    # A layout customization window will be placed in "MainDockSpace".
    # Its Gui is provided by "gui_window_layout_customization"
    layout_customization_window = hello_imgui.DockableWindow()
    layout_customization_window.label = "Layout customization"
    layout_customization_window.dock_space_name = "MainDockSpace"
    layout_customization_window.gui_function = lambda: gui_window_layout_customization(app_state)

    # A Log window named "Logs" will be placed in "MiscSpace". It uses the HelloImGui logger gui
    logs_window = hello_imgui.DockableWindow()
    logs_window.label = "Logs"
    logs_window.dock_space_name = "MiscSpace"
    logs_window.gui_function = hello_imgui.log_gui

    # A Window named "Dear ImGui Demo" will be placed in "MainDockSpace"
    dear_imgui_demo_window = hello_imgui.DockableWindow()
    dear_imgui_demo_window.label = "Dear ImGui Demo"
    dear_imgui_demo_window.dock_space_name = "MainDockSpace"
    dear_imgui_demo_window.imgui_window_flags = imgui.WindowFlags_.menu_bar
    dear_imgui_demo_window.gui_function = imgui.show_demo_window  # type: ignore

    # additional_window is initially not visible (and not mentioned in the view menu).
    # it will be opened only if the user chooses to display it
    additional_window = hello_imgui.DockableWindow()
    additional_window.label = "Additional Window"
    additional_window.is_visible = False  # this window is initially hidden,
    additional_window.include_in_view_menu = False  # it is not shown in the view menu,
    additional_window.remember_is_visible = (
        False  # its visibility is not saved in the settings file,
    )
    additional_window.dock_space_name = (
        "MiscSpace"  # when shown, it will appear in MiscSpace.
    )
    additional_window.gui_function = lambda: imgui.text("This is the additional window")

    dockable_windows = [
        features_demo_window,
        layout_customization_window,
        logs_window,
        dear_imgui_demo_window,
        additional_window,
    ]
    return dockable_windows


#
# 3. Define the layouts:
# A layout is stored inside DockingParams, and stores the splits + the dockable windows.
# Here, we provide the default layout, and two alternative layouts.
def create_default_layout(app_state: AppState) -> hello_imgui.DockingParams:
    docking_params = hello_imgui.DockingParams()
    # By default, the layout name is already "Default"
    # docking_params.layout_name = "Default"
    docking_params.docking_splits = create_default_docking_splits()
    docking_params.dockable_windows = create_dockable_windows(app_state)
    return docking_params


def create_alternative_layouts(app_state: AppState) -> List[hello_imgui.DockingParams]:
    alternative_layout = hello_imgui.DockingParams()
    alternative_layout.layout_name = "Alternative Layout"
    alternative_layout.docking_splits = create_alternative_docking_splits()
    alternative_layout.dockable_windows = create_dockable_windows(app_state)

    tabs_layout = hello_imgui.DockingParams()
    tabs_layout.layout_name = "Tabs Layout"
    tabs_layout.dockable_windows = create_dockable_windows(app_state)
    # Force all windows to be presented in the MainDockSpace
    for window in tabs_layout.dockable_windows:
        window.dock_space_name = "MainDockSpace"
    # In "Tabs Layout", no split is created
    tabs_layout.docking_splits = []

    return [alternative_layout, tabs_layout]


##########################################################################
#    main(): here, we simply fill RunnerParams, then run the application
##########################################################################
def main():
    # By default, an assets folder is installed via pip inside site-packages/lg_imgui_bundle/assets
    # and provides two fonts (fonts/DroidSans.ttf and fonts/fontawesome-webfont.ttf)
    # If you need to add more assets, make a copy of this assets folder and add your own files,
    # and call set_assets_folder
    hello_imgui.set_assets_folder(demo_utils.demos_assets_folder())

    #
    # Part 1: Define the application state, fill the status and menu bars, and load additional font
    #

    # Our application state
    app_state = AppState()

    # Hello ImGui params (they hold the settings as well as the Gui callbacks)
    runner_params = hello_imgui.RunnerParams()
    runner_params.app_window_params.window_title = "Docking Demo"
    runner_params.imgui_window_params.menu_app_title = "Docking Demo"
    runner_params.app_window_params.window_geometry.size = (1000, 900)
    runner_params.app_window_params.restore_previous_geometry = True
    runner_params.app_window_params.borderless = True
    runner_params.app_window_params.borderless_movable = True
    runner_params.app_window_params.borderless_resizable = True
    runner_params.app_window_params.borderless_closable = True

    # Set LoadAdditionalFonts callback
    runner_params.callbacks.load_additional_fonts = lambda: load_fonts(app_state)

    #
    # Status bar
    #
    # We use the default status bar of Hello ImGui
    runner_params.imgui_window_params.show_status_bar = True
    # Add custom widgets in the status bar
    runner_params.callbacks.show_status = lambda: status_bar_gui(app_state)
    # uncomment next line in order to hide the FPS in the status bar
    # runner_params.im_gui_window_params.show_status_fps = False

    #
    # Menu bar
    #
    # Here, we fully customize the menu bar:
    # by setting `show_menu_bar` to True, and `show_menu_app` and `show_menu_view` to False,
    # HelloImGui will display an empty menu bar, which we can fill with our own menu items via the callback `show_menus`
    runner_params.imgui_window_params.show_menu_bar = True
    runner_params.imgui_window_params.show_menu_app = False
    runner_params.imgui_window_params.show_menu_view = False
    # Inside `show_menus`, we can call `hello_imgui.show_view_menu` and `hello_imgui.show_app_menu` if desired
    runner_params.callbacks.show_menus = lambda: show_menu_gui(runner_params)
    # Optional: add items to Hello ImGui default App menu
    runner_params.callbacks.show_app_menu_items = show_app_menu_items

    #
    # Top and bottom toolbars
    #
    # toolbar options
    edge_toolbar_options = hello_imgui.EdgeToolbarOptions()
    edge_toolbar_options.size_em = 2.5
    edge_toolbar_options.window_bg = ImVec4(0.8, 0.8, 0.8, 0.35)
    # top toolbar
    runner_params.callbacks.add_edge_toolbar(
        hello_imgui.EdgeToolbarType.top,
        lambda: show_top_toolbar(app_state),
        edge_toolbar_options,
    )
    # right toolbar
    edge_toolbar_options.window_bg.w = 0.4
    runner_params.callbacks.add_edge_toolbar(
        hello_imgui.EdgeToolbarType.right,
        lambda: show_right_toolbar(app_state),
        edge_toolbar_options,
    )

    #
    # Load user settings at callbacks `post_init` and save them at `before_exit`
    #
    runner_params.callbacks.post_init = lambda: load_my_app_settings(app_state)
    runner_params.callbacks.before_exit = lambda: save_my_app_settings(app_state)

    #
    # Change style
    #
    # 1. Change theme
    tweaked_theme = runner_params.imgui_window_params.tweaked_theme
    tweaked_theme.theme = hello_imgui.ImGuiTheme_.material_flat
    tweaked_theme.tweaks.rounding = 10.0
    # 2. Customize ImGui style at startup
    def setup_imgui_style():
        # Reduce spacing between items ((8, 4) by default)
        imgui.get_style().item_spacing = (6, 4)
    runner_params.callbacks.setup_imgui_style = setup_imgui_style

    #
    # Part 2: Define the application layout and windows
    #

    # First, tell HelloImGui that we want full screen dock space (this will create "MainDockSpace")
    runner_params.imgui_window_params.default_imgui_window_type = (
        hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space
    )
    # In this demo, we also demonstrate multiple viewports: you can drag windows outside
    # out the main window in order to put their content into new native windows
    runner_params.imgui_window_params.enable_viewports = True
    # Set the default layout (this contains the default DockingSplits and DockableWindows)
    runner_params.docking_params = create_default_layout(app_state)
    # Add alternative layouts
    runner_params.alternative_docking_layouts = create_alternative_layouts(app_state)

    #
    # Part 3: Where to save the app settings
    #
    # tag::app_settings[]
    # By default, HelloImGui will save the settings in the current folder.
    # This is convenient when developing, but not so much when deploying the app.
    # You can tell HelloImGui to save the settings in a specific folder: choose between
    #         current_folder
    #         app_user_config_folder
    #         app_executable_folder
    #         home_folder
    #         temp_folder
    #         documents_folder
    #
    # Note: app_user_config_folder is:
    #         AppData under Windows (Example: C:\Users\[Username]\AppData\Roaming)
    #         ~/.config under Linux
    #         "~/Library/Application Support" under macOS or iOS
    runner_params.ini_folder_type = hello_imgui.IniFolderType.app_user_config_folder

    # runnerParams.ini_filename: this will be the name of the ini file in which the settings
    # will be stored.
    # In this example, the subdirectory Docking_Demo will be created under the folder defined
    # by runnerParams.ini_folder_type.
    #
    # Note: if ini_filename is left empty, the name of the ini file will be derived
    # from app_window_params.window_title
    runner_params.ini_filename = "Docking_Demo/Docking_demo.ini"
    # end::app_settings[]

    #
    # Part 4: Run the app
    #
    hello_imgui.run(runner_params)


if __name__ == "__main__":
    main()

Multiplatform C++ applications

When developing C++ applications, Hello ImGui and Dear ImGui Bundle offer an excellent support for multiplatform applications.

See this tutorial video for Hello ImGui:

10' demo video showcasing multi-platform support and rapid
Tip
The principle with Dear ImGui Bundle is the same, just use the dedicated Dear ImGui Bundle project template, and use imgui_bundle_add_app

Custom 3D Background

demo custom background
Figure 5. Custom 3D Background
Tip
As shown in the screenshot, Hello ImGui is able to display a custom 3D scene in the background. This is done by using a dedicated callback.

This demonstration showcases how to:

  • Display a 3D scene in the background via the callback runnerParams.callbacks.CustomBackground

  • Load and compile a shader

  • Adjust uniforms in the GUI

Its source code is heavily documented and should be self-explanatory.

Test & Automation with ImGui Test Engine

demo testengine
Figure 6. ImmGui Test Engine in action

ImGui Test Engine is a Tests & Automation Engine for Dear ImGui.

This demo source code is heavily documented and should be self-explanatory. It shows how to:

  • enable ImGui Test Engine via RunnerParams.use_imgui_test_engine

  • define a callback where the tests are registered (runner_params.callbacks.register_tests)

  • create tests, and:

  • manipulate custom variables

  • check that simulated actions do modify those variables

Note
See Dear ImGui Test Engine License. (TL;DR: free for individuals, educational, open-source and small businesses uses. Paid for larger businesses)
Click to see its source code in C++

C++

// A demo app that demonstrates how to use ImGui Test Engine (https://github.com/ocornut/imgui_test_engine)
//
// It demonstrates how to:
// - enable ImGui Test Engine via runnerParams.useImGuiTestEngine
// - define a callback where the tests are registered (runnerParams.callbacks.RegisterTests)
// - create tests, and:
//   - automate actions using "named references" (see https://github.com/ocornut/imgui_test_engine/wiki/Named-References)
//   - display an optional custom GUI for a test
//   - manipulate custom variables
//   - check that simulated actions do modify those variables
//
// Important note: ImGui Test Engine falls under the Dear ImGui Test Engine License
//    See: https://github.com/ocornut/imgui_test_engine/blob/main/imgui_test_engine/LICENSE.txt
//    TL;DR: free for individuals, educational, open-source and small businesses uses.
//           Paid for larger businesses. Read license for details.
//           License sales to larger businesses are used to fund and sustain the development of Dear ImGui.

#include "immapp/immapp.h"
#include "imgui.h"
#include "imgui_test_engine/imgui_te_engine.h"
#include "imgui_test_engine/imgui_te_context.h"
#include "imgui_test_engine/imgui_te_ui.h"


#include <vector>

// Our tests, that will automate the application
ImGuiTest* testOpenPopup;
ImGuiTest* testCaptureScreenshot;
ImGuiTest* testCustomGui;


// This function is called at startup and will instantiate the tests
void MyRegisterTests()
{
    ImGuiTestEngine* engine = HelloImGui::GetImGuiTestEngine();

    // Demo 1: Open popup
    testOpenPopup = IM_REGISTER_TEST(engine, "Demo Tests", "Open Popup");
    auto testOpenPopupFunc = [](ImGuiTestContext* ctx) {
        // This is the function that will be called by our test
        ctx->SetRef("Dear ImGui Demo");              // From now on, all actions happen in the "Dear ImGui Demo" window
        ctx->ItemOpen("**/Popups & Modal windows");     // Open the "Popups & Modal windows" tree item
        ctx->ItemOpen("**/Modals");                     // Open the "Modal" tree item
        ctx->ItemClick("**/Delete..");               // Click the "Delete.." button ("**" means: search inside children)
        ctx->ItemClick("//Delete?/Cancel");          // Click the "Cancel" button:
        //    here, "//"  means "ignore previous set_ref" and search
        //    for the cancel button in the root popup window named "Delete?"
        ctx->ItemClose("**/Popups & Modal windows");    // Close the "Popups & Modal windows" tree item
    };
    // Let the test call our function
    testOpenPopup->TestFunc = testOpenPopupFunc;

    // Demo 2: Capture Dear ImGui Demo window
    testCaptureScreenshot = IM_REGISTER_TEST(engine, "Demo Tests", "Capture Screenshot");
    auto testCaptureScreenshotFunc = [](ImGuiTestContext* ctx)
    {
        ctx->SetRef("Dear ImGui Demo");                   // From now on, actions happen in the "Dear ImGui Demo" window
        ctx->ItemOpen("**/Widgets");                         // Open the "Widgets", then "Basic" tree item
        ctx->ItemOpenAll("**/Basic");
        ctx->CaptureScreenshotWindow("Dear ImGui Demo"); // Capture window and save screenshot
        ctx->ItemClose("**/Widgets");
    };
    testCaptureScreenshot->TestFunc = testCaptureScreenshotFunc;

    // Demo 3: a test with a custom GUI and custom variables
    // which asserts that simulated actions successfully changed the variables values
    testCustomGui = IM_REGISTER_TEST(engine, "Demo Tests", "Test custom GUI & vars");
    // Our custom variables container
    struct TestVar2 {
        int myInt = 42;
    };
    testCustomGui->SetVarsDataType<TestVar2>();
    auto testCustomGuiFunc = [](ImGuiTestContext* ctx)
    {
        // Custom GUI for this test: it can edit our custom variable
        TestVar2& vars = ctx->GetVars<TestVar2>();
        ImGui::SetNextWindowSize(HelloImGui::EmToVec2(40, 8));
        ImGui::Begin("Custom Gui Test Window", nullptr, ImGuiWindowFlags_NoSavedSettings);
        ImGui::SliderInt("Slider", &vars.myInt, 0, 1000);
        ImGui::End();
    };
    auto testWithVarsTestFunc = [](ImGuiTestContext* ctx){
        // Our test, that will perform actions in the custom GUI, and assert that actions do change the custom variables
        TestVar2& vars = ctx->GetVars<TestVar2>();
        ctx->SetRef("Custom Gui Test Window");
        IM_CHECK_EQ(vars.myInt, 42);
        ctx->ItemInputValue("Slider", 123);
        IM_CHECK_EQ(vars.myInt, 123);
    };
    // Let the test call our test function, and also call our custom GUI
    testCustomGui->TestFunc = testWithVarsTestFunc;
    testCustomGui->GuiFunc = testCustomGuiFunc;
}


// Our application GUI: shows that we can trigger the test manually
void MyGui()
{
    ImGuiTestEngine* testEngine = HelloImGui::GetImGuiTestEngine();
    if (ImGui::Button("Run \"Open popup\""))
        ImGuiTestEngine_QueueTest(testEngine, testOpenPopup);
    if (ImGui::Button("Run \"Capture Screenshot\""))
        ImGuiTestEngine_QueueTest(testEngine, testCaptureScreenshot);
    if (ImGui::Button("Run \"Test custom GUI & vars\""))
        ImGuiTestEngine_QueueTest(testEngine, testCustomGui);

    ImGuiTestEngineIO& engineIo = ImGuiTestEngine_GetIO(testEngine);
    ImGui::Text("Test speed:");
    if (ImGui::Button("Fast"))
        engineIo.ConfigRunSpeed = ImGuiTestRunSpeed_Fast;
    ImGui::SameLine();
    if (ImGui::Button("Normal"))
        engineIo.ConfigRunSpeed = ImGuiTestRunSpeed_Normal;
    ImGui::SameLine();
    if (ImGui::Button("Cinematic"))
        engineIo.ConfigRunSpeed = ImGuiTestRunSpeed_Cinematic;
}

// Defined later: helps to define the application layout, display the ImGui Demo, & ImGui Test Engine Window
void ApplyApplicationLayout(HelloImGui::RunnerParams* runnerParams);


// Our main function, where we need to:
// - instantiate RunnerParams
// - set `runnerParams.useImGuiTestEngine = true`
// - fill `runnerParams.callbacks.registerTests`
int main(int, const char**)
{
    // Instantiate RunnerParams
    HelloImGui::RunnerParams runnerParams;

    // Apply the application layout configuration
    ApplyApplicationLayout(&runnerParams);

    // Enable ImGui Test Engine
    runnerParams.useImGuiTestEngine = true;

    // Set the test registration function
    runnerParams.callbacks.RegisterTests = MyRegisterTests;

    // Run the ImGui application
    HelloImGui::Run(runnerParams);
}


///////////////////////////////////////////////////////////////////////////////
// End of demo code
///////////////////////////////////////////////////////////////////////////////


// Define the default docking splits for the application layout
std::vector<HelloImGui::DockingSplit> CreateDefaultDockingSplits()
{
    // Define the application layout: split the window into 3 spaces
    HelloImGui::DockingSplit splitMainDemo;
    splitMainDemo.initialDock = "MainDockSpace";
    splitMainDemo.newDock = "ImGuiDemoSpace";
    splitMainDemo.direction = ImGuiDir_Right;
    splitMainDemo.ratio = 0.5f;

    HelloImGui::DockingSplit splitMainTest;
    splitMainTest.initialDock = "MainDockSpace";
    splitMainTest.newDock = "TestEngineSpace";
    splitMainTest.direction = ImGuiDir_Down;
    splitMainTest.ratio = 0.7f;

    return {splitMainDemo, splitMainTest};
}

// Define the dockable windows for the application layout
std::vector<HelloImGui::DockableWindow> CreateDockableWindows()
{
    // Define the app windows: MyGui, ImGui Demo Window, Dear ImGui Test Engine
    HelloImGui::DockableWindow myWindow;
    myWindow.label = "Run Demos";
    myWindow.dockSpaceName = "MainDockSpace";
    myWindow.GuiFunction = &MyGui;

    HelloImGui::DockableWindow dearImGuiDemoWindow;
    dearImGuiDemoWindow.label = "Dear ImGui Demo";
    dearImGuiDemoWindow.dockSpaceName = "ImGuiDemoSpace";
    dearImGuiDemoWindow.GuiFunction = []() { ImGui::ShowDemoWindow(); };

    HelloImGui::DockableWindow testEngineWindow;
    testEngineWindow.label = "Dear ImGui Test Engine";
    testEngineWindow.dockSpaceName = "TestEngineSpace";
    testEngineWindow.GuiFunction = []() { ImGuiTestEngine_ShowTestEngineWindows(HelloImGui::GetImGuiTestEngine(), nullptr); };

    return {myWindow, dearImGuiDemoWindow, testEngineWindow};
}


// Apply the application layout and windows to the runner parameters
void ApplyApplicationLayout(HelloImGui::RunnerParams* runnerParams)
{
    // Define the application layout and windows
    runnerParams->appWindowParams.windowTitle = "Demo ImGui Test Engine";
    runnerParams->imGuiWindowParams.defaultImGuiWindowType =
        HelloImGui::DefaultImGuiWindowType::ProvideFullScreenDockSpace;
    runnerParams->dockingParams.dockingSplits = CreateDefaultDockingSplits();
    runnerParams->dockingParams.dockableWindows = CreateDockableWindows();
    runnerParams->dockingParams.layoutCondition = HelloImGui::DockingLayoutCondition::ApplicationStart;
}
Click to see its source code in Python

Python:

# A demo app that demonstrates how to use ImGui Test Engine (https://github.com/ocornut/imgui_test_engine)
#
# It demonstrates how to:
# - enable ImGui Test Engine via RunnerParams.use_imgui_test_engine
# - define a callback where the tests are registered (runner_params.callbacks.register_tests)
# - create tests, and:
#   - automate actions using "named references" (see https://github.com/ocornut/imgui_test_engine/wiki/Named-References)
#   - display an optional custom GUI for a test
#   - manipulate custom variables
#   - check that simulated actions do modify those variables
#
# Important note: ImGui Test Engine falls under the Dear ImGui Test Engine License
#     See: https://github.com/ocornut/imgui_test_engine/blob/main/imgui_test_engine/LICENSE.txt
#     TL;DR: free for individuals, educational, open-source and small businesses uses.
#            Paid for larger businesses. Read license for details.
#            License sales to larger businesses are used to fund and sustain the development of Dear ImGui.


from imgui_bundle import imgui, hello_imgui
from imgui_bundle.imgui.test_engine_checks import CHECK
from typing import List


# Our tests, that will automate the application
test_open_popup: imgui.test_engine.Test
test_capture_screenshot: imgui.test_engine.Test
test_custom_gui = imgui.test_engine.Test


# This function is called at startup and will instantiate the tests
def my_register_tests():
    # fmt: off
    global test_open_popup, test_capture_screenshot, test_custom_gui
    engine = hello_imgui.get_imgui_test_engine()

    # Demo 1: Open popup
    test_open_popup = imgui.test_engine.register_test(engine, "Demo Tests", "Open Popup")
    def test_open_popup_func(ctx: imgui.test_engine.TestContext) -> None:
        # This is the function that will be called by our test
        ctx.set_ref("Dear ImGui Demo")              # From now on, all actions happen in the "Dear ImGui Demo" window
        ctx.item_open("**/Popups & Modal windows")     # Open the "Popups & Modal windows" tree item
        ctx.item_open("**/Modals")                     # Open the "Modal" tree item
        ctx.item_click("**/Delete..")               # Click the "Delete.." button ("**" means: search inside children)
        ctx.item_click("//Delete?/Cancel")          # Click the "Cancel" button:
                                                    #    here, "//"  means "ignore previous set_ref" and search
                                                    #    for the cancel button in the root popup window named "Delete?"
        ctx.item_close("**/Popups & Modal windows")    # Close the "Popups & Modal windows" tree item
    # let the test call our function
    test_open_popup.test_func = test_open_popup_func

    # Demo 2 : Capture Dear ImGui Demo window
    test_capture_screenshot = imgui.test_engine.register_test(engine, "Demo Tests", "Capture Screenshot")
    def test_capture_screenshot_func(ctx: imgui.test_engine.TestContext) -> None:
        ctx.set_ref("Dear ImGui Demo")                   # From now on, actions happen in the "Dear ImGui Demo" window
        ctx.item_open("**/Widgets")                         # Open the "Widgets", then "Basic" tree item
        ctx.item_open_all("**/Basic")
        ctx.capture_screenshot_window("Dear ImGui Demo") # Capture window and save screenshot
        ctx.item_close("**/Widgets")
    test_capture_screenshot.test_func = test_capture_screenshot_func

    # Demo 3: a test with a custom GUI and custom variables
    #         which asserts that simulated actions successfully changed the variables values
    test_custom_gui = imgui.test_engine.register_test(
        engine, "Demo Tests", "Test custom GUI & vars"
    )
    # Our custom variables container
    class TestVar2:
        my_int = 42

    test_var2 = TestVar2()  # our custom variable(s)

    def test_custom_gui_func(ctx: imgui.test_engine.TestContext) -> None:
        # Custom GUI for this test: it can edit our custom variable
        imgui.set_next_window_size(hello_imgui.em_to_vec2(40, 8))
        imgui.begin(
            "Custom Gui Test Window", None, imgui.WindowFlags_.no_saved_settings.value
        )
        _, test_var2.my_int = imgui.slider_int("Slider", test_var2.my_int, 0, 1000)
        imgui.end()

    def test_with_vars_test_func(ctx: imgui.test_engine.TestContext) -> None:
        # Our test, that will perform actions in the custom GUI, and assert that actions do change the custom variables
        # Optional: reset test_var2 to its startup values
        nonlocal test_var2
        test_var2 = TestVar2()
        # Run the test
        ctx.set_ref("Custom Gui Test Window")
        CHECK(test_var2.my_int == 42)
        ctx.item_input_value("Slider", 123)
        CHECK(test_var2.my_int == 123)

    # let the test call our test function, and also call our custom Gui
    test_custom_gui.test_func = test_with_vars_test_func
    test_custom_gui.gui_func = test_custom_gui_func
    # fmt: on


# Our application GUI: shows that we can trigger the test manually
def my_gui():
    test_engine = hello_imgui.get_imgui_test_engine()
    if imgui.button('Run "Open popup"'):
        imgui.test_engine.queue_test(test_engine, test_open_popup)
    if imgui.button('Run "Capture Screenshot"'):
        imgui.test_engine.queue_test(test_engine, test_capture_screenshot)
    if imgui.button('Run "Test custom GUI & vars"'):
        imgui.test_engine.queue_test(test_engine, test_custom_gui)

    engine_io = imgui.test_engine.get_io(test_engine)
    imgui.text("Test speed:")
    if imgui.button("Fast"):
        engine_io.config_run_speed = imgui.test_engine.TestRunSpeed.fast
    imgui.same_line()
    if imgui.button("Normal"):
        engine_io.config_run_speed = imgui.test_engine.TestRunSpeed.normal
    imgui.same_line()
    if imgui.button("Cinematic"):
        engine_io.config_run_speed = imgui.test_engine.TestRunSpeed.cinematic


# Defined later: helps to define the application layout, display the ImGui Demo, & ImGui Test Engine Window
def apply_application_layout(runner_params: hello_imgui.RunnerParams) -> None:
    ...


# Our main  function, where we need to:
#        - instantiate RunnerParams
#        - set `runner_params.use_imgui_test_engine = True`
#        - fill `runner_params.callbacks.register_tests`
def main() -> None:
    runner_params = hello_imgui.RunnerParams()
    apply_application_layout(runner_params)

    runner_params.use_imgui_test_engine = True
    runner_params.callbacks.register_tests = my_register_tests

    hello_imgui.run(runner_params)


# ///////////////////////////////////////////////////////////////////////////////
# // End of demo code
# ///////////////////////////////////////////////////////////////////////////////


# //
# // Note: the code below only helps to
# //    - define the application layout
# //    - display the ImGui Demo Window
# //    - display the ImGui Test Engine Window


def create_default_docking_splits() -> List[hello_imgui.DockingSplit]:
    # Define the application layout: splits the window in 3 spaces
    split_main_demo = hello_imgui.DockingSplit()
    split_main_demo.initial_dock = "MainDockSpace"
    split_main_demo.new_dock = "ImGuiDemoSpace"
    split_main_demo.direction = imgui.Dir_.right
    split_main_demo.ratio = 0.5

    split_main_test = hello_imgui.DockingSplit()
    split_main_test.initial_dock = "MainDockSpace"
    split_main_test.new_dock = "TestEngineSpace"
    split_main_test.direction = imgui.Dir_.down
    split_main_test.ratio = 0.7

    return [split_main_demo, split_main_test]


def create_dockable_windows() -> List[hello_imgui.DockableWindow]:
    # Define the app windows: my_gui, ImGui Demo Window, Dear ImGui Test Engine
    my_window = hello_imgui.DockableWindow()
    my_window.label = "Run Demos"
    my_window.dock_space_name = "MainDockSpace"
    my_window.gui_function = my_gui

    dear_imgui_demo_window = hello_imgui.DockableWindow()
    dear_imgui_demo_window.label = "Dear ImGui Demo"
    dear_imgui_demo_window.dock_space_name = "ImGuiDemoSpace"
    dear_imgui_demo_window.gui_function = imgui.show_demo_window  # type: ignore

    test_engine_window = hello_imgui.DockableWindow()
    test_engine_window.label = "Dear ImGui Test Engine"
    test_engine_window.dock_space_name = "TestEngineSpace"

    def show_test_engine_windows():
        imgui.test_engine.show_test_engine_windows(
            hello_imgui.get_imgui_test_engine(), None
        )

    test_engine_window.gui_function = show_test_engine_windows

    return [my_window, dear_imgui_demo_window, test_engine_window]


def apply_application_layout(runner_params: hello_imgui.RunnerParams) -> None:  # type: ignore # noqa: F811
    # Define the application layout and windows
    runner_params.app_window_params.window_title = "Demo ImGui Test Engine"
    runner_params.imgui_window_params.default_imgui_window_type = (
        hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space
    )
    runner_params.docking_params.docking_splits = create_default_docking_splits()
    runner_params.docking_params.dockable_windows = create_dockable_windows()
    runner_params.docking_params.layout_condition = (
        hello_imgui.DockingLayoutCondition.application_start
    )


if __name__ == "__main__":
    main()

Display & analyze images with ImmVision

demo immvision process 1
Figure 7. Immvision in action
demo immvision process 2
Figure 8. Zooming on the images (with the mouse wheel) to display pixel values

ImmVision, is an immediate image debugger which can display multiple kinds of images (RGB, RGBA, float, etc.), zoom to examine precise pixel values, display float images with a versatile colormap, etc.

This demonstration showcases how to:

  • display two versions of an image, before after an image processing pipeline

  • zoom on specific ROI of those images to see pixel values

  • play with the parameter of the image processing pipeline

Its source code is heavily documented and should be self-explanatory.

Click to see its source code in C++

C++

#include "demo_utils/api_demos.h"
#include "immvision/immvision.h"
#include "immapp/immapp.h"

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>


// The parameters for our image processing pipeline
struct SobelParams
{
    enum class Orientation
    {
        Horizontal,
        Vertical
    };
    float blur_size = 1.25f;
    int deriv_order = 1;  // order of the derivative
    int k_size = 7;  // size of the extended Sobel kernel it must be 1, 3, 5, or 7 (or -1 for Scharr)
    Orientation orientation = Orientation::Vertical;
};


// Our image processing pipeline
cv::Mat ComputeSobel(const cv::Mat& image, const SobelParams& params)
{
    cv::Mat gray;
    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    cv::Mat img_float;
    gray.convertTo(img_float, CV_32F, 1.0 / 255.0);
    cv::Mat blurred;
    cv::GaussianBlur(img_float, blurred, cv::Size(), params.blur_size, params.blur_size);

    double good_scale = 1.0 / std::pow(2.0, (params.k_size - 2 * params.deriv_order - 2));

    int dx, dy;
    if (params.orientation == SobelParams::Orientation::Vertical)
    {
        dx = params.deriv_order;
        dy = 0;
    }
    else
    {
        dx = 0;
        dy = params.deriv_order;
    }
    cv::Mat r;
    cv::Sobel(blurred, r, CV_64F, dx, dy, params.k_size, good_scale);
    return r;
}


// A GUI to edit the parameters for our image processing pipeline
bool GuiSobelParams(SobelParams& params)
{
    bool changed = false;

    // Blur size
    ImGui::SetNextItemWidth(ImmApp::EmSize() * 10);
    if (ImGui::SliderFloat("Blur size", &params.blur_size, 0.5f, 10.0f))
    {
        changed = true;
    }
    ImGui::SameLine();
    ImGui::Text(" | ");
    ImGui::SameLine();

    // Deriv order
    ImGui::Text("Deriv order");
    ImGui::SameLine();
    for (int deriv_order = 1; deriv_order <= 4; ++deriv_order)
    {
        if (ImGui::RadioButton(std::to_string(deriv_order).c_str(), params.deriv_order == deriv_order))
        {
            changed = true;
            params.deriv_order = deriv_order;
        }
        ImGui::SameLine();
    }

    ImGui::Text(" | ");
    ImGui::SameLine();

    ImGui::Text("Orientation");
    ImGui::SameLine();
    if (ImGui::RadioButton("Horizontal", params.orientation == SobelParams::Orientation::Horizontal))
    {
        changed = true;
        params.orientation = SobelParams::Orientation::Horizontal;
    }
    ImGui::SameLine();
    if (ImGui::RadioButton("Vertical", params.orientation == SobelParams::Orientation::Vertical))
    {
        changed = true;
        params.orientation = SobelParams::Orientation::Vertical;
    }

    return changed;
}


// Our Application State contains:
//     - the original & processed image (image & imageSobel)
//     - our parameters for the processing pipeline (sobelParams)
//     - parameters to display the images via ImmVision: they share the same zoom key,
//       so that we can move the two image in sync
struct AppStateProcess {
    cv::Mat image;
    cv::Mat imageSobel;
    SobelParams sobelParams;

    ImmVision::ImageParams immvisionParams;
    ImmVision::ImageParams immvisionParamsSobel;

    AppStateProcess(const std::string& image_file) {
        image = cv::imread(image_file);
        sobelParams = SobelParams();
        imageSobel = ComputeSobel(image, sobelParams);

        immvisionParams = ImmVision::ImageParams();
        immvisionParams.ImageDisplaySize = cv::Size(int(ImmApp::EmSize(22.f)), 0);
        immvisionParams.ZoomKey = "z";

        immvisionParamsSobel = ImmVision::ImageParams();
        immvisionParamsSobel.ImageDisplaySize = cv::Size(int(ImmApp::EmSize(22.f)), 0);
        immvisionParamsSobel.ZoomKey = "z";
        immvisionParamsSobel.ShowOptionsPanel = true;
    }
};


// Our GUI function
//     (which instantiates a static app state at startup)
void demo_immvision_process()
{
    static AppStateProcess appState(DemosAssetsFolder() + "/images/house.jpg");

    ImGuiMd::RenderUnindented(R"(
        This example shows a example of image processing (sobel filter) where you can adjust the params and see their effect in real time.

        * Pan and zoom the image with the mouse and the mouse wheel
        * Apply Colormaps to the filtered image in the options tab.
    )");
    ImGui::Separator();

    if (GuiSobelParams(appState.sobelParams)) {
        appState.imageSobel = ComputeSobel(appState.image, appState.sobelParams);
        appState.immvisionParamsSobel.RefreshImage = true;
    }
    ImmVision::Image("Original", appState.image, &appState.immvisionParams);
    ImGui::SameLine();
    ImmVision::Image("Deriv", appState.imageSobel, &appState.immvisionParamsSobel);
}


// The main function is not present in this file, but it could be written as
//        ImmApp::RunWithMarkdown(demo_immvision_process, "demo_immvision_process");
Click to see its source code in Python

Python:

import os.path

import numpy as np
from typing import Any
from numpy.typing import NDArray
from enum import Enum
import cv2  # type: ignore
import math

from imgui_bundle import imgui, immvision, immapp, imgui_md
from imgui_bundle.demos_python import demo_utils


ImageRgb = NDArray[np.uint8]
ImageFloat = NDArray[np.floating[Any]]


class SobelParams:
    """The parameters for our image processing pipeline"""

    class Orientation(Enum):
        Horizontal = 0
        Vertical = 1

    blur_size = 1.25
    deriv_order = 1  # order of the derivative
    k_size = 7  # size of the extended Sobel kernel it must be 1, 3, 5, or 7 (or -1 for Scharr)
    orientation: Orientation = Orientation.Vertical


def compute_sobel(image: ImageRgb, params: SobelParams) -> ImageFloat:
    """Our image processing pipeline"""
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    img_float = gray / 255.0
    blurred = cv2.GaussianBlur(
        img_float, (0, 0), sigmaX=params.blur_size, sigmaY=params.blur_size
    )

    good_scale = 1.0 / math.pow(2.0, (params.k_size - 2 * params.deriv_order - 2))

    if params.orientation == SobelParams.Orientation.Vertical:
        dx = params.deriv_order
        dy = 0
    else:
        dx = 0
        dy = params.deriv_order
    r = cv2.Sobel(
        blurred, ddepth=cv2.CV_64F, dx=dx, dy=dy, ksize=params.k_size, scale=good_scale
    )
    return r


def gui_sobel_params(params: SobelParams) -> bool:
    """A GUI to edit the parameters for our image processing pipeline"""
    changed = False

    # Blur size
    imgui.set_next_item_width(immapp.em_size() * 10)
    c, params.blur_size = imgui.slider_float("Blur size", params.blur_size, 0.5, 10)
    if c:
        changed = True
    imgui.same_line()
    imgui.text(" | ")
    imgui.same_line()

    # Deriv order
    imgui.text("Deriv order")
    imgui.same_line()
    for deriv_order in (1, 2, 3, 4):
        c, params.deriv_order = imgui.radio_button(
            str(deriv_order), params.deriv_order, deriv_order
        )
        if c:
            changed = True
        imgui.same_line()

    imgui.text(" | ")
    imgui.same_line()

    imgui.text("Orientation")
    imgui.same_line()
    if imgui.radio_button(
        "Horizontal", params.orientation == SobelParams.Orientation.Horizontal
    ):
        changed = True
        params.orientation = SobelParams.Orientation.Horizontal
    imgui.same_line()
    if imgui.radio_button(
        "Vertical", params.orientation == SobelParams.Orientation.Vertical
    ):
        changed = True
        params.orientation = SobelParams.Orientation.Vertical

    return changed


# Our Application State contains:
#     - the original & processed image (image & imageSobel)
#     - our parameters for the processing pipeline (sobelParams)
#     - parameters to display the images via ImmVision: they share the same zoom key,
#       so that we can move the two image in sync
class AppState:
    image: ImageRgb
    image_sobel: ImageFloat
    sobel_params: SobelParams

    immvision_params: immvision.ImageParams
    immvision_params_sobel: immvision.ImageParams

    def __init__(self, image_file: str):
        self.image = cv2.imread(image_file)
        self.sobel_params = SobelParams()
        self.image_sobel = compute_sobel(self.image, self.sobel_params)

        self.immvision_params = immvision.ImageParams()
        self.immvision_params.image_display_size = (int(immapp.em_size(22)), 0)
        self.immvision_params.zoom_key = "z"

        self.immvision_params_sobel = immvision.ImageParams()
        self.immvision_params_sobel.image_display_size = (int(immapp.em_size(22)), 0)
        self.immvision_params_sobel.zoom_key = "z"
        self.immvision_params_sobel.show_options_panel = True


# Our GUI function
#    (which instantiates a static app state at startup)
@immapp.static(app_state=None)
def demo_gui():
    static = demo_gui

    if static.app_state is None:
        this_dir = os.path.dirname(__file__)
        static.app_state = AppState(this_dir + "/../../demos_assets/images/house.jpg")

    imgui_md.render_unindented(
        """
        This example shows a example of image processing (sobel filter) where you can adjust the params and see their effect in real time.

        * Pan and zoom the image with the mouse and the mouse wheel
        * Apply Colormaps to the filtered image in the options tab.
        """
    )
    imgui.separator()

    changed = gui_sobel_params(static.app_state.sobel_params)
    if changed:
        static.app_state.image_sobel = compute_sobel(
            static.app_state.image, static.app_state.sobel_params
        )
    static.app_state.immvision_params_sobel.refresh_image = changed

    immvision.image(
        "Original", static.app_state.image, static.app_state.immvision_params
    )
    imgui.same_line()
    immvision.image(
        "Deriv", static.app_state.image_sobel, static.app_state.immvision_params_sobel
    )


# The main entry point will run our GUI function
if __name__ == "__main__":
    demo_utils.set_hello_imgui_demo_assets_folder()
    immapp.run_with_markdown(demo_gui, window_size=(1000, 1000))

Widgets

Dear ImGui Widgets

demo widgets imgui
Figure 9. Dear ImGui widgets

Dear ImGui provides lots of widgets by default.

ImGui Manual enables you to browse all of them all, while looking at their code.


Additional Widgets

demo widgets knobs
Figure 10. Knobs widget
demo widgets toggle
Figure 11. Toggle widget
demo widgets spinners
Figure 12. Spinners widget
demo widgets file dialog
Figure 13. File dialog
demo widgets coolbar
Figure 14. Cool bar
Click to see the widgets code in C++
// Part of ImGui Bundle - MIT License - Copyright (c) 2022-2024 Pascal Thomet - https://github.com/pthom/imgui_bundle
#include "hello_imgui/hello_imgui.h"
#include "imspinner/imspinner.h"
#include "imgui_toggle/imgui_toggle.h"
#include "imgui_toggle/imgui_toggle_presets.h"
#include "imgui_toggle/imgui_toggle_palette.h"
#include "imgui_toggle/imgui_toggle_renderer.h"
#include "immapp/immapp.h"
#include "portable_file_dialogs/portable_file_dialogs.h"
#include "imgui-command-palette/imcmd_command_palette.h"
#include "imgui-knobs/imgui-knobs.h"
#include "ImGuiColorTextEdit/TextEditor.h"
#ifdef IMGUI_BUNDLE_WITH_IMFILEDIALOG
#include "ImFileDialog/ImFileDialog.h"
#endif
#include "imgui_md_wrapper.h"
#include "ImCoolBar/ImCoolbar.h"
#include "demo_utils/api_demos.h"

#include <fplus/fplus.hpp>
#include <memory>


void DemoKnobs()
{
    ImGuiMd::RenderUnindented(R"(
        # Knobs
        [imgui-knobs](https://github.com/altschuler/imgui-knobs) provides knobs for ImGui.
        )");
    static float knob_float_value = 0.f;
    static int knob_int_value = 0;

    std::vector<std::pair<ImGuiKnobVariant, std::string>> knob_types = {
        {ImGuiKnobVariant_Tick, "tick"},
        {ImGuiKnobVariant_Dot, "dot"},
        {ImGuiKnobVariant_Space, "space"},
        {ImGuiKnobVariant_Stepped, "stepped"},
        {ImGuiKnobVariant_Wiper, "wiper"},
        {ImGuiKnobVariant_WiperDot, "wiper_dot"},
        {ImGuiKnobVariant_WiperOnly, "wiper_only"},
    };

    auto show_float_knobs = [&knob_types](float knob_size)
    {
        std::string knob_size_str = std::to_string(knob_size);
        ImGui::PushID((knob_size_str + "_float").c_str());
        for (const auto& [knob_type, knob_typename] : knob_types)
        {
            ImGuiKnobs::Knob(
                knob_typename.c_str(),
                &knob_float_value,
                /*v_min=*/   0.0f,
                /*v_max=*/   1.0f,
                /*speed=*/   0,
                /*format=*/  "%.2f",
                /*variant=*/ knob_type,
                /*size=*/    knob_size,
                /*flags=*/   0,
                /*steps=*/   100
            );
            ImGui::SameLine();
        }
        ImGui::NewLine();
        ImGui::PopID();
    };


    auto show_int_knobs = [&knob_types](float knob_size)
    {
        std::string knob_size_str = std::to_string(knob_size);
        ImGui::PushID((knob_size_str + "_int").c_str());
        for (const auto& [knob_type, knob_typename] : knob_types)
        {
            ImGuiKnobs::KnobInt(
                knob_typename.c_str(),
                &knob_int_value,
                /*v_min=*/   0.0,
                /*v_max=*/   15,
                /*speed=*/   0,
                /*format=*/  "%02i",
                /*variant=*/ knob_type,
                /*size=*/    knob_size,
                /*flags=*/   0,
                /*steps=*/   10
            );
            ImGui::SameLine();
        }
        ImGui::NewLine();
        ImGui::PopID();
    };

    float knobsSizeSmall = ImmApp::EmSize() * 2.5;
    float knobsSizeBig = knobsSizeSmall * 1.3;

    ImGui::BeginGroup();
    ImGui::Text("Some small knobs");
    show_float_knobs(knobsSizeSmall);
    ImGui::EndGroup();

    ImGui::SameLine();

    ImGui::BeginGroup();
    ImGui::Text("Some big knobs (int values)");
    show_int_knobs(knobsSizeBig);
    ImGui::EndGroup();
}


void DemoSpinner()
{
    ImGuiMd::RenderUnindented(R"(
        # Spinners
        [imspinner](https://github.com/dalerank/imspinner) provides spinners for ImGui.
    )");

    ImColor color(0.3f, 0.5f, 0.9f, 1.f);
    ImGui::Text("spinner_moving_dots");
    ImGui::SameLine();
    ImSpinner::SpinnerMovingDots("spinner_moving_dots", 3.0, color, 28.0);
    ImGui::SameLine();

    float radius = ImGui::GetFontSize() / 1.8f;
    ImGui::Text("spinner_arc_rotation");
    ImGui::SameLine();
    ImSpinner::SpinnerArcRotation("spinner_arc_rotation", radius, 4.0, color);
    ImGui::SameLine();

    float radius1 = ImGui::GetFontSize() / 2.5f;
    ImGui::Text("spinner_ang_triple");
    ImGui::SameLine();
    ImSpinner::SpinnerAngTriple("spinner_ang_triple", radius1, radius1 * 1.5f, radius1 * 2.0f, 2.5f, color, color, color);
}


void DemoToggle()
{
    static bool flag = true;

    ImGuiMd::RenderUnindented(R"(
        # Toggle Switch
        [imgui_toggle](https://github.com/cmdwtf/imgui_toggle) provides toggle switches for ImGui."""
    )");

    bool changed = false;
    changed |= ImGui::Toggle("Default Toggle", &flag);
    ImGui::SameLine();

    changed |= ImGui::Toggle("Animated Toggle", &flag, ImGuiToggleFlags_Animated);
    ImGui::SameLine();

    auto toggle_config = ImGuiTogglePresets::MaterialStyle();
    toggle_config.AnimationDuration = 0.4f;
    changed |= ImGui::Toggle("Material Style (with slowed anim)", &flag, toggle_config);

    ImGui::SameLine();
    changed |= ImGui::Toggle("iOS style", &flag, ImGuiTogglePresets::iOSStyle(0.2f));

    ImGui::SameLine();
    changed |= ImGui::Toggle(
        "iOS style (light)", &flag, ImGuiTogglePresets::iOSStyle(0.2f, true));
}


void DemoPortableFileDialogs()
{
    static std::string lastFileSelection;

    ImGui::PushID("pfd");
    ImGuiMd::RenderUnindented(R"(
        # Portable File Dialogs
         [portable-file-dialogs](https://github.com/samhocevar/portable-file-dialogs) provides native file dialogs
    )");

    auto logResult = [](std::string what) {
        lastFileSelection = what;
    };
    auto logResultList = [](const std::vector<std::string>& whats) {
        lastFileSelection = fplus::join(std::string("\n"), whats);
    };

    static std::unique_ptr<pfd::open_file> openFileDialog;
    if (ImGui::Button("Open File"))
        openFileDialog = std::make_unique<pfd::open_file>("Select file");
    if (openFileDialog.get() && openFileDialog->ready())
    {
        logResultList(openFileDialog->result());
        openFileDialog.reset();
    }

    ImGui::SameLine();


    static std::unique_ptr<pfd::open_file> openFileMultiselect;
    if (ImGui::Button("Open File (multiselect)"))
        openFileMultiselect.reset(new pfd::open_file("Select file", "", {}, pfd::opt::multiselect));
    if (openFileMultiselect.get() && openFileMultiselect->ready())
    {
        logResultList(openFileMultiselect->result());
        openFileMultiselect.reset();
    }

    ImGui::SameLine();

    static std::unique_ptr<pfd::save_file> saveFileDialog;
    if (ImGui::Button("Save File"))
        saveFileDialog = std::make_unique<pfd::save_file>("Save file");
    if (saveFileDialog.get() && saveFileDialog->ready())
    {
        logResult(saveFileDialog->result());
        saveFileDialog.reset();
    }

    ImGui::SameLine();

    static std::unique_ptr<pfd::select_folder> selectFolderDialog;
    if (ImGui::Button("Select Folder"))
        selectFolderDialog = std::make_unique<pfd::select_folder>("Select folder");
    if (selectFolderDialog.get() && selectFolderDialog->ready())
    {
        logResult(selectFolderDialog->result());
        selectFolderDialog.reset();
    }


    if (lastFileSelection.size() > 0)
        ImGui::Text("%s", lastFileSelection.c_str());

    ImGui::PopID();
}


void DemoImFileDialog()
{
#ifdef IMGUI_BUNDLE_WITH_IMFILEDIALOG
    static std::string selectedFilename;

    ImGuiMd::RenderUnindented(R"(
        # ImFileDialog
         [ImFileDialog](https://github.com/pthom/ImFileDialog.git) provides file dialogs for ImGui, with images preview.
         *Not (yet) adapted for High DPI resolution under windows*
        )");

    if (ImGui::Button("Open file"))
        ifd::FileDialog::Instance().Open(
            "ShaderOpenDialog",
            "Open a shader",
            "Image file (*.png*.jpg*.jpeg*.bmp*.tga).png,.jpg,.jpeg,.bmp,.tga,.*",
            true
        );
    ImGui::SameLine();
    if (ImGui::Button("Open directory"))
        ifd::FileDialog::Instance().Open("DirectoryOpenDialog", "Open a directory", "");
    ImGui::SameLine();
    if (ImGui::Button("Save file"))
        ifd::FileDialog::Instance().Save("ShaderSaveDialog", "Save a shader", "*.sprj .sprj");

    if (selectedFilename.size() > 0)
        ImGui::Text("Last file selection:\n%s", selectedFilename.c_str());

    if (ifd::FileDialog::Instance().IsDone("ShaderOpenDialog"))
    {
        if (ifd::FileDialog::Instance().HasResult())
        {
            // get_results: plural form - ShaderOpenDialog supports multi-selection
            auto results = ifd::FileDialog::Instance().GetResults();
            selectedFilename = "";
            for (auto path: results)
                selectedFilename += path.string() + "\n";
        }
        ifd::FileDialog::Instance().Close();
    }

    if (ifd::FileDialog::Instance().IsDone("DirectoryOpenDialog"))
    {
        if (ifd::FileDialog::Instance().HasResult())
            selectedFilename = ifd::FileDialog::Instance().GetResult().string();
        ifd::FileDialog::Instance().Close();
    }

    if (ifd::FileDialog::Instance().IsDone("ShaderSaveDialog"))
    {
        if (ifd::FileDialog::Instance().HasResult())
            selectedFilename = ifd::FileDialog::Instance().GetResult().string();
        ifd::FileDialog::Instance().Close();
    }
#endif // #ifdef IMGUI_BUNDLE_WITH_IMFILEDIALOG
}


void DemoCommandPalette()
{
    static bool wasInited = false;
    static bool showCommandPalette = false;
    static ImCmd::Context * commandPaletteContext = nullptr;
    static int counter = 0;

    auto initCommandPalette = []()
    {
        commandPaletteContext = ImCmd::CreateContext();
        ImVec4 highlight_font_color(1.0f, 0.0f, 0.0f, 1.0f);
        ImCmd::SetStyleColor(ImCmdTextType_Highlight, ImGui::ColorConvertFloat4ToU32(highlight_font_color));

        // Add theme command: a two steps command, with initial callback + SubsequentCallback
        {
            ImCmd::Command select_theme_cmd;
            select_theme_cmd.Name = "Select theme";
            select_theme_cmd.InitialCallback = [&]() {
                ImCmd::Prompt(std::vector<std::string>{
                    "Classic",
                    "Dark",
                    "Light",
                });
            };
            select_theme_cmd.SubsequentCallback = [&](int selected_option) {
                switch (selected_option) {
                    case 0: ImGui::StyleColorsClassic(); break;
                    case 1: ImGui::StyleColorsDark(); break;
                    case 2: ImGui::StyleColorsLight(); break;
                    default: break;
                }
            };
            ImCmd::AddCommand(std::move(select_theme_cmd));
        }

        // Simple command that increments a counter
        {
            ImCmd::Command inc_cmd;
            inc_cmd.Name = "increment counter";
            inc_cmd.InitialCallback = [] { counter += 1; };
            ImCmd::AddCommand(inc_cmd);
        }
    };

    if (!wasInited)
    {
        initCommandPalette();
        wasInited = true;
    }

    ImGuiMd::RenderUnindented(R"(
        # Command Palette
        [imgui-command-palette](https://github.com/hnOsmium0001/imgui-command-palette.git) provides a Sublime Text or VSCode style command palette in ImGui
    )");

    auto& io = ImGui::GetIO();
    if (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_P))
        showCommandPalette = ! showCommandPalette;

    if (showCommandPalette)
        ImCmd::CommandPaletteWindow("CommandPalette", &showCommandPalette);

    ImGui::NewLine();
    ImGui::Text("Press Ctrl+Shift+P to bring up the command palette");
    ImGui::NewLine();
    ImGui::Text("counter=%i", counter);
}


void DemoCoolBar()
{
    auto ShowCoolBarButton = [](const std::string& label) -> bool
    {
        float w         = ImGui::GetCoolBarItemWidth();

        // Display transparent image and check if clicked
        HelloImGui::ImageFromAsset("images/bear_transparent.png", ImVec2(w, w));
        bool clicked = ImGui::IsItemHovered() && ImGui::IsMouseClicked(0);

        // Optional: add a label on the image
        {
            ImVec2 topLeftCorner = ImGui::GetItemRectMin();
            ImVec2 textPos(topLeftCorner.x + ImmApp::EmSize(1.f), topLeftCorner.y + ImmApp::EmSize(1.f));
            ImGui::GetForegroundDrawList()->AddText(textPos, 0xFFFFFFFF, label.c_str());
        }

        return clicked;
    };


    std::vector<std::string> buttonLabels {"A", "B", "C", "D", "E", "F"};
    ImGuiMd::RenderUnindented(R"(
        # ImCoolBar:
        ImCoolBar provides a dock-like Cool bar for Dear ImGui
    )");

    ImGui::ImCoolBarConfig coolBarConfig;
    coolBarConfig.anchor = ImVec2(0.5f, 0.07f); // position in the window (ratio of window size)
    if (ImGui::BeginCoolBar("##CoolBarMain", ImCoolBarFlags_Horizontal, coolBarConfig))
    {
        for (const std::string& label: buttonLabels)
        {
            if (ImGui::CoolBarItem())
            {
                if (ShowCoolBarButton(label))
                    printf("Clicked %s\n", label.c_str());
            }
        }
        ImGui::EndCoolBar();
    }

    ImGui::NewLine(); ImGui::NewLine();
}


void demo_widgets()
{
    DemoCoolBar();
    DemoPortableFileDialogs(); ImGui::NewLine();
    DemoImFileDialog(); ImGui::NewLine();
    DemoKnobs();
    DemoToggle(); ImGui::NewLine();
    DemoSpinner();
    DemoCommandPalette();
}
Click to see the widgets code in Python
# Part of ImGui Bundle - MIT License - Copyright (c) 2022-2023 Pascal Thomet - https://github.com/pthom/imgui_bundle
from typing import List
from imgui_bundle import (
    imgui,
    hello_imgui,
    imgui_md,
    imgui_toggle,
    ImVec2,
    immapp,
    ImVec4,
    im_cool_bar,
)
from imgui_bundle import imgui_command_palette as imcmd


@immapp.static(knob_float_value=0, knob_int_value=0)
def demo_knobs():
    static = demo_knobs
    from imgui_bundle import imgui_knobs

    imgui_md.render(
        """
# Knobs
  [imgui-knobs](https://github.com/altschuler/imgui-knobs) provides knobs for ImGui."""
    )
    knob_types = {
        "tick": imgui_knobs.ImGuiKnobVariant_.tick,
        "dot": imgui_knobs.ImGuiKnobVariant_.dot,
        "space": imgui_knobs.ImGuiKnobVariant_.space,
        "stepped": imgui_knobs.ImGuiKnobVariant_.stepped,
        "wiper": imgui_knobs.ImGuiKnobVariant_.wiper,
        "wiper_dot": imgui_knobs.ImGuiKnobVariant_.wiper_dot,
        "wiper_only": imgui_knobs.ImGuiKnobVariant_.wiper_only,
    }

    def show_float_knobs(knob_size: float):
        imgui.push_id(f"{knob_size}_float")
        for knob_typename, knob_type in knob_types.items():
            changed, static.knob_float_value = imgui_knobs.knob(
                knob_typename,
                p_value=static.knob_float_value,
                v_min=0.0,
                v_max=1.0,
                speed=0,
                format="%.2f",
                variant=knob_type,
                size=knob_size,
                flags=0,
                steps=100,
            )
            imgui.same_line()
        imgui.new_line()
        imgui.pop_id()

    def show_int_knobs(knob_size: float):
        imgui.push_id(f"{knob_size}_int")
        for knob_typename, knob_type in knob_types.items():
            changed, static.knob_int_value = imgui_knobs.knob_int(
                knob_typename,
                p_value=static.knob_int_value,
                v_min=0,
                v_max=15,
                speed=0,
                format="%02i",
                variant=knob_type,
                steps=10,
                size=knob_size,
            )
            imgui.same_line()
        imgui.new_line()
        imgui.pop_id()

    knobs_size_small = immapp.em_size() * 2.5
    knobs_size_big = knobs_size_small * 1.3

    imgui.begin_group()
    imgui.text("Some small knobs")
    show_float_knobs(knobs_size_small)
    imgui.end_group()

    imgui.same_line()

    imgui.begin_group()
    imgui.text("Some big knobs (int values)")
    show_int_knobs(knobs_size_big)
    imgui.end_group()


def demo_spinner():
    from imgui_bundle import imspinner

    imgui_md.render(
        """
# Spinners
  [imspinner](https://github.com/dalerank/imspinner) provides spinners for ImGui."""
    )

    color = imgui.ImColor(0.3, 0.5, 0.9, 1.0)
    imgui.text("spinner_moving_dots")
    imgui.same_line()
    imspinner.spinner_moving_dots("spinner_moving_dots", 3.0, color, 28.0)
    imgui.same_line()

    radius = imgui.get_font_size() / 1.8
    imgui.text("spinner_arc_rotation")
    imgui.same_line()
    imspinner.spinner_arc_rotation("spinner_arc_rotation", radius, 4.0, color)
    imgui.same_line()

    radius1 = imgui.get_font_size() / 2.5
    imgui.text("spinner_ang_triple")
    imgui.same_line()
    imspinner.spinner_ang_triple(
        "spinner_ang_triple",
        radius1,
        radius1 * 1.5,
        radius1 * 2.0,
        2.5,
        color,
        color,
        color,
    )


@immapp.static(flag=True)
def demo_toggle():
    static = demo_toggle
    imgui_md.render_unindented(
        """
        # Toggle Switch
          [imgui_toggle](https://github.com/cmdwtf/imgui_toggle) provides toggle switches for ImGui."""
    )

    _changed, static.flag = imgui_toggle.toggle("Default Toggle", static.flag)
    imgui.same_line()

    _changed, static.flag = imgui_toggle.toggle(
        "Animated Toggle", static.flag, imgui_toggle.ToggleFlags_.animated
    )
    imgui.same_line()

    toggle_config = imgui_toggle.material_style()
    toggle_config.animation_duration = 0.4
    _changed, static.flag = imgui_toggle.toggle(
        "Material Style (with slowed anim)", static.flag, config=toggle_config
    )

    imgui.same_line()
    _changed, static.flag = imgui_toggle.toggle(
        "iOS style", static.flag, config=imgui_toggle.ios_style(size_scale=0.2)
    )

    imgui.same_line()
    _changed, static.flag = imgui_toggle.toggle(
        "iOS style (light)",
        static.flag,
        config=imgui_toggle.ios_style(size_scale=0.2, light_mode=True),
    )


@immapp.static(
    open_file_dialog=None,
    open_file_multiselect=None,
    save_file_dialog=None,
    select_folder_dialog=None,
    last_file_selection="",
)
def demo_portable_file_dialogs():
    static = demo_portable_file_dialogs

    from imgui_bundle import portable_file_dialogs as pfd

    imgui.push_id("pfd")
    imgui_md.render_unindented(
        """
        # Portable File Dialogs
         [portable-file-dialogs](https://github.com/samhocevar/portable-file-dialogs) provides native file dialogs
    """
    )

    def log_result(what: str):
        static.last_file_selection = what

    def log_result_list(whats: List[str]):
        static.last_file_selection = "\n".join(whats)

    if imgui.button("Open file"):
        static.open_file_dialog = pfd.open_file("Select file")
    if static.open_file_dialog is not None and static.open_file_dialog.ready():
        log_result_list(static.open_file_dialog.result())
        static.open_file_dialog = None

    imgui.same_line()

    if imgui.button("Open file (multiselect)"):
        static.open_file_multiselect = pfd.open_file(
            "Select file", options=pfd.opt.multiselect
        )
    if (
        static.open_file_multiselect is not None
        and static.open_file_multiselect.ready()
    ):
        log_result_list(static.open_file_multiselect.result())
        static.open_file_multiselect = None

    imgui.same_line()

    if imgui.button("Save file"):
        static.save_file_dialog = pfd.save_file("Save file")
    if static.save_file_dialog is not None and static.save_file_dialog.ready():
        log_result(static.save_file_dialog.result())
        static.save_file_dialog = None

    imgui.same_line()

    if imgui.button("Select folder"):
        static.select_folder_dialog = pfd.select_folder("Select folder")
    if static.select_folder_dialog is not None and static.select_folder_dialog.ready():
        log_result(static.select_folder_dialog.result())
        static.select_folder_dialog = None

    if len(static.last_file_selection) > 0:
        imgui.text(static.last_file_selection)

    imgui.pop_id()


@immapp.static(selected_filename="")
def demo_imfile_dialog():
    static = demo_imfile_dialog  # Access to static variable via static
    from imgui_bundle import im_file_dialog as ifd

    imgui_md.render_unindented(
        """
        # ImFileDialog
         [ImFileDialog](https://github.com/pthom/ImFileDialog.git) provides file dialogs for ImGui, with images preview.
         *Not (yet) adapted for High DPI resolution under windows*
        """
    )

    if imgui.button("Open file"):
        ifd.FileDialog.instance().open(
            "ShaderOpenDialog",
            "Open a shader",
            "Image file (*.png*.jpg*.jpeg*.bmp*.tga).png,.jpg,.jpeg,.bmp,.tga,.*",
            True,
        )
    imgui.same_line()
    if imgui.button("Open directory"):
        ifd.FileDialog.instance().open("DirectoryOpenDialog", "Open a directory", "")
    imgui.same_line()
    if imgui.button("Save file"):
        ifd.FileDialog.instance().save(
            "ShaderSaveDialog", "Save a shader", "*.sprj .sprj"
        )

    if len(static.selected_filename) > 0:
        imgui.text(f"Last file selection:\n  {static.selected_filename}")

    # file dialogs
    if ifd.FileDialog.instance().is_done("ShaderOpenDialog"):
        if ifd.FileDialog.instance().has_result():
            # get_results: plural form - ShaderOpenDialog supports multi-selection
            res = ifd.FileDialog.instance().get_results()
            filenames = [f.path() for f in res]
            static.selected_filename = "\n  ".join(filenames)

        ifd.FileDialog.instance().close()

    if ifd.FileDialog.instance().is_done("DirectoryOpenDialog"):
        if ifd.FileDialog.instance().has_result():
            static.selected_filename = ifd.FileDialog.instance().get_result().path()

        ifd.FileDialog.instance().close()

    if ifd.FileDialog.instance().is_done("ShaderSaveDialog"):
        if ifd.FileDialog.instance().has_result():
            static.selected_filename = ifd.FileDialog.instance().get_result().path()

        ifd.FileDialog.instance().close()


@immapp.static(
    was_inited=False,
    show_command_palette=False,
    counter=0,
    command_palette_context=None,
)
def demo_command_palette():
    static = demo_command_palette

    def init_command_palette():
        static.command_palette_context = imcmd.ContextWrapper()
        highlight_font_color = ImVec4(1.0, 0.0, 0.0, 1.0)
        imcmd.set_style_color(
            imcmd.ImCmdTextType.highlight,
            imgui.color_convert_float4_to_u32(highlight_font_color),
        )
        # Add theme command: a two steps command, with initial callback + SubsequentCallback
        select_theme_cmd = imcmd.Command()
        select_theme_cmd.name = "Select theme"

        def select_theme_cmd_initial_cb():
            imcmd.prompt(["Classic", "Dark", "Light"])

        def select_theme_cmd_subsequent_cb(selected_option: int):
            if selected_option == 0:
                imgui.style_colors_classic()
            elif selected_option == 1:
                imgui.style_colors_dark()
            elif selected_option == 2:
                imgui.style_colors_light()

        select_theme_cmd.initial_callback = select_theme_cmd_initial_cb
        select_theme_cmd.subsequent_callback = select_theme_cmd_subsequent_cb
        imcmd.add_command(select_theme_cmd)

        # Simple command that increments a counter
        inc_cmd = imcmd.Command()
        inc_cmd.name = "increment counter"

        def inc_counter():
            static.counter += 1

        inc_cmd.initial_callback = inc_counter
        imcmd.add_command(inc_cmd)

    if not static.was_inited:
        init_command_palette()
        static.was_inited = True

    imgui_md.render_unindented(
        """
        # Command Palette
        [imgui-command-palette](https://github.com/hnOsmium0001/imgui-command-palette.git) provides a Sublime Text or VSCode style command palette in ImGui
        """
    )

    io = imgui.get_io()
    if io.key_ctrl and io.key_shift and imgui.is_key_pressed(imgui.Key.p):
        static.show_command_palette = not static.show_command_palette

    if static.show_command_palette:
        static.show_command_palette = imcmd.command_palette_window(
            "CommandPalette", True
        )

    imgui.new_line()
    imgui.text("Press Ctrl+Shift+P to bring up the command palette")
    imgui.new_line()
    imgui.text(f"{static.counter=}")


def demo_cool_bar():
    # Function to show a CoolBar button
    def show_cool_bar_button(label):
        w = im_cool_bar.get_cool_bar_item_width()

        # Display transparent image and check if clicked
        hello_imgui.image_from_asset("images/bear_transparent.png", (w, w))
        clicked = imgui.is_item_hovered() and imgui.is_mouse_clicked(0)

        # Optional: add a label on the image
        top_left_corner = imgui.get_item_rect_min()
        text_pos = (
            top_left_corner.x + immapp.em_size(1.0),
            top_left_corner.y + immapp.em_size(1.0),
        )
        imgui.get_window_draw_list().add_text(text_pos, 0xFFFFFFFF, label)

        return clicked

    button_labels = ["A", "B", "C", "D", "E", "F"]
    imgui_md.render_unindented(
        """
        # ImCoolBar:
        ImCoolBar provides a dock-like Cool bar for Dear ImGui
        """
    )

    cool_bar_config = im_cool_bar.ImCoolBarConfig()
    cool_bar_config.anchor = ImVec2(
        0.5, 0.07
    )  #  position in the window (ratio of window size)
    if im_cool_bar.begin_cool_bar(
        "##CoolBarMain", im_cool_bar.ImCoolBarFlags_.horizontal, cool_bar_config
    ):
        for label in button_labels:
            if im_cool_bar.cool_bar_item():
                if show_cool_bar_button(label):
                    print(f"Clicked {label}")
        im_cool_bar.end_cool_bar()

    imgui.new_line()
    imgui.new_line()


def demo_gui():
    demo_cool_bar()
    demo_portable_file_dialogs()
    imgui.new_line()
    demo_imfile_dialog()
    imgui.new_line()
    demo_knobs()
    demo_toggle()
    imgui.new_line()
    demo_spinner()
    demo_command_palette()


if __name__ == "__main__":
    from imgui_bundle.demos_python import demo_utils
    demo_utils.set_hello_imgui_demo_assets_folder()

    from imgui_bundle import immapp
    immapp.run(demo_gui, with_markdown=True, window_size=(1000, 1000))  # type: ignore

Logger

demo widgets logger
Figure 15. Logger
Click to see the logger code in C++
// Part of ImGui Bundle - MIT License - Copyright (c) 2022-2024 Pascal Thomet - https://github.com/pthom/imgui_bundle
#include "imgui_md_wrapper/imgui_md_wrapper.h"
#include "immapp/immapp.h"
#include "hello_imgui/hello_imgui.h"
#include "demo_utils/api_demos.h"

#include <vector>
#include <string>


void demo_logger()
{
    static std::vector<std::string> fortunes {
        "If at first you don't succeed, skydiving is not for you.",
        "You will be a winner today. Pick a fight.",
        "The world may be your oyster, but it doesn't mean you'll get its pearl.",
        "Borrow money from a pessimist, they don't expect it back.",
        "You will be hungry again in an hour.",
        "A closed mouth gathers no foot.",
        "Today, you will invent the wheel...again.",
        "If you can't convince them, confuse them.",
        "The journey of a thousand miles begins with a single step, or a really good map.",
        "You will find a pot of gold at the end of a rainbow, but it'll be someone else's.",
        "Opportunities will knock on your door, but don't worry, they'll be gone by the time you get up to answer.",
        "You will have a long and healthy life...and a very boring one.",
        "A wise man once said nothing.",
        "You will have a great day...tomorrow.",
        "The only thing constant in life is change, except for death and taxes, those are pretty constant too."
    };

    static size_t idxFortune = 0;

    auto addLogs = []()
    {
        for (int i = 0; i < 10; ++i)
        {
            HelloImGui::LogLevel logLevel = HelloImGui::LogLevel(rand() % 4);
            HelloImGui::Log(logLevel, fortunes[idxFortune].c_str());
            ++ idxFortune;
            if (idxFortune >= fortunes.size())
                idxFortune = 0;
        }
    };
    static bool addedLogs = false;
    if (! addedLogs)
    {
        addLogs();
        addedLogs = true;
    }

    ImGuiMd::RenderUnindented(R"(
        # Graphical logger for ImGui
        This logger is adapted from [ImGuiAl](https://github.com/leiradel/ImGuiAl)

        Its colors are computed automatically from the WindowBg color, in order to remain readable when the theme is changed.
    )");
    ImGui::Separator();

    if (ImGui::Button("Add logs"))
        addLogs();

    ImGui::Separator();
    HelloImGui::LogGui();
}
Click to see the logger code in Python
# Part of ImGui Bundle - MIT License - Copyright (c) 2022-2023 Pascal Thomet - https://github.com/pthom/imgui_bundle
import random
from imgui_bundle import imgui, hello_imgui, imgui_md, immapp
from imgui_bundle.demos_python.demo_utils import api_demos


@immapp.static(idx_fortune=0, added_logs=False)
def demo_gui():
    static = demo_gui
    fortunes = [
        "If at first you don't succeed, skydiving is not for you.",
        "You will be a winner today. Pick a fight.",
        "The world may be your oyster, but it doesn't mean you'll get its pearl.",
        "Borrow money from a pessimist, they don't expect it back.",
        "You will be hungry again in an hour.",
        "A closed mouth gathers no foot.",
        "Today, you will invent the wheel...again.",
        "If you can't convince them, confuse them.",
        "The journey of a thousand miles begins with a single step, or a really good map.",
        "You will find a pot of gold at the end of a rainbow, but it'll be someone else's.",
        "Opportunities will knock on your door, but don't worry, they'll be gone by the time you get up to answer.",
        "You will have a long and healthy life...and a very boring one.",
        "A wise man once said nothing.",
        "You will have a great day...tomorrow.",
        "The only thing constant in life is change, except for death and taxes, those are pretty constant too.",
    ]

    def add_logs():
        for _i in range(10):
            log_level = random.choice(
                [
                    hello_imgui.LogLevel.debug,
                    hello_imgui.LogLevel.info,
                    hello_imgui.LogLevel.warning,
                    hello_imgui.LogLevel.error,
                ]
            )
            hello_imgui.log(log_level, fortunes[static.idx_fortune])
            static.idx_fortune += 1
            if static.idx_fortune >= len(fortunes):
                static.idx_fortune = 0

    if not static.added_logs:
        add_logs()
        static.added_logs = True

    imgui_md.render_unindented(
        """
        # Graphical logger for ImGui
        This logger is adapted from [ImGuiAl](https://github.com/leiradel/ImGuiAl)

        Its colors are computed automatically from the WindowBg color, in order to remain readable when the theme is changed.
        """
    )
    imgui.separator()

    if imgui.button("Add logs"):
        for _i in range(10):
            add_logs()

    imgui.separator()
    hello_imgui.log_gui()


def main():
    api_demos.set_hello_imgui_demo_assets_folder()
    immapp.run(demo_gui, "Log", with_markdown=True)


if __name__ == "__main__":
    main()

Code Editor

demo widgets editor
Figure 16. Code editor
Click to see the code editor code in C++
// Part of ImGui Bundle - MIT License - Copyright (c) 2022-2024 Pascal Thomet - https://github.com/pthom/imgui_bundle
#include "imgui.h"
#include "immapp/immapp.h"
#include "ImGuiColorTextEdit/TextEditor.h"
#include <fplus/fplus.hpp>

TextEditor _PrepareTextEditor()
{
    TextEditor editor;
    std::string filename = __FILE__;
#ifndef __EMSCRIPTEN__
    std::string this_file_code = fplus::read_text_file(filename)();
#else
    std::string this_file_code = fplus::read_text_file("/demos_cpp/demo_text_edit.cpp")();
#endif
    editor.SetText(this_file_code);
    editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
    return editor;
}


void demo_text_edit()
{
    static TextEditor editor = _PrepareTextEditor();

    ImGuiMd::Render(R"(
# ImGuiColorTextEdit:
[ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit)  is a colorizing text editor for ImGui, able to colorize C, C++, hlsl, Sql, angel_script and lua code
    )");

    auto ShowPaletteButtons = []()
    {
        if (ImGui::SmallButton("Dark palette"))
            editor.SetPalette(TextEditor::GetDarkPalette());
        ImGui::SameLine();
        if (ImGui::SmallButton("Light palette"))
            editor.SetPalette(TextEditor::GetLightPalette());
        ImGui::SameLine();
        if (ImGui::SmallButton("Retro blue palette"))
            editor.SetPalette(TextEditor::GetRetroBluePalette());
        ImGui::SameLine();
        if (ImGui::SmallButton("Mariana palette"))
            editor.SetPalette(TextEditor::GetMarianaPalette());
    };

    ShowPaletteButtons();
    ImGui::PushFont(ImGuiMd::GetCodeFont());
    editor.Render("Code");
    ImGui::PopFont();
}
Click to see the code editor code in Python
# Part of ImGui Bundle - MIT License - Copyright (c) 2022-2023 Pascal Thomet - https://github.com/pthom/imgui_bundle
from imgui_bundle import imgui, imgui_color_text_edit as ed, imgui_md
from imgui_bundle.immapp import static

TextEditor = ed.TextEditor


def _prepare_text_editor():
    with open(__file__, encoding="utf8") as f:
        this_file_code = f.read()
    editor = TextEditor()
    editor.set_text(this_file_code)
    editor.set_language_definition(TextEditor.LanguageDefinition.python())
    return editor


@static(editor=_prepare_text_editor())
def demo_gui():
    static = demo_gui
    editor = static.editor

    imgui_md.render(
        """
# ImGuiColorTextEdit:
[ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit)  is a colorizing text editor for ImGui, able to colorize C, C++, hlsl, Sql, angel_script and lua code
    """
    )

    def show_palette_buttons():
        if imgui.small_button("Dark palette"):
            editor.set_palette(ed.TextEditor.get_dark_palette())
        imgui.same_line()
        if imgui.small_button("Light palette"):
            editor.set_palette(TextEditor.get_light_palette())
        imgui.same_line()
        if imgui.small_button("Retro blue palette"):
            editor.set_palette(TextEditor.get_retro_blue_palette())
        imgui.same_line()
        if imgui.small_button("Mariana palette"):
            editor.set_palette(TextEditor.get_mariana_palette())

    show_palette_buttons()
    imgui.push_font(imgui_md.get_code_font())
    editor.render("Code")
    imgui.pop_font()


def main():
    from imgui_bundle import immapp

    immapp.run(demo_gui, with_markdown=True)


if __name__ == "__main__":
    main()

And many more!

Tip
Tip: use the interactive manual as an inspiration

demo immapp apps

The interactive manual provides many demos, with easy access to their code. It includes all the examples that are explained here, and many more.

demo node editor
Figure 17. ImGui Node editor in action
demo gizmo
Figure 18. ImGuizmo in action

Usage instructions

Dear ImGui - Immediate GUI

Dear ImGui is an implementation of the Immediate Gui paradigm.

Dear ImGui demo (and manual)

Dear ImGui comes with a complete demo. It demonstrates all the widgets, together with an example code on how to use them.

Tip
To run this demo in your browser, launch ImGui Manual.

For each widget, you will see the corresponding demo code (in C++. Read the part "C++ / Python porting advices" to see how easy it is to translate Gui code from C++ to python.

Dear ImGui C++ API

Dear ImGui’s C++ API is thoroughly documented in its header files:

Dear ImGui Python API

The python API closely mirrors the C++ API, and its documentation is extremely easy to access from your IDE, via thoroughly documented stub (*.pyi) files.

Example

An example is often worth a thousand words, the following code:

C++

// Display a text
ImGui::Text("Counter = %i", app_state.counter);
ImGui::SameLine(); // by default ImGui starts a new line at each widget

// The following line displays a button
if (ImGui::Button("increment counter"))
    // And returns true if it was clicked: you can *immediately* handle the click
    app_state.counter += 1;

// Input a text: in C++, InputText returns a bool and modifies the text directly
bool changed = ImGui::InputText("Your name?", &app_state.name);
ImGui::Text("Hello %s!", app_state.name.c_str());

Python

# Display a text
imgui.text(f"Counter = {app_state.counter}")
imgui.same_line()  # by default ImGui starts a new line at each widget

# The following line displays a button
if imgui.button("increment counter"):
    # And returns true if it was clicked: you can *immediately* handle the click
    app_state.counter += 1

# Input a text: in python, input_text returns a tuple(modified, new_value)
changed, app_state.name = imgui.input_text("Your name?", app_state.name)
imgui.text(f"Hello {app_state.name}!")

Displays this:

immediate gui example

Hello ImGui - Starter pack

Dear ImGui Bundle includes Hello ImGui, which is itself based on ImGui. "Hello ImGui" can be compared to a starter pack that enables to easily write cross-platform Gui apps for Windows, macOS, Linux, iOS, and emscripten.

API & Usage

See the "Hello ImGui" API doc and Application parameter doc.

Features

Multiplatform utilities

  • Truly multiplatform: Linux, Windows, macOS, iOS, Android, emscripten (with 4 lines of CMake code)

  • Easily embed assets on all platforms (no code required)

  • Customize app settings (icon and app name for mobile platforms, etc.- no code required)

  • Customize application icon on all platforms (including mobile and macOS - no code required)

Dear ImGui Tweaks

  • Power Save mode: reduce FPS when idling

  • High DPI support: scale UI according to DPI, whatever the platform

  • Advanced layout handling: dockable windows, multiple layouts

  • Window geometry utilities: autosize application window, restore app window position

  • Theme tweaking: extensive list of additional themes

  • Support for movable and resizable borderless windows

  • Advanced font support: icons, emojis and colored fonts

  • Integration with ImGui Test Engine: automate and test your apps

  • Save user settings: window position, layout, opened windows, theme, user defined custom settings

  • Easily add a custom 3D background to your app

Backends

  • Available platform backends: SDL2, Glfw3

  • Available rendering backends: OpenGL3, Metal, Vulkan, DirectX

Note
The usage of Hello ImGui is optional. You can also build an imgui application from scratch, in C++ or in python (see python example)
Tip
HelloImGui is fully configurable by POD (plain old data) structures. See their description

Advanced layout and theming with Hello ImGui:

See the demo named "demo_docking", which demonstrates:

  • How to handle complex layouts: you can define several layouts and switch between them: each layout which will remember the user modifications and the list of opened windows

  • How to use theming

  • How to store you own user settings in the app ini file

  • How to add a status bar and a log window

  • How to reduce the FPS when idling (to reduce CPU usage)

Links:

ImmApp - Immediate App

ImGui Bundle includes a library named ImmApp (which stands for Immediate App). ImmApp is a thin extension of HelloImGui that enables to easily initialize the ImGuiBundle addons that require additional setup at startup

How to start an application with addons

Click to see an example application with addons

Some libraries included by ImGui Bundle require an initialization at startup. ImmApp makes this easy via AddOnParams.

The example program below demonstrates how to run an application which will use implot (which requires a context to be created at startup), and imgui_md (which requires additional fonts to be loaded at startup).

C++

#ifdef IMGUI_BUNDLE_WITH_IMPLOT
#include "immapp/immapp.h"
#include "imgui_md_wrapper/imgui_md_wrapper.h"
#include "implot/implot.h"
#include "demo_utils/api_demos.h"
#include <vector>
#include <cmath>


int main(int, char**)
{
    // This call is specific to the ImGui Bundle interactive manual. In a standard application, you could write:
    //         HelloImGui::SetAssetsFolder("my_assets"); // (By default, HelloImGui will search inside "assets")
    ChdirBesideAssetsFolder();

    constexpr double pi = 3.1415926535897932384626433;
    std::vector<double> x, y1, y2;
    for (double _x = 0; _x < 4 * pi; _x += 0.01)
    {
        x.push_back(_x);
        y1.push_back(std::cos(_x));
        y2.push_back(std::sin(_x));
    }

    auto gui = [x,y1,y2]()
    {
        ImGuiMd::Render("# This is the plot of _cosinus_ and *sinus*");  // Markdown
        if (ImPlot::BeginPlot("Plot"))
        {
            ImPlot::PlotLine("y1", x.data(), y1.data(), x.size());
            ImPlot::PlotLine("y2", x.data(), y2.data(), x.size());
            ImPlot::EndPlot();
        }
    };

    HelloImGui::SimpleRunnerParams runnerParams { .guiFunction = gui, .windowSize = {600, 400} };
    ImmApp::AddOnsParams addons { .withImplot = true, .withMarkdown = true };
    ImmApp::Run(runnerParams, addons);

    return 0;
}
#else // #ifdef IMGUI_BUNDLE_WITH_IMPLOT
#include <cstdio>
int main(int, char**) { std::printf("This demo requires ImPlot.\n"); }
#endif

Python:

import numpy as np
from imgui_bundle import implot, imgui_md, immapp
from imgui_bundle.demos_python import demo_utils


def main():
    # This call is specific to the ImGui Bundle interactive manual. In a standard application, you could write:
    #         hello_imgui.set_assets_folder("my_assets"); # (By default, HelloImGui will search inside "assets")
    demo_utils.set_hello_imgui_demo_assets_folder()

    x = np.arange(0, np.pi * 4, 0.01)
    y1 = np.cos(x)
    y2 = np.sin(x)

    def gui():
        imgui_md.render("# This is the plot of _cosinus_ and *sinus*")  # Markdown
        if implot.begin_plot("Plot"):
            implot.plot_line("y1", x, y1)
            implot.plot_line("y2", x, y2)
            implot.end_plot()

    immapp.run(gui, with_implot=True, with_markdown=True, window_size=(600, 400))


if __name__ == "__main__":
    main()

Application Settings

Settings location

By default, the settings are stored in a ini file whose named is derived from the window title (i.e. runnerParams.appWindowParams.windowTitle). This is convenient when developing, but not so much when deploying the app.

You can finely define where they are stored by filling runnerParams.iniFolderType and runnerParams.iniFilename.

runnerParams.iniFolderType

Choose between: CurrentFolder,AppUserConfigFolder, AppExecutableFolder,HomeFolder, TempFolder and DocumentsFolder.

Note
Note: AppUserConfigFolder corresponds to …​\[Username]\AppData\Roaming under Windows, ~/.config under Linux, ~/Library/Application Support under macOS or iOS

runnerParams.iniFilename

This will be the name of the ini file in which the settings will be stored. It can include a subfolder, in which case it will be created under the folder defined by runnerParams.iniFolderType.

Note: if left empty, the name of the ini file will be derived from appWindowParams.windowTitle.

Examples

Click to expand the examples

C++ example (extract from demo_docking.cpp)

    // By default, HelloImGui will save the settings in the current folder.
    // This is convenient when developing, but not so much when deploying the app.
    // You can tell HelloImGui to save the settings in a specific folder: choose between
    //         CurrentFolder
    //         AppUserConfigFolder
    //         AppExecutableFolder
    //         HomeFolder
    //         TempFolder
    //         DocumentsFolder
    //
    // Note: AppUserConfigFolder is:
    //         AppData under Windows (Example: C:\Users\[Username]\AppData\Roaming)
    //         ~/.config under Linux
    //         "~/Library/Application Support" under macOS or iOS
    runnerParams.iniFolderType = HelloImGui::IniFolderType::AppUserConfigFolder;

    // runnerParams.iniFilename: this will be the name of the ini file in which the settings
    // will be stored.
    // In this example, the subdirectory Docking_Demo will be created under the folder defined
    // by runnerParams.iniFolderType.
    //
    // Note: if iniFilename is left empty, the name of the ini file will be derived
    // from appWindowParams.windowTitle
    runnerParams.iniFilename = "Docking_Demo/Docking_demo.ini";

Python example (extract from demo_docking.py)

    # By default, HelloImGui will save the settings in the current folder.
    # This is convenient when developing, but not so much when deploying the app.
    # You can tell HelloImGui to save the settings in a specific folder: choose between
    #         current_folder
    #         app_user_config_folder
    #         app_executable_folder
    #         home_folder
    #         temp_folder
    #         documents_folder
    #
    # Note: app_user_config_folder is:
    #         AppData under Windows (Example: C:\Users\[Username]\AppData\Roaming)
    #         ~/.config under Linux
    #         "~/Library/Application Support" under macOS or iOS
    runner_params.ini_folder_type = hello_imgui.IniFolderType.app_user_config_folder

    # runnerParams.ini_filename: this will be the name of the ini file in which the settings
    # will be stored.
    # In this example, the subdirectory Docking_Demo will be created under the folder defined
    # by runnerParams.ini_folder_type.
    #
    # Note: if ini_filename is left empty, the name of the ini file will be derived
    # from app_window_params.window_title
    runner_params.ini_filename = "Docking_Demo/Docking_demo.ini"

Settings content

The settings file contains, standard ImGui settings (window position, size, etc.), as well as additional settings defined by HelloImGui:

  • Application status: app window location, opened windows, status bar settings, etc. See members named remember_xxx in the Hello Imgui API for a complete list.

  • Settings for each application layout (see video for an example)

Store custom settings

You may store additional user settings in the application settings. This is provided as a convenience only, and it is not intended to store large quantities of text data. See related doc for more details.

Python: alternative backends

HelloImGui and ImmApp use glfw as a default backend.

If you wish to use a different backend, it is possible to use sdl2 or pyglet, via pure python backends.

The python backends folder contains a set of python backends, that can be used as a replacement for the default glfw backend. This way you will have complete control on your application (they are inspired from pyimgui backends).

Note
In this case, you will not benefit from HelloImGui and ImmApp rapid development features (HighDPI support, layout management, automatic idling, etc…​).

Documentation

See documentation in the python backends folder.

Examples

Example with a pure python sdl2 backend (click to expand)
# An example of using Dear ImGui with SDL using a *full python* backend.
# This mode is inspired from [pyimgui](https://github.com/pyimgui/pyimgui) backends, and is still experimental.
#
# See full python backends implementations here:
# https://github.com/pthom/imgui_bundle/tree/main/bindings/imgui_bundle/python_backends

# You will need to install sdl2:
#    pip install pysdl2 pysdl2-dll

from imgui_bundle import imgui
from imgui_bundle.python_backends.sdl_backend import SDL2Renderer
import OpenGL.GL as gl  # type: ignore
from sdl2 import *  # type: ignore
import ctypes
import sys


class AppState:
    text: str = """Hello, World\nLorem ipsum, etc.\netc."""
    text2: str = "Ahh"


app_state = AppState()


def main():
    window, gl_context = impl_pysdl2_init()
    imgui.create_context()
    impl = SDL2Renderer(window)

    show_custom_window = True

    running = True
    event = SDL_Event()
    while running:
        while SDL_PollEvent(ctypes.byref(event)) != 0:
            if event.type == SDL_QUIT:
                running = False
                break
            impl.process_event(event)
        impl.process_inputs()

        imgui.new_frame()

        if imgui.begin_main_menu_bar():
            if imgui.begin_menu("File", True):

                clicked_quit, selected_quit = imgui.menu_item(
                    "Quit", "Cmd+Q", False, True
                )

                if clicked_quit:
                    sys.exit(0)

                imgui.end_menu()
            imgui.end_main_menu_bar()

        if show_custom_window:
            imgui.set_next_window_size((400, 400))
            is_expand, show_custom_window = imgui.begin("Custom window", True)
            if is_expand:
                imgui.text("Example Text")
                if imgui.button("Hello"):
                    print("World")
                _, app_state.text = imgui.input_text_multiline(
                    "Edit", app_state.text, imgui.ImVec2(200, 200)
                )
                _, app_state.text2 = imgui.input_text("Text2", app_state.text2)

                io = imgui.get_io()
                imgui.text(f"""
                Keyboard modifiers:
                    {io.key_ctrl=}
                    {io.key_alt=}
                    {io.key_shift=}
                    {io.key_super=}""")
            imgui.end()

        gl.glClearColor(1.0, 1.0, 1.0, 1)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)

        imgui.render()
        impl.render(imgui.get_draw_data())
        SDL_GL_SwapWindow(window)

    impl.shutdown()
    SDL_GL_DeleteContext(gl_context)
    SDL_DestroyWindow(window)
    SDL_Quit()


def impl_pysdl2_init():
    width, height = 1280, 720
    window_name = "minimal ImGui/SDL2 example"

    if SDL_Init(SDL_INIT_EVERYTHING) < 0:
        print(
            "Error: SDL could not initialize! SDL Error: "
            + SDL_GetError().decode("utf-8")
        )
        sys.exit(1)

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24)
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8)
    SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1)
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1)
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE)

    SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, b"1")
    SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, b"1")

    window = SDL_CreateWindow(
        window_name.encode("utf-8"),
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        width,
        height,
        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE,
    )

    if window is None:
        print(
            "Error: Window could not be created! SDL Error: "
            + SDL_GetError().decode("utf-8")
        )
        sys.exit(1)

    gl_context = SDL_GL_CreateContext(window)
    if gl_context is None:
        print(
            "Error: Cannot create OpenGL Context! SDL Error: "
            + SDL_GetError().decode("utf-8")
        )
        sys.exit(1)

    SDL_GL_MakeCurrent(window, gl_context)
    if SDL_GL_SetSwapInterval(1) < 0:
        print(
            "Warning: Unable to set VSync! SDL Error: " + SDL_GetError().decode("utf-8")
        )
        sys.exit(1)

    return window, gl_context


if __name__ == "__main__":
    main()
Example with a pure python glfw backend (click to expand)
# An example of using Dear ImGui with Glfw using a *full python* backend.
# This mode is inspired from [pyimgui](https://github.com/pyimgui/pyimgui) backends, and is still experimental.
#
# See full python backends implementations here:
# https://github.com/pthom/imgui_bundle/tree/main/bindings/imgui_bundle/python_backends

from imgui_bundle.python_backends.glfw_backend import GlfwRenderer
import OpenGL.GL as gl  # type: ignore
from imgui_bundle import imgui, imgui_ctx
import glfw  # type: ignore
import sys


class AppState:
    text: str = """Hello, World\nLorem ipsum, etc.\netc."""


app_state = AppState()


def main():
    imgui.create_context()
    window = impl_glfw_init()
    impl = GlfwRenderer(window)

    show_custom_window = True

    while not glfw.window_should_close(window):
        glfw.poll_events()
        impl.process_inputs()

        imgui.new_frame()

        if imgui.begin_main_menu_bar():
            if imgui.begin_menu("File", True):

                clicked_quit, selected_quit = imgui.menu_item(
                    "Quit", "Cmd+Q", False, True
                )

                if clicked_quit:
                    sys.exit(0)

                imgui.end_menu()
            imgui.end_main_menu_bar()

        if show_custom_window:
            imgui.set_next_window_size((400, 400))
            is_expand, show_custom_window = imgui.begin("Custom window", True)
            if is_expand:
                imgui.text("Example Text")
                if imgui.button("Hello"):
                    print("World")
                _, app_state.text = imgui.input_text_multiline(
                    "Edit", app_state.text, imgui.ImVec2(200, 200)
                )
                io = imgui.get_io()
                imgui.text(f"""
                Keyboard modifiers:
                    {io.key_ctrl=}
                    {io.key_alt=}
                    {io.key_shift=}
                    {io.key_super=}""")

                if imgui.button("Open popup"):
                    imgui.open_popup("my popup")
                with imgui_ctx.begin_popup_modal("my popup") as popup:
                    if popup.visible:
                        imgui.text("Hello from popup!")
                        if imgui.button("Close popup"):
                            imgui.close_current_popup()

            imgui.end()

        gl.glClearColor(1.0, 1.0, 1.0, 1)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)

        imgui.render()
        impl.render(imgui.get_draw_data())
        glfw.swap_buffers(window)

    impl.shutdown()
    glfw.terminate()


def impl_glfw_init():
    width, height = 1280, 720
    window_name = "minimal ImGui/GLFW3 example"

    if not glfw.init():
        print("Could not initialize OpenGL context")
        sys.exit(1)

    # OS X supports only forward-compatible core profiles from 3.2
    glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
    glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
    glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)

    glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, gl.GL_TRUE)

    # Create a windowed mode window and its OpenGL context
    window = glfw.create_window(int(width), int(height), window_name, None, None)
    glfw.make_context_current(window)

    if not window:
        glfw.terminate()
        print("Could not initialize Window")
        sys.exit(1)

    return window


if __name__ == "__main__":
    main()

Usage withing jupyter notebook

ImmApp adds support for integration inside jupyter notebook: the application will be run in an external window, and a screenshot will be placed on the notebook after execution.

This requires a window server, and will not run on Google collab.

Below is a screenshot, that you can test by running jupyter notebook inside bindings/imgui_bundle/demos_python/notebooks

immapp notebook example
Figure 19. Using ImGui Bundle inside Jupyter Notebook

Repository folders structure

Click to see a detailed explanation of this repository folder structure.
./
+-- Readme.md -> bindings/imgui_bundle/Readme.md           # doc
+-- Readme_devel.md
|
+-- _example_integration/                         # Demonstrate how to easily use
|         +-- CMakeLists.txt                      # imgui_bundle in a C++ app
|         +-- assets/                             # (this is a github template available a
|         +-- hello_world.main.cpp                # https://github.com/pthom/imgui_bundle_template
|
+-- imgui_bundle_cmake/                           # imgui_bundle_add_app() :
|         |                                       # a cmake function you can use
|         +-- imgui_bundle_add_app.cmake          # to create an app in one line
|
+-- bindings/                                     # root for the python bindings
|         +-- imgui_bundle/
|                  +-- assets/                    # assets/ folder: you need to
|                  |                              # copy this folder
|                  |                              # into your app folder if you
|                  |                              # intend to use markdown
|                  |
|                  +-- demos_assets/              # assets used by demos
|                  +-- demos_cpp/                 # lots of C++ demos
|                  +-- demos_python/              # lots of python demos
|                  +-- imgui/                     # imgui stubs
|                  |     +-- __init__.pyi
|                  |     +-- backends.pyi
|                  |     +-- internal.pyi
|                  |     +-- py.typed
|                  +-- implot.pyi                           # implot stubs
|                  +-- __init__.py
|                  +-- __init__.pyi
|                  +-- hello_imgui.pyi
|                  +-- ...                                  # lots of other libs stubs
|                  +-- ...
|                  +-- ...
|                  +-- immapp/                              # immapp: immediate app
|                  |        |                               # utilities
|                  |        +-- __init__.py
|                  |        +-- __init__.pyi
|                  |        +-- icons_fontawesome.py
|                  |        +-- immapp_cpp.pyi
|                  |        +-- immapp_utils.py
|                  |        +-- py.typed
|                  +-- _imgui_bundle.cpython-38-darwin.so  # imGui_bundle python
|                  |                                       # dynamic library
|                  +-- glfw_utils.py
|                  +-- python_backends/                   # Backends implemented in pure python
|                  +-- py.typed
|
|
+-- cmake/                                                 # Private cmake utilities
|         +-- add_imgui.cmake
|         +-- ...
|
+-- external/                                              # Root of all bound libraries
|         +-- CMakeLists.txt
|         +-- imgui/                                       # ImGui root
|         |         +-- bindings/                          # ImGui bindings
|         |         +-- imgui/                             # ImGui submodule
|         +-- ImGuizmo/
|         |         +-- bindings/                          # ImGuizmo bindings
|         |         +-- ImGuizmo/                          # ImGuizmo submodule
|         |         +-- ImGuizmoPure/                      # Manual wrappers to help
|         |                                                # bindings generation
|         |
|         +-- ... lots of other bound libraries/           # Lots of other bound libraries
|         |         +-- {lib_name}/
|         |         +-- bindings/
|         |
|         +-- _doc/
|         |
|         +-- bindings_generation/                         # Script to generate bindings
|         |         |                                      # and to facilitate external
|         |         +-- __init__.py                        # libraries update
|         |         +-- all_external_libraries.py
|         |         +-- autogenerate_all.py
|         |         +-- ...
|         |
|         +-- SDL/SDL/                                     # Linked library (without
|         |                                                # python bindings)
|         +-- fplus/fplus/                                 # Library without bindings
|         +-- glfw/glfw                                    # Library without bindings
|
+-- lg_cmake_utils/                                        # Cmake utils for bindings
|         |                                                # generation
|         +-- lg_cmake_utils.cmake
|         +-- ...
|
+-- pybind_native_debug/
|         +-- CMakeLists.txt
|         +-- Readme.md
|         +-- pybind_native_debug.cpp
|         +-- pybind_native_debug.py
|
+-- src/
|         +-- imgui_bundle/                               # main cpp library: almost empty,
                                                          # but linked to all external libraries

C++ / Python porting advices

General advices

ImGui is a C++ library that was ported to Python. In order to work with it, you will often refer to its manual, which shows example code in C++.

In order to translate from C++ to Python:

  1. Change the function names and parameters' names from CamelCase to snake_case

  2. Change the way the output are handled.

    1. in C++ ImGui::RadioButton modifies its second parameter (which is passed by address) and returns true if the user clicked the radio button.

    2. In python, the (possibly modified) value is transmitted via the return: imgui.radio_button returns a Tuple[bool, str] which contains (user_clicked, new_value).

  3. if porting some code that uses static variables, use the @immapp.static decorator. In this case, this decorator simply adds a variable value at the function scope. It is preserved between calls. Normally, this variable should be accessed via demo_radio_button.value, however the first line of the function adds a synonym named static for more clarity. Do not overuse them! Static variable suffer from almost the same shortcomings as global variables, so you should prefer to modify an application state.

Example:

C++

void DemoRadioButton()
{
    static int value = 0;
    ImGui::RadioButton("radio a", &value, 0); ImGui::SameLine();
    ImGui::RadioButton("radio b", &value, 1); ImGui::SameLine();
    ImGui::RadioButton("radio c", &value, 2);
}

Python

@immapp.static(value=0)
def demo_radio_button():
    static = demo_radio_button
    clicked, static.value = imgui.radio_button("radio a", static.value, 0)
    imgui.same_line()
    clicked, static.value = imgui.radio_button("radio b", static.value, 1)
    imgui.same_line()
    clicked, static.value = imgui.radio_button("radio c", static.value, 2)

Enums and TextInput

In the example below, two differences are important:

InputText functions:

imgui.input_text (Python) is equivalent to ImGui::InputText (C++)

  • In C++, it uses two parameters for the text: the text pointer, and its length.

  • In Python, you can simply pass a string, and get back its modified value in the returned tuple.

Enums handling:

  • ImGuiInputTextFlags_ (C++) corresponds to imgui.InputTextFlags_ (python) and it is an enum (note the trailing underscore).

  • ImGuiInputTextFlags (C++) corresponds to imgui.InputTextFlags (python) and it is an int (note: no trailing underscore)

You will find many similar enums.

The dichotomy between int and enums, enables you to write flags that are a combinations of values from the enum (see example below).

Example

C++

void DemoInputTextUpperCase()
{
    static char text[64] = "";
    ImGuiInputTextFlags flags = (
        ImGuiInputTextFlags_CharsUppercase
        | ImGuiInputTextFlags_CharsNoBlank
    );
    /*bool changed = */ ImGui::InputText("Upper case, no spaces", text, 64, flags);
}

Python

@immapp.static(text="")
def demo_input_text_decimal() -> None:
    static = demo_input_text_decimal
    flags:imgui.InputTextFlags = (
            imgui.InputTextFlags_.chars_uppercase.value
          | imgui.InputTextFlags_.chars_no_blank.value
        )
    changed, static.text = imgui.input_text("Upper case, no spaces", static.text, flags)

Note: in C++, by using imgui_stdlib.h, it is also possible to write:

#include "imgui/misc/cpp/imgui_stdlib.h"

void DemoInputTextUpperCase_StdString()
{
    static std::string text;
    ImGuiInputTextFlags flags = (
        ImGuiInputTextFlags_CharsUppercase
        | ImGuiInputTextFlags_CharsNoBlank
    );
    /*bool changed = */ ImGui::InputText("Upper case, no spaces", &text, flags);
}

Python context managers:

In C++, you would write:

ImGui::Begin("My Window")
ImGui::Text("Hello World");
ImGui::End(); // ImGui::End() should be called even if ImGui::Begin() returns false

In Python, the module imgui_ctx provides a lot of context managers that automatically call imgui.end(), imgui.end_child(), etc., when the context is exited, so that you can write:

from imgui_bundle import imgui, imgui_ctx

with imgui_ctx.begin("My Window"): # imgui.end() called automatically
    imgui.text("Hello World")

Of course, you can choose to use the standard API by using the module imgui:

imgui.begin("My Window")
imgui.text("Hello World")
imgui.end()

Advanced glfw callbacks

When using the glfw backend, you can set advanced callbacks on all glfw events.

Below is an example that triggers a callback whenever the window size is changed:

import imgui_bundle
import glfw   # always import glfw *after* imgui_bundle!!!


# define a callback
def my_window_size_callback(window: glfw._GLFWwindow, w: int, h: int):
    print(f"Window size changed to {w}x{h}")


# Get the glfw window used by hello imgui
window = imgui_bundle.glfw_utils.glfw_window_hello_imgui()
glfw.set_window_size_callback(window, my_window_size_callback)
Caution
It is important to import glfw after imgui_bundle, since - upon import - imgui_bundle informs glfw that it shall use its own version of the glfw dynamic library.

Debug native C++ in python scripts

ImGui Bundle provides tooling to help you debug the C++ side, when you encounter a bug that is difficult to diagnose from Python.

It can be used in two steps:

  1. Edit the file pybind_native_debug/pybind_native_debug.py. Change its content so that it runs the python code you would like to debug. Make sure it works when you run it as a python script.

  2. Now, debug the C++ project pybind_native_debug which is defined in the directory pybind_native_debug/. This will run your python code from C++, and you can debug the C++ side (place breakpoints, watch variables, etc).

Example: this issue on macOS was solved thanks to this.

Python specific utilities

Display Matplotlib plots in ImGui

imgui_fig.py is a small utility to display Matplotlib plots in ImGui.

See demo_matplotlib.py for an example.

Pure python backends

python_backends contains pure python backends for glfw, pyglet and sdl2. They do not offer the same DPI handling as HelloImGui, but they are a good starting point if you want to use alternative backends.

See examples for more information.

Closing words

Who is this project for

As mentioned in the intro,

Dear ImGui Bundle is a comprehensive bundle for Dear ImGui, featuring various powerful libraries from its ecosystem. Designed to facilitate the creation of applications in C++ and Python across Windows, macOS, Linux, iOS, Android, and emscripten (Web apps), it is ideal for application developers, and researchers eager to dive into GUI development with ease and efficiency. This bundle simplifies the process, allowing you to focus on the creative aspects of your projects.

Dear ImGui Bundle aims to make applications prototyping fast and easy, in a multiplatform / multi-tooling context. The intent is to reduce the time between an idea and a first GUI prototype down to almost zero.

It is well adapted for

  • developers and researchers who want to switch easily between and research and development environment by facilitating the port of research artifacts

  • beginners and developers who want to quickly develop an application without learning a GUI framework

Who is this project not for

You should prefer a more complete framework (such as Qt for example) if your intent is to build a fully fledged application, with support for internationalization, advanced styling, etc.

Also, the library makes no guarantee of ABI stability, and its API is opened to slight adaptations and breaking changes if they are found to make the overall usage better and/or safer.

Acknowledgments

Dear ImGui Bundle would not be possible without the work of the authors of "Dear ImGui", and especially Omar Cornut.

It also includes a lot of other projects, and I’d like to thank their authors for their awesome work!

A particular mention for Evan Pezent (author of ImPlot), Cédric Guillemet (author of ImGuizmo), Balázs Jákó (author of ImGuiColorTextEdit), and Michał Cichoń (author of imgui-node-editor), and Dmitry Mekhontsev (author of imgui-md), Andy Borrel (author of imgui-tex-inspect, another image debugging tool, which I discovered long after having developed immvision).

This doc was built using Asciidoc.

Immvision was inspired by The Image Debugger, by Bill Baxter.

License

The MIT License (MIT)

Copyright (c) 2021-2024 Pascal Thomet

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Alternatives

pyimgui provides battle-tested comprehensive python bindings for ImGui. I worked with this project a lot, and contributed a bit to it. In the end, I had to develop a separate project, in order to be able to add auto-generated and auto-documented python modules.

Dear PyGui (repository) provides python bindings for ImGui with a lot of addons, and a more pythonesque API, which makes it perhaps more suited for Python only projects.

About the author

Dear ImGui Bundle is developed by Pascal Thomet. I am reachable on my Github page. I sometimes blog. There is a playlist related to ImGui Bundle on YouTube.

I have a past in computer vision, and a lot of experience in the trenches between development and research teams; and I found ImGui to be a nice way to reduce the delay between a research prototype and its use in production code.

I also have an inclination for self documenting code, and the doc you are reading was a way to explore new ways to document projects.

How is Dear ImGui Bundle developed

The development of the initial version of Dear ImGui Bundle took about one year at full time.

The bindings are auto-generated thanks to an advanced parser, so that they are easy to keep up to date.

Please be tolerant if you find issues! Dear ImGui Bundle is developed for free, under a very permissive license, by one main author (and most of its API comes from external libraries).

If you need consulting about this library or about the bindings generator in the context of a commercial project, please contact me by email.

Contributions are welcome!

History

Three of my past projects gave me the idea to develop this library.

  • ImGui Manual, an interactive manual for Dear ImGui, which I developed in June 2020

  • implot demo which I developed in 2020.

  • imgui_datascience, a python package I developed in 2018 for image analysis and debugging. Its successor is immvision.

Developments for Dear ImGui Bundle and its related automatic binding generator began in january 2022.

FAQ

See FAQ

Developer docs

imgui_bundle's People

Contributors

aman-anas avatar andreondra avatar dcnieho avatar dependabot[bot] avatar district10 avatar hugle avatar jc211 avatar learn-more avatar nick-0 avatar pthom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

imgui_bundle's Issues

Heatmap Demo

Not sure how to do a PR with the auto-generator, so I thought i would leave this here.

image

Minimal example:

from imgui_bundle import imgui, immapp, implot
import numpy as np


def gui():
    imgui.text("Hello, heatmap!")
    x = np.linspace(-4,4, 401)
    xx = np.outer(x,x)
    yy = np.sinc(xx)
    rows,cols = yy.shape
    n_ticks = 5
    x_ticks = y_ticks =[str(x) for x in np.linspace(-4,4,n_ticks)]

    axis_flags = implot.AxisFlags_.lock | implot.AxisFlags_.no_grid_lines | implot.AxisFlags_.no_tick_marks
    cmap = implot.Colormap_.viridis
    implot.push_colormap(cmap)
    imgui.begin_group()
    if implot.begin_plot("Sinc Function",[imgui.get_content_region_avail().x -70,-1],implot.Flags_.no_legend | implot.Flags_.no_mouse_text):
        implot.setup_axes(None,None, axis_flags, axis_flags)
        implot.setup_axis_ticks(implot.ImAxis_.x1,0,1,n_ticks,x_ticks,False)
        implot.setup_axis_ticks(implot.ImAxis_.y1,0,1,n_ticks,y_ticks,False)
        implot.plot_heatmap("##heatmap", yy, rows, cols, yy.min(), yy.max(), None, [0,1],[1,0],0)
        implot.end_plot()
    imgui.end_group()
    imgui.same_line()
    implot.colormap_scale("##heatmap_scale",yy.min(),yy.max(),imgui.ImVec2(60,-1),"%g", 0,cmap)
    implot.pop_colormap()



immapp.run(
    gui_function=gui,  # The Gui function to run
    window_title="Hello Heatmap",  # the window title
    window_size_auto=True,  # Auto size the application window given its widgets
    # Uncomment the next line to restore window position and size from previous run
    # window_restore_previous_geometry==True
    with_implot=True
)

And the bindings:

m.def("setup_axis_ticks", 
    [](ImAxis axis, double v_min, double v_max, int n_ticks, std::vector<std::string> labels, bool keep_default=false)
    {
        std::vector<const char*> label_char;
        for (std::string const& str : labels){
            label_char.push_back(str.data());
        }
        ImPlot::SetupAxisTicks(axis, v_min, v_max, n_ticks, label_char.data(), keep_default);
    }, py::arg("axis"), py::arg("v_min"), py::arg("v_max"), py::arg("n_ticks"), py::arg("labels"), py::arg("keep_default"),
    "Sets an axis' ticks and optionally the labels for the next plot. To keep the default ticks, set #keep_default=true."
    );

    m.def("plot_heatmap",
        [](const char * label_id, const py::array & values, int rows, int cols, double scale_min = 0, double scale_max=0, const char * label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags = 0)
        {
            auto PlotHeatmap_adapt_c_buffers = [](const char * label_id, const py::array & values, int rows, int cols, double scale_min = 0, double scale_max=0, const char * label_fmt="%.1f",  const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1),  ImPlotHeatmapFlags flags = 0)
            {
                // convert py::array to C standard buffer (const)
                const void * values_from_pyarray = values.data();

                #ifdef _WIN32
                using np_uint_l = uint32_t;
                using np_int_l = int32_t;
                #else
                using np_uint_l = uint64_t;
                using np_int_l = int64_t;
                #endif
                // call the correct template version by casting
                char values_type = values.dtype().char_();
                if (values_type == 'B')
                    ImPlot::PlotHeatmap(label_id, static_cast<const uint8_t *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'b')
                    ImPlot::PlotHeatmap(label_id, static_cast<const int8_t *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'H')
                    ImPlot::PlotHeatmap(label_id, static_cast<const uint16_t *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'h')
                    ImPlot::PlotHeatmap(label_id, static_cast<const int16_t *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'I')
                    ImPlot::PlotHeatmap(label_id, static_cast<const uint32_t *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'i')
                    ImPlot::PlotHeatmap(label_id, static_cast<const int32_t *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'L')
                    ImPlot::PlotHeatmap(label_id, static_cast<const np_uint_l *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'l')
                    ImPlot::PlotHeatmap(label_id, static_cast<const np_int_l *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'f')
                    ImPlot::PlotHeatmap(label_id, static_cast<const float *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'd')
                    ImPlot::PlotHeatmap(label_id, static_cast<const double *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'g')
                    ImPlot::PlotHeatmap(label_id, static_cast<const long double *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                else if (values_type == 'q')
                    ImPlot::PlotHeatmap(label_id, static_cast<const long long *>(values_from_pyarray), rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
                // If we reach this point, the array type is not supported!
                else
                    throw std::runtime_error(std::string("Bad array type ('") + values_type + "') for param values");
            };

            PlotHeatmap_adapt_c_buffers(label_id, values,  rows, cols, scale_min, scale_max, label_fmt, bounds_min, bounds_max, flags);
        },     py::arg("label_id"), py::arg("values"), py::arg("rows"), py::arg("cols"), py::arg("scale_min")= 0 , py::arg("scale_max") = 0, py::arg("label_fmt")="%.1f", py::arg("bounds_min")= (0,0), py::arg("bounds_max")=(1,1),py::arg("flags")=0 );

~Nick

assert error when creating tab bar

Thanks for your work. I'm testing imgui functions and met the following error when creating tab bar and tab items.

from imgui_bundle import imgui, immapp, hello_imgui

def show_gui():
    if imgui.is_key_pressed(imgui.Key.escape):
        runner_params.app_shall_exit = True

    if imgui.begin_tab_bar('###tabs', imgui.TabBarFlags_.none):
        if imgui.begin_tab_item('sizes'):
            imgui.text('sizes')
            imgui.end_tab_item()
        if imgui.begin_tab_item('colors'):
            imgui.text('colors')
            imgui.end_tab_item()
        imgui.end_tab_bar()
    
if __name__ == "__main__":
    runner_params = hello_imgui.RunnerParams()
    runner_params.callbacks.show_gui = show_gui
    immapp.run(runner_params=runner_params)

assert error:

Traceback (most recent call last):
  File "gui.py", line 26, in <module>
    immapp.run(runner_params=runner_params)
  File "gui.py", line 21, in show_gui
    imgui.end_tab_bar()
RuntimeError: IM_ASSERT( window->IDStack.Size > 1 )   ---   imgui.cpp:8170

It's ok with only one single tab item.
Where did the problem occur?

Usability issue around MenuItem binding

@dcnieho : this is a question for you

When we last discussed about Selectable and MenuItem, we decided to disable some overload because they had the same signature on the python side.

See #36

For MenuItem, we only selected the second overload of those two:

#ifdef IMGUI_BUNDLE_PYTHON_UNSUPPORTED_API
    IMGUI_API bool          MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true);  // return true when activated.
#endif
    IMGUI_API bool          MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true);              // return true when activated + toggle (*p_selected) if p_selected != NULL

However, my opinion is that the second overload is not that friendly in Python. For example, in C++ I can write

if (ImGui::MenuItem("A Custom app menu item"))
      HelloImGui::Log(HelloImGui::LogLevel::Info, "Clicked on A Custom app menu item");

However in python, I must write:

  clicked, _ = imgui.menu_item("A Custom app menu item", "", False)
  if clicked:
      hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on A Custom app menu item")

The second overload has the advantage (and default) of returning a tuple with the selected bool value. However it is not that difficult to switch from the user side.

What do you think?

Could you also bind ImGui's OpenGL2 backend?

Hi, Pascal,

I just met this awesome repo and trying to use it in my projects.
There were some shaders only support OpenGL 2.1/GLSL 120.
So I have to use GL2 in my project.
I saw imgui_bundle only binded GL3 backend as below.

m.def("opengl3_init",
ImGui_ImplOpenGL3_Init, py::arg("glsl_version"));
m.def("opengl3_shutdown",
ImGui_ImplOpenGL3_Shutdown);
m.def("opengl3_new_frame",
ImGui_ImplOpenGL3_NewFrame);
m.def("opengl3_render_draw_data",
ImGui_ImplOpenGL3_RenderDrawData, py::arg("draw_data"));
m.def("opengl3_create_fonts_texture",
ImGui_ImplOpenGL3_CreateFontsTexture);
m.def("opengl3_destroy_fonts_texture",
ImGui_ImplOpenGL3_DestroyFontsTexture);
m.def("opengl3_create_device_objects",
ImGui_ImplOpenGL3_CreateDeviceObjects);
m.def("opengl3_destroy_device_objects",
ImGui_ImplOpenGL3_DestroyDeviceObjects);
//////////////////// </generated_from:imgui_impl_opengl3.h> ////////////////////

Could you also add the Dear ImGui's OpenGL2 backend support?

IMGUI_IMPL_API bool     ImGui_ImplOpenGL2_Init();
IMGUI_IMPL_API void     ImGui_ImplOpenGL2_Shutdown();
IMGUI_IMPL_API void     ImGui_ImplOpenGL2_NewFrame();
IMGUI_IMPL_API void     ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data);


// Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool     ImGui_ImplOpenGL2_CreateFontsTexture();
IMGUI_IMPL_API void     ImGui_ImplOpenGL2_DestroyFontsTexture();
IMGUI_IMPL_API bool     ImGui_ImplOpenGL2_CreateDeviceObjects();
IMGUI_IMPL_API void     ImGui_ImplOpenGL2_DestroyDeviceObjects();

two versions of imgui.selectable

Ok, last comment for now. Nice work man on the wrapper, i got my first windows to show even though not everything works yet. Anyway, what i run in now is that there are two versions of imgui.selectable (and i guess there are more like this one) where one takes a bool and returns a bool, and the other takes a pointer to bool and returns a tuple of two bools. Here i made it error so you can see the signatures:

TypeError: selectable(): incompatible function arguments. The following argument types are supported:
    1. (label: str, selected: bool = False, flags: int = 0, size: imgui_bundle._imgui_bundle.imgui.ImVec2 = <imgui_bundle._imgui_bundle.imgui.ImVec2 object at 0x00000285EAB525F0>) -> bool
    2. (label: str, p_selected: bool, flags: int = 0, size: imgui_bundle._imgui_bundle.imgui.ImVec2 = <imgui_bundle._imgui_bundle.imgui.ImVec2 object at 0x00000285EA6D2770>) -> Tuple[bool, bool]

I want to use the latter, but get the former. Even though both are defined in the generated pybind code. I guess that is because their python function syntax is identical? Both just take a bool. Perhaps this is user error. How do i call the latter one with signature returning -> Tuple[bool, bool]?

We had a closely related discussion about this before, IIRC. The two functions are redundant for the python binding, so we have to choose one style. for pyimgui compatibility (and because i know of no other good reason) and additional flexibility (this form is more informative) i vote to only expose version 2. I'm not sure if this case can be automatically detected (that two different c++ function lead to a python function with the same arguments) and the choice automated.

ImFontAtlas::GetTexDataAsRGBA32()

I asked about getting access to ImFontAtlas::GetTexDataAsRGBA32(). You implemented font_atlas_get_tex_data_as_rgba32, but i notice that it is only present (commented out) in the .pyi file. Have you nixed it and i missed the testing window?

Font loading doesn't work for me

And in my port i don't get font loading to work. That is, my icons show up as question marks. I have tried adding

imgui_backends.opengl3_destroy_fonts_texture()
imgui_backends.opengl3_create_fonts_texture()

after the calls to imgui.font_atlas_add_font_from_file_ttf which i think should be needed because else the new font texture doesn't get built (not needed in your demo though, as you call these before the very first frame is rendered, and fonts are set up in ImGui_ImplOpenGL3_CreateDeviceObjects() which is called the first frame). So adding those calls didn't help. Guess i am doing something wrong as your demo does work. But what i have now worked with pyimgui, so i am wondering what the difference/my oversight is...

Also, when i try to use the ImFont returned from imgui.font_atlas_add_font_from_file_ttf with imgui.push_font, it asserts, probably because the font isn't loaded? I may be missing something here. Code is around here: https://github.com/dcnieho/glassesValidator/blob/imgui_bundle/src/glassesValidator/GUI/_impl/gui.py#L982

draw_list.add_image_rounded signature issue?

This might be user error. I have an image i want to draw. It is uploaded through direct interaction with OpenGL and i have a texture id. I then call draw_list.add_image_rounded(texture_id, pos, pos2, *args, flags=flags, **kwargs) and get the following error

TypeError: add_image_rounded(): incompatible function arguments. The following argument types are supported:
    1. (self: imgui_bundle._imgui_bundle.imgui.ImDrawList, user_texture_id: capsule, p_min: imgui_bundle._imgui_bundle.imgui.ImVec2, p_max: imgui_bundle._imgui_bundle.imgui.ImVec2, uv_min: imgui_bundle._imgui_bundle.imgui.ImVec2, uv_max: imgui_bundle._imgui_bundle.imgui.ImVec2, col: int, rounding: float, flags: int = 0) -> None

Invoked with: <imgui_bundle._imgui_bundle.imgui.ImDrawList object at 0x000001973685C630>, 2, <imgui_bundle._imgui_bundle.imgui.ImVec2 object at 0x000001973685C0F0>, <imgui_bundle._imgui_bundle.imgui.ImVec2 object at 0x000001973685C1B0>; kwargs: flags=<ImDrawFlags_.round_corners_all: 240>, rounding=6.0

Removing *args from the call (its an empty tuple) and explicitly naming the other two arguments as p_min and p_max doesn't change this, same incompatible arguments error.

type(self.texture_id) returns <class 'numpy.uintc'>, as is (apparently) returned by gl.glGenTextures(1) (import OpenGL.GL as gl)
I note that 'add_image_rounded()' expects a capsule, but i do not know what that is or how to provide it. I would expect providing a normal uint, like with other opengl programming, should work.

fonts really required ?

hye, thanks for the work !!
is there a way to remove so much fonts required on a basic integration ?
thanks

Installation fails on python 3.12 (pre-release)

Initially i ran into PyGLM failing to install but after seeing it's apparently only used in one imgui-bundle demo, i tried removing it from setup.py but now building wheels for imgui-bundle fails.
(maybe because i removed it, i'm not familiar with building packages)
Python version: 3.12.0rc3

Log:

PS E:\SoS-TAS> pip install git+https://github.com/shenef/imgui_bundle.git
Collecting git+https://github.com/shenef/imgui_bundle.git
  Cloning https://github.com/shenef/imgui_bundle.git to c:\users\shen\appdata\local\temp\pip-req-build-b8604ehv
  Running command git clone --filter=blob:none --quiet https://github.com/shenef/imgui_bundle.git 'C:\Users\shen\AppData\Local\Temp\pip-req-build-b8604ehv'
  Resolved https://github.com/shenef/imgui_bundle.git to commit 37380db6c53953f6807e27e5713aff1389b140dd
  Running command git submodule update --init --recursive -q
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
Requirement already satisfied: numpy>=1.15 in c:\users\shen\appdata\local\programs\python\python312\lib\site-packages (from imgui-bundle==0.8.8) (1.26.0rc1)
Collecting munch>=2.0.0 (from imgui-bundle==0.8.8)
  Obtaining dependency information for munch>=2.0.0 from https://files.pythonhosted.org/packages/56/b3/7c69b37f03260a061883bec0e7b05be7117c1b1c85f5212c72c8c2bc3c8c/munch-4.0.0-py2.py3-none-any.whl.metadata
  Using cached munch-4.0.0-py2.py3-none-any.whl.metadata (5.9 kB)
Requirement already satisfied: glfw>2.5 in c:\users\shen\appdata\local\programs\python\python312\lib\site-packages (from imgui-bundle==0.8.8) (2.6.2)
Requirement already satisfied: PyOpenGL>=3.0 in c:\users\shen\appdata\local\programs\python\python312\lib\site-packages (from imgui-bundle==0.8.8) (3.1.7)
Collecting pillow>=9.0.0 (from imgui-bundle==0.8.8)
  Obtaining dependency information for pillow>=9.0.0 from https://files.pythonhosted.org/packages/00/00/d42abea0b4a415ea1e0975c981f95f354de1280d7e7bef3882bf1464b795/Pillow-10.0.1-cp312-cp312-win_amd64.whl.metadata
  Downloading Pillow-10.0.1-cp312-cp312-win_amd64.whl.metadata (9.6 kB)
Downloading munch-4.0.0-py2.py3-none-any.whl (9.9 kB)
Downloading Pillow-10.0.1-cp312-cp312-win_amd64.whl (2.5 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.5/2.5 MB 7.6 MB/s eta 0:00:00
Building wheels for collected packages: imgui-bundle
  Building wheel for imgui-bundle (pyproject.toml) ... error
  error: subprocess-exited-with-error

  × Building wheel for imgui-bundle (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> [355 lines of output]


      --------------------------------------------------------------------------------
      -- Trying 'Ninja (Visual Studio 17 2022 x64 v143)' generator
      --------------------------------
      ---------------------------
      ----------------------
      -----------------
      ------------
      -------
      --
      Not searching for unused variables given on the command line.
      CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
        Compatibility with CMake < 3.5 will be removed from a future version of
        CMake.

        Update the VERSION argument <min> value or use a ...<max> suffix to tell
        CMake that the project does not need compatibility with older versions.


      -- The C compiler identification is MSVC 19.36.32535.0
      -- Detecting C compiler ABI info
      -- Detecting C compiler ABI info - done
      -- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.36.32532/bin/Hostx86/x64/cl.exe - skipped
      -- Detecting C compile features
      -- Detecting C compile features - done
      -- The CXX compiler identification is MSVC 19.36.32535.0
      -- Detecting CXX compiler ABI info
      -- Detecting CXX compiler ABI info - done
      -- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.36.32532/bin/Hostx86/x64/cl.exe - skipped
      -- Detecting CXX compile features
      -- Detecting CXX compile features - done
      -- Configuring done (2.8s)
      -- Generating done (0.0s)
      -- Build files have been written to: C:/Users/shen/AppData/Local/Temp/pip-req-build-b8604ehv/_cmake_test_compile/build
      --
      -------
      ------------
      -----------------
      ----------------------
      ---------------------------
      --------------------------------
      -- Trying 'Ninja (Visual Studio 17 2022 x64 v143)' generator - success
      --------------------------------------------------------------------------------

      Configuring Project
        Working directory:
          C:\Users\shen\AppData\Local\Temp\pip-req-build-b8604ehv\_skbuild\win-amd64-3.12\cmake-build
        Command:
          'C:\Users\shen\AppData\Local\Temp\pip-build-env-j79e7p0d\overlay\Lib\site-packages\cmake\data\bin/cmake.exe' 'C:\Users\shen\AppData\Local\Temp\pip-req-build-b8604ehv' -G Ninja -D_SKBUILD_FORCE_MSVC=1930 --no-warn-unused-cli '-DCMAKE_INSTALL_PREFIX:PATH=C:\Users\shen\AppData\Local\Temp\pip-req-build-b8604ehv\_skbuild\win-amd64-3.12\cmake-install\bindings\imgui_bundle' -DPYTHON_VERSION_STRING:STRING=3.12.0rc3 -DSKBUILD:INTERNAL=TRUE '-DCMAKE_MODULE_PATH:PATH=C:\Users\shen\AppData\Local\Temp\pip-build-env-j79e7p0d\overlay\Lib\site-packages\skbuild\resources\cmake' '-DPYTHON_EXECUTABLE:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\python.exe' '-DPYTHON_INCLUDE_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\Include' '-DPYTHON_LIBRARY:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\libs\python312.lib' '-DPython_EXECUTABLE:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\python.exe' '-DPython_ROOT_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312' -DPython_FIND_REGISTRY:STRING=NEVER '-DPython_INCLUDE_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\Include' '-DPython_LIBRARY:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\libs\python312.lib' '-DPython3_EXECUTABLE:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\python.exe' '-DPython3_ROOT_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312' -DPython3_FIND_REGISTRY:STRING=NEVER '-DPython3_INCLUDE_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\Include' '-DPython3_LIBRARY:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\libs\python312.lib' -DCMAKE_BUILD_TYPE:STRING=Release

      Not searching for unused variables given on the command line.
      -- The C compiler identification is MSVC 19.36.32535.0
      -- The CXX compiler identification is MSVC 19.36.32535.0
      -- Detecting C compiler ABI info
      -- Detecting C compiler ABI info - done
      -- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.36.32532/bin/Hostx86/x64/cl.exe - skipped
      -- Detecting C compile features
      -- Detecting C compile features - done
      -- Detecting CXX compiler ABI info
      -- Detecting CXX compiler ABI info - done
      -- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.36.32532/bin/Hostx86/x64/cl.exe - skipped
      -- Detecting CXX compile features
      -- Detecting CXX compile features - done
      'C:/Users/shen/AppData/Local/Programs/Python/Python312/python.exe' '-c' 'import pybind11; print(pybind11.get_cmake_dir())'
      CMake Warning (dev) at C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/pybind11/share/cmake/pybind11/FindPythonLibsNew.cmake:98 (find_package):
        Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules
        are removed.  Run "cmake --help-policy CMP0148" for policy details.  Use
        the cmake_policy command to set the policy and suppress this warning.

      Call Stack (most recent call first):
        C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/pybind11/share/cmake/pybind11/pybind11Tools.cmake:50 (find_package)
        C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/pybind11/share/cmake/pybind11/pybind11Common.cmake:188 (include)
        C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/pybind11/share/cmake/pybind11/pybind11Config.cmake:250 (include)
        cmake/find_pybind11.cmake:20 (find_package)
        CMakeLists.txt:132 (find_pybind11)
      This warning is for project developers.  Use -Wno-dev to suppress it.

      -- Found PythonInterp: C:/Users/shen/AppData/Local/Programs/Python/Python312/python.exe (found suitable version "3.12", minimum required is "3.6")
      -- Found PythonLibs: C:/Users/shen/AppData/Local/Programs/Python/Python312/libs/python312.lib
      -- Performing Test HAS_MSVC_GL_LTCG
      -- Performing Test HAS_MSVC_GL_LTCG - Success
      -- Found pybind11: C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/pybind11/include (found version "2.11.1")
      -- Performing Test CMAKE_HAVE_LIBC_PTHREAD
      -- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
      -- Looking for pthread_create in pthreads
      -- Looking for pthread_create in pthreads - not found
      -- Looking for pthread_create in pthread
      -- Looking for pthread_create in pthread - not found
      -- Found Threads: TRUE
      -- Using Win32 for window creation
      CMake Deprecation Warning at external/SDL/SDL/CMakeLists.txt:5 (cmake_minimum_required):
        Compatibility with CMake < 3.5 will be removed from a future version of
        CMake.

        Update the VERSION argument <min> value or use a ...<max> suffix to tell
        CMake that the project does not need compatibility with older versions.


      -- Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE)
      -- Looking for __GLIBC__
      -- Looking for __GLIBC__ - not found
      -- Looking for immintrin.h
      -- Looking for immintrin.h - found
      -- Looking for stdint.h
      -- Looking for stdint.h - found
      -- Performing Test HAVE_WIN32_CC
      -- Performing Test HAVE_WIN32_CC - Success
      -- Looking for d3d9.h
      -- Looking for d3d9.h - found
      -- Looking for d3d11_1.h
      -- Looking for d3d11_1.h - found
      -- Performing Test HAVE_D3D12_H
      -- Performing Test HAVE_D3D12_H - Success
      -- Looking for ddraw.h
      -- Looking for ddraw.h - found
      -- Looking for dsound.h
      -- Looking for dsound.h - found
      -- Looking for dinput.h
      -- Looking for dinput.h - found
      -- Looking for dxgi.h
      -- Looking for dxgi.h - found
      -- Performing Test HAVE_XINPUT_H
      -- Performing Test HAVE_XINPUT_H - Success
      -- Performing Test HAVE_XINPUT_GAMEPAD_EX
      -- Performing Test HAVE_XINPUT_GAMEPAD_EX - Failed
      -- Performing Test HAVE_XINPUT_STATE_EX
      -- Performing Test HAVE_XINPUT_STATE_EX - Failed
      -- Performing Test HAVE_WINDOWS_GAMING_INPUT_H
      -- Performing Test HAVE_WINDOWS_GAMING_INPUT_H - Success
      -- Looking for tpcshrd.h
      -- Looking for tpcshrd.h - found
      -- Looking for roapi.h
      -- Looking for roapi.h - found
      -- Looking for mmdeviceapi.h
      -- Looking for mmdeviceapi.h - found
      -- Looking for audioclient.h
      -- Looking for audioclient.h - found
      -- Looking for sensorsapi.h
      -- Looking for sensorsapi.h - found
      -- Looking for shellscalingapi.h
      -- Looking for shellscalingapi.h - found
      -- Found Git: C:/Program Files/Git/cmd/git.exe (found version "2.41.0.windows.2")
      CMake Deprecation Warning at external/SDL/SDL/CMakeLists.txt:3143 (cmake_minimum_required):
        Compatibility with CMake < 3.5 will be removed from a future version of
        CMake.

        Update the VERSION argument <min> value or use a ...<max> suffix to tell
        CMake that the project does not need compatibility with older versions.


      --
      -- SDL2 was configured with the following options:
      --
      -- Platform: Windows-10.0.22631
      -- 64-bit:   TRUE
      -- Compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.36.32532/bin/Hostx86/x64/cl.exe
      -- Revision: SDL-release-2.26.1-11-gd992d4f23
      --
      -- Subsystems:
      --   Atomic:        ON
      --   Audio: ON
      --   Video: ON
      --   Render:        ON
      --   Events:        ON
      --   Joystick:      ON
      --   Haptic:        ON
      --   Hidapi:        ON
      --   Power: ON
      --   Threads:       ON
      --   Timers:        ON
      --   File:  ON
      --   Loadso:        ON
      --   CPUinfo:       ON
      --   Filesystem:    ON
      --   Sensor:        ON
      --   Locale:        ON
      --   Misc:  ON
      --
      -- Options:
      --   SDL2_DISABLE_INSTALL        (Wanted: ON): OFF
      --   SDL2_DISABLE_SDL2MAIN       (Wanted: OFF): OFF
      --   SDL2_DISABLE_UNINSTALL      (Wanted: OFF): OFF
      --   SDL_3DNOW                   (Wanted: ON): OFF
      --   SDL_ALSA                    (Wanted: OFF): OFF
      --   SDL_ALSA_SHARED             (Wanted: OFF): OFF
      --   SDL_ALTIVEC                 (Wanted: ON): OFF
      --   SDL_ARMNEON                 (Wanted: OFF): OFF
      --   SDL_ARMSIMD                 (Wanted: OFF): OFF
      --   SDL_ARTS                    (Wanted: OFF): OFF
      --   SDL_ARTS_SHARED             (Wanted: OFF): OFF
      --   SDL_ASAN                    (Wanted: OFF): OFF
      --   SDL_ASSEMBLY                (Wanted: ON): OFF
      --   SDL_ASSERTIONS              (Wanted: auto): auto
      --   SDL_BACKGROUNDING_SIGNAL    (Wanted: OFF): OFF
      --   SDL_CCACHE                  (Wanted: ON): OFF
      --   SDL_CLOCK_GETTIME           (Wanted: OFF): OFF
      --   SDL_COCOA                   (Wanted: OFF): OFF
      --   SDL_DBUS                    (Wanted: OFF): OFF
      --   SDL_DIRECTFB                (Wanted: OFF): OFF
      --   SDL_DIRECTFB_SHARED         (Wanted: OFF): OFF
      --   SDL_DIRECTX                 (Wanted: ON): ON
      --   SDL_DISKAUDIO               (Wanted: ON): ON
      --   SDL_DUMMYAUDIO              (Wanted: ON): ON
      --   SDL_DUMMYVIDEO              (Wanted: ON): ON
      --   SDL_ESD                     (Wanted: OFF): OFF
      --   SDL_ESD_SHARED              (Wanted: OFF): OFF
      --   SDL_FOREGROUNDING_SIGNAL    (Wanted: OFF): OFF
      --   SDL_FUSIONSOUND             (Wanted: OFF): OFF
      --   SDL_FUSIONSOUND_SHARED      (Wanted: OFF): OFF
      --   SDL_GCC_ATOMICS             (Wanted: OFF): OFF
      --   SDL_HIDAPI                  (Wanted: ON): ON
      --   SDL_HIDAPI_JOYSTICK         (Wanted: ON): ON
      --   SDL_HIDAPI_LIBUSB           (Wanted: OFF): OFF
      --   SDL_IBUS                    (Wanted: OFF): OFF
      --   SDL_INSTALL_TESTS           (Wanted: OFF): OFF
      --   SDL_JACK                    (Wanted: OFF): OFF
      --   SDL_JACK_SHARED             (Wanted: OFF): OFF
      --   SDL_KMSDRM                  (Wanted: OFF): OFF
      --   SDL_KMSDRM_SHARED           (Wanted: OFF): OFF
      --   SDL_LIBC                    (Wanted: OFF): OFF
      --   SDL_LIBSAMPLERATE           (Wanted: OFF): OFF
      --   SDL_LIBSAMPLERATE_SHARED    (Wanted: OFF): OFF
      --   SDL_METAL                   (Wanted: OFF): OFF
      --   SDL_MMX                     (Wanted: ON): OFF
      --   SDL_NAS                     (Wanted: OFF): OFF
      --   SDL_NAS_SHARED              (Wanted: OFF): OFF
      --   SDL_OFFSCREEN               (Wanted: ON): ON
      --   SDL_OPENGL                  (Wanted: ON): ON
      --   SDL_OPENGLES                (Wanted: ON): ON
      --   SDL_OSS                     (Wanted: OFF): OFF
      --   SDL_PIPEWIRE                (Wanted: OFF): OFF
      --   SDL_PIPEWIRE_SHARED         (Wanted: OFF): OFF
      --   SDL_PTHREADS                (Wanted: OFF): OFF
      --   SDL_PTHREADS_SEM            (Wanted: OFF): OFF
      --   SDL_PULSEAUDIO              (Wanted: OFF): OFF
      --   SDL_PULSEAUDIO_SHARED       (Wanted: OFF): OFF
      --   SDL_RENDER_D3D              (Wanted: ON): ON
      --   SDL_RENDER_METAL            (Wanted: OFF): OFF
      --   SDL_RPATH                   (Wanted: OFF): OFF
      --   SDL_RPI                     (Wanted: OFF): OFF
      --   SDL_SNDIO                   (Wanted: OFF): OFF
      --   SDL_SNDIO_SHARED            (Wanted: OFF): OFF
      --   SDL_SSE                     (Wanted: ON): ON
      --   SDL_SSE2                    (Wanted: ON): ON
      --   SDL_SSE3                    (Wanted: ON): ON
      --   SDL_SSEMATH                 (Wanted: ON): OFF
      --   SDL_STATIC_PIC              (Wanted: OFF): OFF
      --   SDL_SYSTEM_ICONV            (Wanted: ON): OFF
      --   SDL_TESTS                   (Wanted: OFF): OFF
      --   SDL_VENDOR_INFO             (Wanted: ): OFF
      --   SDL_VIRTUAL_JOYSTICK        (Wanted: ON): ON
      --   SDL_VIVANTE                 (Wanted: OFF): OFF
      --   SDL_VULKAN                  (Wanted: ON): ON
      --   SDL_WASAPI                  (Wanted: ON): ON
      --   SDL_WAYLAND                 (Wanted: OFF): OFF
      --   SDL_WAYLAND_LIBDECOR        (Wanted: OFF): OFF
      --   SDL_WAYLAND_LIBDECOR_SHARED (Wanted: OFF): OFF
      --   SDL_WAYLAND_QT_TOUCH        (Wanted: OFF): OFF
      --   SDL_WAYLAND_SHARED          (Wanted: OFF): OFF
      --   SDL_X11                     (Wanted: OFF): OFF
      --   SDL_X11_SHARED              (Wanted: OFF): OFF
      --   SDL_X11_XCURSOR             (Wanted: OFF): OFF
      --   SDL_X11_XDBE                (Wanted: OFF): OFF
      --   SDL_X11_XFIXES              (Wanted: OFF): OFF
      --   SDL_X11_XINPUT              (Wanted: OFF): OFF
      --   SDL_X11_XRANDR              (Wanted: OFF): OFF
      --   SDL_X11_XSCRNSAVER          (Wanted: OFF): OFF
      --   SDL_X11_XSHAPE              (Wanted: OFF): OFF
      --   SDL_XINPUT                  (Wanted: ON): ON
      --
      --  CFLAGS:        /DWIN32 /D_WINDOWS
      --  EXTRA_CFLAGS:
      --  EXTRA_LDFLAGS:
      --  EXTRA_LIBS:    user32;gdi32;winmm;imm32;ole32;oleaut32;version;uuid;advapi32;setupapi;shell32;dinput8
      --
      --  Build Shared Library: ON
      --  Build Static Library: OFF
      --
      -- Performing Test CHECK_CPU_ARCHITECTURE_X86
      -- Performing Test CHECK_CPU_ARCHITECTURE_X86 - Failed
      ccache not found cannot use
      -- Library hello_imgui
      CMake Warning at external/hello_imgui/hello_imgui/src/hello_imgui/CMakeLists.txt:64 (find_package):
        By not providing "FindSDL2.cmake" in CMAKE_MODULE_PATH this project has
        asked CMake to find a package configuration file provided by "SDL2", but
        CMake did not find one.

        Could not find a package configuration file provided by "SDL2" with any of
        the following names:

          SDL2Config.cmake
          sdl2-config.cmake

        Add the installation prefix of "SDL2" to CMAKE_PREFIX_PATH or set
        "SDL2_DIR" to a directory containing one of the above files.  If "SDL2"
        provides a separate development package or SDK, be sure it has been
        installed.
      Call Stack (most recent call first):
        external/hello_imgui/hello_imgui/src/hello_imgui/CMakeLists.txt:86 (link_sdl)


      CMake Warning at external/immvision/immvision/cmake/find_opencv.cmake:216 (find_package):
        By not providing "FindOpenCV.cmake" in CMAKE_MODULE_PATH this project has
        asked CMake to find a package configuration file provided by "OpenCV", but
        CMake did not find one.

        Could not find a package configuration file provided by "OpenCV" with any
        of the following names:

          OpenCVConfig.cmake
          opencv-config.cmake

        Add the installation prefix of "OpenCV" to CMAKE_PREFIX_PATH or set
        "OpenCV_DIR" to a directory containing one of the above files.  If "OpenCV"
        provides a separate development package or SDK, be sure it has been
        installed.
      Call Stack (most recent call first):
        external/immvision/CMakeLists.txt:2 (immvision_find_opencv)


      FIND OPENCV use immvision_download_opencv_official_package_win
      -- Populating opencv_official_package_win
      -- Configuring done (0.2s)
      -- Generating done (0.0s)
      -- Build files have been written to: C:/Users/shen/AppData/Local/Temp/pip-req-build-b8604ehv/_skbuild/win-amd64-3.12/cmake-build/_deps/opencv_official_package_win-subbuild
      ninja: error: Stat(C:/Users/shen/AppData/Local/Temp/pip-req-build-b8604ehv/_skbuild/win-amd64-3.12/cmake-build/_deps/opencv_official_package_win-subbuild/opencv_official_package_win-populate-prefix/src/opencv_official_package_win-populate-stamp/opencv_official_package_win-populate-done): Filename longer than 260 characters
      CMake Error at C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/cmake/data/share/cmake-3.27/Modules/FetchContent.cmake:1662 (message):
        Build step for opencv_official_package_win failed: 1
      Call Stack (most recent call first):
        C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/cmake/data/share/cmake-3.27/Modules/FetchContent.cmake:1802:EVAL:2 (__FetchContent_directPopulate)
        C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/cmake/data/share/cmake-3.27/Modules/FetchContent.cmake:1802 (cmake_language)
        C:/Users/shen/AppData/Local/Temp/pip-build-env-j79e7p0d/overlay/Lib/site-packages/cmake/data/share/cmake-3.27/Modules/FetchContent.cmake:2016 (FetchContent_Populate)
        external/immvision/immvision/cmake/find_opencv.cmake:53 (FetchContent_MakeAvailable)
        external/immvision/immvision/cmake/find_opencv.cmake:222 (immvision_download_opencv_official_package_win)
        external/immvision/CMakeLists.txt:2 (immvision_find_opencv)


      -- Configuring incomplete, errors occurred!
      Traceback (most recent call last):
        File "C:\Users\shen\AppData\Local\Temp\pip-build-env-j79e7p0d\overlay\Lib\site-packages\skbuild\setuptools_wrap.py", line 666, in setup
          env = cmkr.configure(
                ^^^^^^^^^^^^^^^
        File "C:\Users\shen\AppData\Local\Temp\pip-build-env-j79e7p0d\overlay\Lib\site-packages\skbuild\cmaker.py", line 357, in configure
          raise SKBuildError(msg)

      An error occurred while configuring with CMake.
        Command:
          'C:\Users\shen\AppData\Local\Temp\pip-build-env-j79e7p0d\overlay\Lib\site-packages\cmake\data\bin/cmake.exe' 'C:\Users\shen\AppData\Local\Temp\pip-req-build-b8604ehv' -G Ninja -D_SKBUILD_FORCE_MSVC=1930 --no-warn-unused-cli '-DCMAKE_INSTALL_PREFIX:PATH=C:\Users\shen\AppData\Loca\overlay\Lib\site-packages\skbuild\resources\cmake' '-DPYTHON_EXECUTABLE:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\python.exe' '-DPYTHON_INCLUDE_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\Include' '-DPYTHON_LIBRARY:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\libs\python312.lib' '-DPython_EXECUTABLE:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\python.exe' '-DPython_ROOT_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312' -DPython_FIND_REGISTRY:STRING=NEVER '-DPython_INCLUDE_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\Include' '-DPython_LIBRARY:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\libs\python312.lib' '-DPython3_EXECUTABLE:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\python.exe' '-DPython3_ROOT_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312' -DPython3_FIND_REGISTRY:STRING=NEVER '-DPython3_INCLUDE_DIR:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\Include' '-DPython3_LIBRARY:PATH=C:\Users\shen\AppData\Local\Programs\Python\Python312\libs\python312.lib' -DCMAKE_BUILD_TYPE:STRING=Release
        Source directory:
          C:\Users\shen\AppData\Local\Temp\pip-req-build-b8604ehv
        Working directory:
          C:\Users\shen\AppData\Local\Temp\pip-req-build-b8604ehv\_skbuild\win-amd64-3.12\cmake-build
      Please see CMake's output for more information.

      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for imgui-bundle
Failed to build imgui-bundle
ERROR: Could not build wheels for imgui-bundle, which is required to install pyproject.toml-based projects

Regarding PyGLM, i already created an issue in their repo.

demo_tex_inspect problems

  1. Running it gives me a window that is vertically so big it doesn't fit on my screen.
  2. when running i do get an error:
ERROR: 1:23: 'function' : is removed in Forward Compatible context texture2D
ERROR: 1:23: 'texture2D' : no matching overloaded function found (using implicit conversion)
ERROR: 1:23: 'texture2D' : function is not known
ERROR: 1:23: '+' : wrong operand types no operation '+' exists that takes a left-hand operand of type '4X4 matrix of highp float' and a right operand of type 'uniform 4-component vector of highp float' (or there is no acceptable conversion)
ERROR: 1:23: '=' :  cannot convert from '4X4 matrix of highp float' to '4-component vector of highp float'


ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link shader program! (with GLSL '#version 130
')
Attached fragment shader is not compiled.```

It does seem to work, so perhaps not an issue otherwise. Keep it and report to library author?

how to pip install -v . on windows? multi error

          Hmm, i see that pthom/litgen actually forwards to pthom/srcmlcpp. So i just updated the requirements-dev.txt file.

That gets me to here:

Building wheels for collected packages: srcmlcpp, srcml_caller
  Building wheel for srcmlcpp (pyproject.toml) ... done
  Created wheel for srcmlcpp: filename=srcmlcpp-0.1.0-py3-none-any.whl size=613575 sha256=548a49c907bf7d799ce5738b5e8e02cc6b7854e609900f1e11e88cc4bc34d8a4
  Stored in directory: C:\Users\huml-dkn\AppData\Local\Temp\pip-ephem-wheel-cache-yu5eydfe\wheels\4c\fb\21\2cbb57b39f4b643cb14b59b7f54a4519e0e3c381cc4d608eed
  Building wheel for srcml_caller (pyproject.toml) ... error
  error: subprocess-exited-with-error

  × Building wheel for srcml_caller (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> [111 lines of output]


      --------------------------------------------------------------------------------
      -- Trying "Ninja (Visual Studio 17 2022 x64 v143)" generator
      --------------------------------
      ---------------------------
      ----------------------
      -----------------
      ------------
      -------
      --
      Not searching for unused variables given on the command line.
      -- The C compiler identification is MSVC 19.34.31935.0
      -- Detecting C compiler ABI info
      -- Detecting C compiler ABI info - done
      -- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx86/x64/cl.exe - skipped
      -- Detecting C compile features
      -- Detecting C compile features - done
      -- The CXX compiler identification is MSVC 19.34.31935.0
      CMake Warning (dev) at C:/Users/huml-dkn/AppData/Local/Temp/pip-build-env-veuax1i0/overlay/Lib/site-packages/cmake/data/share/cmake-3.25/Modules/CMakeDetermineCXXCompiler.cmake:162 (if):
        Policy CMP0054 is not set: Only interpret if() arguments as variables or
        keywords when unquoted.  Run "cmake --help-policy CMP0054" for policy
        details.  Use the cmake_policy command to set the policy and suppress this
        warning.

        Quoted variables like "MSVC" will no longer be dereferenced when the policy
        is set to NEW.  Since the policy is not set the OLD behavior will be used.
      Call Stack (most recent call first):
        CMakeLists.txt:4 (ENABLE_LANGUAGE)
      This warning is for project developers.  Use -Wno-dev to suppress it.

      CMake Warning (dev) at C:/Users/huml-dkn/AppData/Local/Temp/pip-build-env-veuax1i0/overlay/Lib/site-packages/cmake/data/share/cmake-3.25/Modules/CMakeDetermineCXXCompiler.cmake:183 (elseif):
        Policy CMP0054 is not set: Only interpret if() arguments as variables or
        keywords when unquoted.  Run "cmake --help-policy CMP0054" for policy
        details.  Use the cmake_policy command to set the policy and suppress this
        warning.

        Quoted variables like "MSVC" will no longer be dereferenced when the policy
        is set to NEW.  Since the policy is not set the OLD behavior will be used.
      Call Stack (most recent call first):
        CMakeLists.txt:4 (ENABLE_LANGUAGE)
      This warning is for project developers.  Use -Wno-dev to suppress it.

      -- Detecting CXX compiler ABI info
      -- Detecting CXX compiler ABI info - done
      -- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx86/x64/cl.exe - skipped
      -- Detecting CXX compile features
      -- Detecting CXX compile features - done
      -- Configuring done
      -- Generating done
      -- Build files have been written to: C:/Users/huml-dkn/AppData/Local/Temp/pip-install-mycottj2/srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7/_cmake_test_compile/build
      --
      -------
      ------------
      -----------------
      ----------------------
      ---------------------------
      --------------------------------
      -- Trying "Ninja (Visual Studio 17 2022 x64 v143)" generator - success
      --------------------------------------------------------------------------------

      Configuring Project
        Working directory:
          C:\Users\huml-dkn\AppData\Local\Temp\pip-install-mycottj2\srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7\_skbuild\win-amd64-3.10\cmake-build
        Command:
          cmake 'C:\Users\huml-dkn\AppData\Local\Temp\pip-install-mycottj2\srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7' -G Ninja '-DCMAKE_INSTALL_PREFIX:PATH=C:\Users\huml-dkn\AppData\Local\Temp\pip-install-mycottj2\srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7\_skbuild\win-amd64-3.10\cmake-install\bindings\srcml_caller' -DPYTHON_VERSION_STRING:STRING=3.10.7 -DSKBUILD:INTERNAL=TRUE '-DCMAKE_MODULE_PATH:PATH=C:\Users\huml-dkn\AppData\Local\Temp\pip-build-env-veuax1i0\overlay\Lib\site-packages\skbuild\resources\cmake' '-DPYTHON_EXECUTABLE:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv\Scripts\python.exe' '-DPYTHON_INCLUDE_DIR:PATH=C:\Program Files\Python310\Include' '-DPYTHON_LIBRARY:PATH=C:\Program Files\Python310\libs\python310.lib' '-DPython_EXECUTABLE:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv\Scripts\python.exe' '-DPython_ROOT_DIR:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv' '-DPython_INCLUDE_DIR:PATH=C:\Program Files\Python310\Include' -DPython_FIND_REGISTRY:STRING=NEVER '-DPython3_EXECUTABLE:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv\Scripts\python.exe' '-DPython3_ROOT_DIR:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv' '-DPython3_INCLUDE_DIR:PATH=C:\Program Files\Python310\Include' -DPython3_FIND_REGISTRY:STRING=NEVER -DCMAKE_BUILD_TYPE:STRING=Release

      -- The C compiler identification is MSVC 19.34.31935.0
      -- The CXX compiler identification is MSVC 19.34.31935.0
      -- Detecting C compiler ABI info
      -- Detecting C compiler ABI info - done
      -- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx86/x64/cl.exe - skipped
      -- Detecting C compile features
      -- Detecting C compile features - done
      -- Detecting CXX compiler ABI info
      -- Detecting CXX compiler ABI info - done
      -- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.34.31933/bin/Hostx86/x64/cl.exe - skipped
      -- Detecting CXX compile features
      -- Detecting CXX compile features - done
      'C:/Users/huml-dkn/Desktop/imgui_bundle/venv/Scripts/python.exe' '-c' 'import pybind11; print(pybind11.get_cmake_dir())'
      -- Found PythonInterp: C:/Users/huml-dkn/Desktop/imgui_bundle/venv/Scripts/python.exe (found suitable version "3.10.7", minimum required is "3.6")
      -- Found PythonLibs: C:/Program Files/Python310/libs/python310.lib
      -- Performing Test HAS_MSVC_GL_LTCG
      -- Performing Test HAS_MSVC_GL_LTCG - Success
      -- Found pybind11: C:/Users/huml-dkn/AppData/Local/Temp/pip-build-env-veuax1i0/overlay/Lib/site-packages/pybind11/include (found version "2.10.1")
      -- Project version: 1.0.0
      CMake Error at C:/Users/huml-dkn/AppData/Local/Temp/pip-build-env-veuax1i0/overlay/Lib/site-packages/cmake/data/share/cmake-3.25/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
        Could NOT find LibArchive (missing: LibArchive_LIBRARY
        LibArchive_INCLUDE_DIR) (Required is at least version "3")
      Call Stack (most recent call first):
        C:/Users/huml-dkn/AppData/Local/Temp/pip-build-env-veuax1i0/overlay/Lib/site-packages/cmake/data/share/cmake-3.25/Modules/FindPackageHandleStandardArgs.cmake:600 (_FPHSA_FAILURE_MESSAGE)
        C:/Users/huml-dkn/AppData/Local/Temp/pip-build-env-veuax1i0/overlay/Lib/site-packages/cmake/data/share/cmake-3.25/Modules/FindLibArchive.cmake:67 (find_package_handle_standard_args)
        external/srcML/src/client/CMakeLists.txt:9 (find_package)


      -- Configuring incomplete, errors occurred!
      See also "C:/Users/huml-dkn/AppData/Local/Temp/pip-install-mycottj2/srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7/_skbuild/win-amd64-3.10/cmake-build/CMakeFiles/CMakeOutput.log".
      Traceback (most recent call last):
        File "C:\Users\huml-dkn\AppData\Local\Temp\pip-build-env-veuax1i0\overlay\Lib\site-packages\skbuild\setuptools_wrap.py", line 632, in setup
          env = cmkr.configure(
        File "C:\Users\huml-dkn\AppData\Local\Temp\pip-build-env-veuax1i0\overlay\Lib\site-packages\skbuild\cmaker.py", line 334, in configure
          raise SKBuildError(

      An error occurred while configuring with CMake.
        Command:
          cmake 'C:\Users\huml-dkn\AppData\Local\Temp\pip-install-mycottj2\srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7' -G Ninja '-DCMAKE_INSTALL_PREFIX:PATH=C:\Users\huml-dkn\AppData\Local\Temp\pip-install-mycottj2\srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7\_skbuild\win-amd64-3.10\cmake-install\bindings\srcml_caller' -DPYTHON_VERSION_STRING:STRING=3.10.7 -DSKBUILD:INTERNAL=TRUE '-DCMAKE_MODULE_PATH:PATH=C:\Users\huml-dkn\AppData\Local\Temp\pip-build-env-veuax1i0\overlay\Lib\site-packages\skbuild\resources\cmake' '-DPYTHON_EXECUTABLE:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv\Scripts\python.exe' '-DPYTHON_INCLUDE_DIR:PATH=C:\Program Files\Python310\Include' '-DPYTHON_LIBRARY:PATH=C:\Program Files\Python310\libs\python310.lib' '-DPython_EXECUTABLE:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv\Scripts\python.exe' '-DPython_ROOT_DIR:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv' '-DPython_INCLUDE_DIR:PATH=C:\Program Files\Python310\Include' -DPython_FIND_REGISTRY:STRING=NEVER '-DPython3_EXECUTABLE:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv\Scripts\python.exe' '-DPython3_ROOT_DIR:PATH=C:\Users\huml-dkn\Desktop\imgui_bundle\venv' '-DPython3_INCLUDE_DIR:PATH=C:\Program Files\Python310\Include' -DPython3_FIND_REGISTRY:STRING=NEVER -DCMAKE_BUILD_TYPE:STRING=Release
        Source directory:
          C:\Users\huml-dkn\AppData\Local\Temp\pip-install-mycottj2\srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7
        Working directory:
          C:\Users\huml-dkn\AppData\Local\Temp\pip-install-mycottj2\srcml-caller_6ce31932bc6f4d048523b8433c1e7bf7\_skbuild\win-amd64-3.10\cmake-build
      Please see CMake's output for more information.
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for srcml_caller
Successfully built srcmlcpp
Failed to build srcml_caller
ERROR: Could not build wheels for srcml_caller, which is required to install pyproject.toml-based projects

I see libarchive can be downloaded here, but where do i place it for CMake to find it?

Originally posted by @dcnieho in #51 (comment)

imgui: cleanup binding SelectableFlagsPrivate_

@dcnieho:

More generally, I see that in litgen setup for imgui, you added
the RegexReplacementList. Do you think that can also be used to clean up
that dirty enum hack of imgui for some of the extensions in internal (like
what is now
imgui.internal.SelectableFlagsPrivate_.im_gui_selectable_flags_select_on_click
and
should more ideally be
imgui.internal.SelectableFlagsPrivate_.select_on_click)?

macos deployment target

Despite your pyproject.toml listing 11.0 as a deployment target, the arm64 (but not the x86_64) wheels for 3.10 and 3.9 seem to have 13.0 as deployment target, see here: https://pypi.org/project/imgui-bundle/#files. That is causing trouble for some of my users who are on an M1, its a pretty high requirement to reach. Is it possible to correct that?

Can't set imgui.get_io().ini_filename

Q: This should work, but doesn’t: ... imgui.get_io().ini_filename = ...

Well, an automatic generation for `const char * ImGuiIO::IniFilename" is hopeless, since it provides absolutely no storage.
I had to do a manual patch: 6ef5df6

A: use imgui.set_io_ini_filename() instead

don't pip install opencv-python if opencv-contrib-python is installed

I just pip-installed the current dev branch into the venv for my app. My app uses functionality from the opencv-contrib-python. After installing imgui_bundle, i now have both opencv-contrib-python and opencv-python installed, and trying to use functionality from the contrib version does not work.

In short, opencv-python should not be installed if opencv-contrib-python is already installed. I am not sure if there is a way to specify that with the dependency infrastructure (e.g. somehow to say that either of these is fine), but right now this messes with a perfectly fine set of pip packages ;)

ImGuiTableSortSpecs isn't wrapped correctly

I think ImGuiTableSortSpecs isn't wrapped correctly. specs should be a list of TableColumnSortSpecs, not a single one. My pyimgui code has for sort_spec in sort_specs_in.specs, where sort_specs_in is a ImGuiTableSortSpecs gotten from imgui.table_get_sort_specs().

imgui.internal.splitter_behavior incorrect

\imgui_bundle\imgui\internal.pyi line 6023. The C++ signature is:

IMGUI_API bool          SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0);

But the python signature

def splitter_behavior(
    bb: ImRect,
    id_: ID,
    axis: Axis,
    size1: np.ndarray,
    size2: np.ndarray,
    min_size2: float,
    hover_extend: float = 0.0,
    hover_visibility_delay: float = 0.0,
    bg_col: ImU32 = 0,
) -> bool

is missing min_size1.

Also, the call to `` is wrong:

ImGui::SplitterBehavior(bb, id, axis, static_cast<float *>(size1_from_pyarray), static_cast<float *>(size2_from_pyarray), static_cast<float>(size2_count), min_size2, hover_extend, hover_visibility_delay, bg_col);

there shouldn't be static_cast<float>(size2_count)

The more appropriate signature here may be:

def splitter_behavior(
    bb: ImRect,
    id_: ID,
    axis: Axis,
    size1: float,
    size2: float,
    min_size1: float,
    min_size2: float,
    hover_extend: float = 0.0,
    hover_visibility_delay: float = 0.0,
    bg_col: ImU32 = 0,
) -> bool, float, float

using _adapt_modifiable_immutable_to_return to return the modified values of size1 and size2

[Question] Non-OpenGL backend support (Metal)

Hi! Sorry if I'm missing something obvious, but do I understand correctly that like hello-imgui on which it is based, imgui-bundle supports only OpenGL as a backend? My use case is an app that uses Metal for rendering, and I'm curious if it's possible to use imgui-bundle for overlay UI, or I should stick with the "raw" ImGui? Thanks!

use sdl and the effect of dpi is very strange

  1. I set HELLOIMGUI_USE_SDL_OPENGL3 and IMGUI_BUNDLE_WITH_SDL ON
  2. runnerParams.backendType = HelloImGui::BackendType::Sdl;
  3. my easy code
#include "include.h"

using namespace std;

void Gui()
{
    ImGui::Text("Test text");
}

int main()
{
    HelloImGui::SimpleRunnerParams runnnerParams;
    runnnerParams.guiFunction = Gui;
    runnnerParams.windowSize = { 400, 300 };

    HelloImGui::RunnerParams runnerParams = runnnerParams.ToRunnerParams();
    runnerParams.imGuiWindowParams.configWindowsMoveFromTitleBarOnly = true;
    runnerParams.backendType = HelloImGui::BackendType::Sdl;

    ImmApp::AddOnsParams addOnsParams;
    //addOnsParams.withMarkdown = true;
    //addOnsParams.withImplot = true;
    ImmApp::Run(runnerParams, addOnsParams);
    return 0;
}
  1. Scaling to 100% is normal
    image

  2. Scaling to 125% is abnormal. I test img button in this situation, the mouse cannot normally click on the displayed button, there seems to be a little deviation. But I can't take a good screenshot of this situation because my screenshot software can't see the mouse pointer
    image
    image

6.When I switch backendType to Glfw, any scaling is normal.So I don't know if I made a mistake in using sdl. I sincerely hope I can get your help, and I'm sorry for my English level

ImGuiStyle::Colors (and other arrays) not accessible from python bindings

In the app i'm porting, I manually modify the default style by modifying the values in the ´ImGuiStyle::Colors (imgui.get_style().colors` in pyimgui). This doesn't seem to exist in your port. How do i set specific colors/modify my current style?

Same for KeysDown (should be imgui.get_io().keys_down) and a bunch of other arrays in the ImGuiIO struct. As these have known size, i guess there is a bigger chance they may be wrappable?

Please share your feedback

"ImGui Bundle" is a brand new python and C++ package (Oct 2022). It works on MacOS, Windows and Linux.

Feedback from users would be appreciated in order to make sure its installation works on different platforms.

If you installed it, and if you have time, could you please answer the following:

Python users

  • How did you install imgui-bundle for python: via git clone + pip install -v . or via pip install imgui-bundle (i.e install from pypi)
  • If you installed from pypi, which platform are you using ? Did the build use the prebuilt wheel, or did it have to build from source?
  • Does imgui_bundle_demo work ?
  • Do you have code completion for imgui and implot in your IDE (and which IDE are you using)? If not, please read the FAQ

C++ Users

  • Did the code compile painlessly, and do the demo work?

Thanks in advance!

At exit customization

Hello Imgui offers options related to an application exit:

  • The App menu contains a default Quit option
  • the runner params have an appShallExit setting.
  • there is a BeforeExit callback (when application is already shutting down and you can't change your mind).

I am wondering whether there is a way to combine the existing settings or some others I have overlooked to inject some logic that would display a confirmation dialog to the user (e.g. "Are you really sure you want to close the app and lose your unsaved changes?") ?

map imgui_internal.h, integrations

Hi! I just came across this project and really like where it is heading. I'm currently using pyimgui and its not fully feature complete (complete enough that its only a minor inconvenience) and more importantly many releases behind. Since its all manual effort, this trouble likely remains.

Your effort here looks like a really good idea. Direct port (so no own flavor) of the great imgui, yet in a way that you can somewhat easily stay up to date with releases and various branches (docking!). Nice. And keeping all the docs intact, very nice.

I had some thoughts, perhaps some are just wrong because i'm not an expert on all of this.

Maybe i have missed it when looking around, but could you map imgui_internal.h as well (perhaps i missed it when having a look)? This is important for users of imgui who want to customize a little further, from something as simple as setting an item as disabled, imgui.internal.push_item_flag(imgui.internal.ITEM_DISABLED, True), to building your whole own widgets. I like the idea of exposing that through a submodule like pyimgui does (see above example).

Second, since i already have a working program using pyimgui (not yet on github), i'm at this stage firstly interested in getting the imgui bit of this effort. I think that means i would need the integrations bit (e.g. the glfw+opengl backend, though others may want to use one of the other backends). Is that something you envision providing? I guess they can't simply be autogenerated since one would have to mix and match a bit (like a C++ consumer of dear imgui would include imgui_impl_glfw.cpp/.h and imgui_impl_opengl3.cpp/.h in their program)? Or should this instead be converted to pure Python somehow? On a related note, i would also be cool to convert the demo window to pure python, so the whole autogenerated lib can be tested there, visually.

Lastly, for the future, this nicely makes it relatively easy to wrap other libraries like you show with implot. It might be worth thinking about how that can be scalably provided, if you are interested. That people could add imnodes, one of the various file dialogues, etc. Perhaps through syntax like pip install lg_imgui_bundle[imgui,implot,imnodes,xxx], selecting which they want. This repo can then provide the various submodules, kinda like vcpkg does. I'm viewing this from a high level, i'm not familiar with the intricacies of how to achieve that. Worth thinking about though, i think.

Thanks for the great and interesting work!

default value fail for functions taking ImColor

I found something that turns out to be a big problem: Functions taking an ImColor argument but specifying a ImU32 default value.
An example is this from imspinner:

def spinner_ang_triple(
    label: str,
    radius1: float,
    radius2: float,
    radius3: float,
    thickness: float,
    c1: ImColor = 0xffffffff,
    c2: ImColor = 0xffffff80,
    c3: ImColor = 0xffffffff,
    speed: float = 2.8,
    angle: float = IM_PI
    ) -> None:
    pass

0xffffffff is not an ImColor, and thus you get an incompatible function arguments error when not specifying that input argument yourself (or when specifying it using, for example, imgui.color_convert_float4_to_u32() as i did). On the C++ side this is not a problem as there is an ImColor constructor taking ImU32 and this is also wrapped by imgui_bundle. However, Python/PyBind would not do this conversion for you automatically and instead you get that incompatible function arguments error.

So, I thought, ah, ImColor also needs a bunch of special helper functions like ImVec2 and ImVec4. It does, I didn't have ImColor on my radar when i made the other ones. Among those would be a line py::implicitly_convertible<ImU32, ImColor>(); telling pybind that there is a conversion here, solving the issues we're seeing. However, when i add that, I get an error when importing the module: ImportError: implicitly_convertible: Unable to find type unsigned int. Googling that, i find this, and more importantly this associated PR. So, PyBind doesn't support implicit conversion for native types, only for user-defined types. As you see, that PR was not accepted in the end. That means there is a problem here that can only be solved by fixing the signatures of all functions that expect an ImColor and specify a default color as an int. I.e., this in the cpp does work:

m.def("spinner_ang_triple",
        ImSpinner::SpinnerAngTriple, py::arg("label"), py::arg("radius1"), py::arg("radius2"), py::arg("radius3"), py::arg("thickness"), py::arg("c1") = ImColor(0xffffffff), py::arg("c2") = ImColor(0xffffff80), py::arg("c3") = ImColor(0xffffffff), py::arg("speed") = 2.8f, py::arg("angle") = IM_PI);

Note that i added ImColor() around the default arguments. The corresponding stub would be:

def spinner_ang_triple(
    label: str,
    radius1: float,
    radius2: float,
    radius3: float,
    thickness: float,
    c1: ImColor = ImColor(0xffffffff),
    c2: ImColor = ImColor(0xffffff80),
    c3: ImColor = ImColor(0xffffffff),
    speed: float = 2.8,
    angle: float = IM_PI
    ) -> None:
    pass

This is a hairy one to fix... Best would almost be to re-examine adding support for this to pybind. Even if the default arguments are fixed with something like the above (hopefully some kind of opt in mechanism of the generator) that still leaves the problem that users unexpectedly cannot use ImU32's for colors, something you might do when porting from C++ or from pyimgui where this did work.

imgui.internal.dock_builder_split_node not wrapped correctly?

It appears ImGui::DockBuilderSplitNode() is not wrapped correctly. Its last two parameters are output pointers, yet the python wrapper has them as normal IDs. I think the Python signature should instead be:

# IMGUI_API ImGuiID       DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir);     /* original C++ signature */
def dock_builder_split_node(
    node_id: ID,
    split_dir: Dir,
    size_ratio_for_node_at_dir: float,
) -> Tuple[ID,ID,ID]:
    """Create 2 child nodes in this parent node."""
    pass

Pyglet support

Hi,
Do you have any advice as to how I can implement pyglet support for imgui_bundle?

Is it possible to dynamically add dockable windows.

I want to dynamically add imgui windows in python. I would expect something like.

# my_dockable_window in MainDockSpace
hello_imgui.get_runner_params().docking_params.dockable_windows.append(my_dockable_window)

Would create a window and show it docked in Maindockspace.

Is this possible. If it is how would I do this and should there be an demo.

PS. First ever github issue or issue in fact.

imgui.save_ini_settings_to_memory() default argument problem

ini = imgui.save_ini_settings_to_memory() fails even though the one input argument is optional:

TypeError: save_ini_settings_to_memory(): incompatible function arguments. The following argument types are supported:
    1. (out_ini_size: int = None) -> str

Invoked with:

calling it with e.g. 1000 as argument works fine.

Will ImPlotPlot struct fields be added to the python bindings in the future?

Hi,
first of all thanks for such a great work on porting (and packaging) Imgui and co. to python. I was playing with Implot in python and wanted to access the current plot data between BeginPlot and EndPlot. That's when I've noticed that there are no python bindings to access the fields of the Plot structure but only bindings for the struct methods

struct ImPlotPlot
{
    ImGuiID              ID;
    ImPlotFlags          Flags;
    ImPlotFlags          PreviousFlags;
    ImPlotLocation       MouseTextLocation;
    ImPlotMouseTextFlags MouseTextFlags;
    ImPlotAxis           Axes[ImAxis_COUNT];
    ImGuiTextBuffer      TextBuffer;
    ImPlotItemGroup      Items;
    ImAxis               CurrentX;
    ImAxis               CurrentY;
    ImRect               FrameRect;
    ImRect               CanvasRect;
    ImRect               PlotRect;
    ImRect               AxesRect;
    ImRect               SelectRect;
    ImVec2               SelectStart;
    int                  TitleOffset;
    bool                 JustCreated;
    bool                 Initialized;
    bool                 SetupLocked;
    bool                 FitThisFrame;
    bool                 Hovered;
    bool                 Held;
    bool                 Selecting;
    bool                 Selected;
    bool                 ContextLocked;

Are you planning on adding bindings for the fields in the future? Is there something that's preventing this from happening? Should I give it a try myself and submit a PR?

The motivation would be that I want to implement a different plot UpdateInput behaviour for my plot and for that I need to access the plot inner components

make ImVec2 and ImVec4 transparent / constructible from list, tuple, and perhaps numpy.array

What would make the library a bit nicer to use would be some implicit conversions. For instance, i had imgui.get_style().item_spacing = (5,5) which worked fine with pyimgui, but now i need to assign a imgui.ImVec2(5,5) instead. It would be nice if such vectors would be transparently handled so users don't have to sprinkle ImVec2s all over the code. I guess i'll run into a similar thing soon with colors. Right now my pyimgui code is imgui.get_style().colors[imgui.Col_.modal_window_dim_bg] = (0, 0, 0, 0.5), which i suppose I'll need to change to imgui.get_style().colors[imgui.Col_.modal_window_dim_bg] = imgui.ImVec4(0, 0, 0, 0.5).

It would be nice if such setters and other functions that take an ImVec2 or ImVec4 would also take tuples, lists and perhaps even numpy.arrays.

Somewhat relatedly, it would be great if you could make imgui.ImVec2 and imgui.ImVec4 iterable so that statements such as *imgui.ImVec4(0.,0.,0.,0.), for x in imgui.ImVec2(0.,0.) and list comprehensions would be legal and possible.

imgui_bundle_demo observations

I have now run imgui_bundle_demo after a successful pip install. observations:

  1. on the imgui bundle screen the list of libraries under about/batteries included needs to be updated
  2. the text editor with code under immediate mode gui is too small on my hidpi screen. Same for the ones under basic code advices (which should be advice by the way), and the other nodes under that
  3. should the log buttons under advanced create items in some log? I don't see any log listing anywhere there. Oh wait, now i get it, those are not buttons to create some fake items, but for filtering the log. running the glfw callback demo creates some entries. Works fine, just didn't understand it.
  4. in demo apps, clicking imgui_example_glfw_opengl3 works, but opens a window that is not DPI scaled, everything is tiny. Not sure if you want to fix that. It also opens a popup with app code that i can't close again, only collapse
  5. demo_custom_fonts works nicely and is correctly scaled
  6. Docking demo button: demo works nicely, only one small thing is that the progress bar when launching the rocket seems to not be dpi scaled, this is what i get:
    image
    also, not all GUI elements work in some of the color schemes (try light) but that might not be something you can do anythign about.
  7. implot tab: all works well, but like the haiku, all the plots are not tall enough. Screenshot:
    image
  8. Markdown spacing isn't great (hidpi problem?), see e.g.:
    image
    and also the spacing after the full stop between sentences in the implot screenshot above (seems not enough space after full stop)
  9. editor demo: link not parsed correctly?
    image
    also, clicking ´test glfw link´ takes the window almost offscreen (top left), can hardly access the Windows window bar to drag it back. maybe set window pos 100,100 instead of 10,10?
  10. additional widgets: all works, but imfiledialog widgets open way too small, guess another hidpi thing. Spinners are possibly a bit small:
    image
  11. something while running the imgui_bundle_demo also put ImGuiMd::OnOpenLink_Default url "images/world.jpg" should start with http! on the command line. Perhaps you know what triggered that.

demo request: storing multiple docker/viewport layouts in imgui.ini

The issue of how to store and retrieve multiple layouts has been discussed a bit, but I think a good example would be extremely valuable, and would promote use of docker/viewports much more broadly, and extend your docking example. This currently has "restore default layout" - which is obviously already a good start on this.

This would mean not only would the positions of all the windows be saved, but their current state (visible or not) on close or at a specific saved/restore point.

This has been discussed a few times, but otherwise not comprehensively managed
ocornut/imgui#4033
ocornut/imgui#2564
pthom/hello_imgui#52

Conan package source

Hi!
It would be very interesting to add this repo to the conan package center.
Is there any plan to add it?
Best regards!

porting my app to imgui_bundle.imgui

This is using pip-installed v0.6.5

I decided to port a project from pyimgui to imgui_bundle as I would like to post it to pypi and I can’t do that with direct dependencies (like pyimgui version 2.0).

So, some observations from porting this program (not yet available online).

First it tried to simple replace
import imgui with from pyimgui to imgui_bundle.imgui. Obviously a bunch of code adjustments are needed, mostly for the different flags (e.g. imgui.TABLE_SCROLL_X -> imgui.ImGuiTableFlags_.scroll_x). These all get a bit long, even if they are very tight to the original imgui header. That said, this naming is not ideal I think and we have some freedom here since in C++ you’d simply directly use ImGuiTableFlags_ScrollX. imgui.TableFlags.scroll_x would be nice, or imgui.table_flags.scroll_x to be more pythonic and have consistent change from camel to snake case. So that’s no double imgui, and no trailing underscore.

Relatedly, autocomplete works very nicely, but is a little confusing as you have each enum twice (e.g. imgui.ImGuiTableFlags and imgui.ImGuiTableFlags_. The former is just an int if I understand correctly. Shall we just have the latter (ideally renamed like above)?
My biggest problem: how do I use imgui by itself? Or is that not the idea? I have no equivalent of from imgui.integrations.glfw import GlfwRenderer in imgui_bundle. Is the idea that you must use the hello_imgui wrapper? That makes it much less of a drop-in replacement. And I think I cannot use that, because I use glfw callbacks for my app, but can’t access those, I think? I have set up:

glfw.set_char_callback(self.window, self.char_callback)
glfw.set_window_focus_callback(self.window, self.focus_callback)
glfw.set_window_pos_callback(self.window, self.pos_callback)
glfw.set_drop_callback(self.window, self.drop_callback)
glfw.swap_interval(globals.settings.vsync_ratio)

And other interaction with glfw (such as window scaling when moving between dpi monitors).
Can I set these up? How?

Does imgui_bundle also expose its opengl binding? Can I port import OpenGL.GL as gl to something in the imgui_bundle, or is that out of scope? I’m asking as my code for instance does:

max_tex_size = gl.glGetIntegerv(gl.GL_MAX_TEXTURE_SIZE)
imgui.io.fonts.texture_desired_width = max_tex_size

Some small observations:

  1. The flag imgui_internal.ImGuiSelectableFlagsPrivate_.im_gui_selectable_flags_select_on_click is a bit over the top, some conversion error?
  2. FLT_MIN (pyimgui: imgui.FLOAT_MIN) is unavailable. Is ok, can be replaced with np.finfo('single').tiny (untested), but seems a bit heavy to require numpy for just that. That pyimgui provides imgui.FLOAT_MIN is a good solution I think. Users may well get this wrong otherwise, since a python float is a double.
  3. This should work, but doesn’t:
    from imgui_bundle import imgui as imgui
    imgui.create_context()
    imgui.get_io().ini_filename = str(utils.get_data_path() / "imgui.ini")
    # raises: AttributeError: can't set attribute
    
  4. Are you planning to also bundle your immvision library? I’m not exactly sure on what it does, but the description is interesting!

haiku heart demo hidpi

the height of the plot window is too small, making it very horizontally stretched. Also, when making the window wider, the plot stretches horizontally with the window, but not vertically even though there is plenty of space, making it even more horizontally stretched out.

CI Wheel: is github throttling me down?

See failed builds after 1hour:

https://github.com/pthom/imgui_bundle/actions/runs/3663854181/jobs/6193892877#step:3:7659 (windows)

           file='D:/a/imgui_bundle/imgui_bundle/_skbuild/win-amd64-3.10/cmake-build/_deps/opencv_static_package_win-subbuild/opencv_static_package_win-populate-prefix/src/opencv-4.6.0_official_win_x64_world.zip'
    -- MD5 hash of
        D:/a/imgui_bundle/imgui_bundle/_skbuild/win-amd64-3.10/cmake-build/_deps/opencv_static_package_win-subbuild/opencv_static_package_win-populate-prefix/src/opencv-4.6.0_official_win_x64_world.zip
      does not match expected value
        expected: 'a9d92bdf8510d09c3bf3cb080731ea91'
          actual: 'c3a1056d10c9425209b22549263d995c'

And https://github.com/pthom/imgui_bundle/actions/runs/3663854181/jobs/6193892769#step:3:12774
(linux Building cp38-manylinux_i686 wheel)

This particular feel failed although the previous wheels cp38-manylinux_x86_64, cp39-manylinux_x86_64, cp310-manylinux_x86_64, cp311-manylinux_x86_64 wheel succeeded.
I had similar occurrences in previous builds.

   ============================= test session starts ==============================
  platform linux -- Python 3.8.15, pytest-7.2.0, pluggy-1.0.0
  rootdir: /project, configfile: pytest.ini
  collected 1 item
  
  Fatal Python error: Segmentation fault
  
  Current thread 0xf7ca8940 (most recent call first):
    File "<frozen importlib._bootstrap>", line 219 in _call_with_frames_removed
    File "<frozen importlib._bootstrap_external>", line 1166 in create_module
    File "<frozen importlib._bootstrap>", line 556 in module_from_spec
    File "<frozen importlib._bootstrap>", line 657 in _load_unlocked

Can't pip-install requirements-dev.txt

I wanted to just see what the generator does (and then possibly work on #38 (reply in thread)), but pip install -U -r requirements-dev.txt already fails. I know you're not expecting others to use this yet, but thought i may as well mention it here (though i guess technically what i paste below should be an issue in the litgen repo). This is what i got trying to pip-install:

(venv) C:\...\imgui_bundle>pip install -U -r requirements-dev.txt
Collecting litgen@ git+https://github.com/pthom/litgen
  Cloning https://github.com/pthom/litgen to c:\users\huml-dkn\appdata\local\temp\pip-install-sh6uf_z1\litgen_fdb59bc6d2154d2a9479d3c3384255e4
  Running command git clone --filter=blob:none --quiet https://github.com/pthom/litgen 'C:\Users\huml-dkn\AppData\Local\Temp\pip-install-sh6uf_z1\litgen_fdb59bc6d2154d2a9479d3c3384255e4'
  Resolved https://github.com/pthom/litgen to commit 432fb6b424bddf2c5dc838b58cb522226fb45dc0
  Running command git submodule update --init --recursive -q
  error: unable to create file hello_imgui_cmake/android/apkCMake/templates/sdl/gradle_template/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java: Filename too long
  error: unable to create file hello_imgui_cmake/android/apkCMake/templates/sdl/gradle_template/app/src/main/java/org/libsdl/app/HIDDeviceManager.java: Filename too long
  error: unable to create file hello_imgui_cmake/android/apkCMake/templates/sdl/gradle_template/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java: Filename too long
  error: unable to create file hello_imgui_cmake/android/apkCMake/templates/sdl/gradle_template/app/src/main/java/org/libsdl/app/SDLAudioManager.java: Filename too long
  error: unable to create file hello_imgui_cmake/android/apkCMake/templates/sdl/gradle_template/app/src/main/java/org/libsdl/app/SDLControllerManager.java: Filename too long
  From https://github.com/pthom/imgui
   * branch              a08931272922462f8bd4afb52011f8b5bc6a6ebe -> FETCH_HEAD
  Unable to checkout 'c59ba4cf2659648c584dd354ab646811b4cc9aa7' in submodule path 'demos/litgen/imgui_bundle/external/hello_imgui'
  Failed to recurse into submodule path 'demos/litgen/imgui_bundle'
  error: subprocess-exited-with-error

  × git submodule update --init --recursive -q did not run successfully.
  │ exit code: 1
  ╰─> See above for output.

Python usage with SDL

Hi!

I am currently using pyimgui with pySDL and moderngl. pyimgui is doing what I want but is a little bit limited. I would like to switch to imgui_bundle for its completness.

Is there an other way to initialize imgui than through immap? All example initialise the window through it.

Update: I saw an example on GLFW, IMGUI and OpenGL. Is there a backend for SDL2?

Thx for your time. :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.