Demo using assets & add-ons

demo assets addons
Figure 19. 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 "hello_imgui/icons_font_awesome_4.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()