Skip to content

Raw cimgui GUI

This example shows raw cimgui widgets controlling a Datoviz visual.

Preview

Raw cimgui GUI

Run And Adapt

Commands below assume a Datoviz source checkout and start at the repository root. Use your configured build environment; Python routes additionally require local bindings.

Route Availability Command or action
C Canonical native source just example-c features/gui_cimgui (build and run), or rerun ./build/examples/c/features/gui_cimgui
Python Available; direct-engine adaptation python3 -m examples.python.gallery.features.gui_cimgui
Browser Native only raw cimgui access requires the native ImGui app path

Use this example as capability or integration evidence, not as a minimal copy-paste template. Start from the nearest supported, copy-safe example and add this feature after verifying the linked API reference.

What To Look For

Four point positions and colors are uploaded once, while the GUI slider rewrites the diameter_px attribute for all points. The raw cimgui window also displays a small status table and can open the Dear ImGui demo. Move the diameter slider and compare the live marker sizes with the unchanged positions and colors; this demonstrates how advanced users can mix direct imgui calls with Datoviz data updates.

Source

#!/usr/bin/env python3
"""Raw cimgui widgets updating a retained point visual."""

from __future__ import annotations

import ctypes

import numpy as np

import datoviz as dvz
import datoviz._ctypes as raw

from examples.python.gallery import common as ex


POINT_COUNT = 4
IMGUI_TABLE_FLAGS_ROW_BG = 1 << 6
IMGUI_TABLE_FLAGS_BORDERS_INNER = (1 << 7) | (1 << 9)


class ImVec2(ctypes.Structure):
    _fields_ = [
        ("x", ctypes.c_float),
        ("y", ctypes.c_float),
    ]


class _RawImGui:
    def __init__(self) -> None:
        lib = raw.dvz
        self.begin = lib.igBegin
        self.begin.argtypes = [
            ctypes.c_char_p,
            ctypes.POINTER(ctypes.c_bool),
            ctypes.c_int,
        ]
        self.begin.restype = ctypes.c_bool

        self.end = lib.igEnd
        self.end.argtypes = []
        self.end.restype = None

        self.text_unformatted = lib.igTextUnformatted
        self.text_unformatted.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
        self.text_unformatted.restype = None

        self.get_version = lib.igGetVersion
        self.get_version.argtypes = []
        self.get_version.restype = ctypes.c_char_p

        self.begin_table = lib.igBeginTable
        self.begin_table.argtypes = [
            ctypes.c_char_p,
            ctypes.c_int,
            ctypes.c_int,
            ImVec2,
            ctypes.c_float,
        ]
        self.begin_table.restype = ctypes.c_bool

        self.end_table = lib.igEndTable
        self.end_table.argtypes = []
        self.end_table.restype = None

        self.table_next_row = lib.igTableNextRow
        self.table_next_row.argtypes = [ctypes.c_int, ctypes.c_float]
        self.table_next_row.restype = None

        self.table_set_column_index = lib.igTableSetColumnIndex
        self.table_set_column_index.argtypes = [ctypes.c_int]
        self.table_set_column_index.restype = ctypes.c_bool

        self.slider_float = lib.igSliderFloat
        self.slider_float.argtypes = [
            ctypes.c_char_p,
            ctypes.POINTER(ctypes.c_float),
            ctypes.c_float,
            ctypes.c_float,
            ctypes.c_char_p,
            ctypes.c_int,
        ]
        self.slider_float.restype = ctypes.c_bool

        self.checkbox = lib.igCheckbox
        self.checkbox.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_bool)]
        self.checkbox.restype = ctypes.c_bool

        self.show_demo_window = lib.igShowDemoWindow
        self.show_demo_window.argtypes = [ctypes.POINTER(ctypes.c_bool)]
        self.show_demo_window.restype = None


IMGUI = _RawImGui()


class GuiCimguiState:
    def __init__(self, point) -> None:
        self.point = point
        self.diameter_px = ctypes.c_float(44.0)
        self.show_demo = ctypes.c_bool(False)


def _positions() -> np.ndarray:
    return np.array(
        [
            [-0.60, -0.30, 0.0],
            [-0.20, +0.30, 0.0],
            [+0.20, -0.30, 0.0],
            [+0.60, +0.30, 0.0],
        ],
        dtype=np.float32,
    )


def _colors() -> np.ndarray:
    return ex.color_array(ex.CYAN, ex.GREEN, ex.YELLOW, ex.TEXT)


def _upload(state: GuiCimguiState) -> None:
    diameters = np.full(POINT_COUNT, state.diameter_px.value, dtype=np.float32)
    if dvz.dvz_visual_set_data(state.point, "diameter_px", diameters) != 0:
        raise RuntimeError("dvz_visual_set_data(diameter_px) failed")


def _build_scene():
    scene, figure, panel = ex.scene_panel()

    point = dvz.dvz_point(scene, 0)
    if not point:
        raise RuntimeError("dvz_point() failed")
    state = GuiCimguiState(point)
    if dvz.dvz_visual_set_data_many(
        point,
        {
            "position": _positions(),
            "color": _colors(),
        },
    ) != 0:
        raise RuntimeError("dvz_visual_set_data_many(point) failed")
    _upload(state)

    ex.set_filled_point_style(point)
    if dvz.dvz_visual_set_depth_test(point, False) != 0:
        raise RuntimeError("dvz_visual_set_depth_test() failed")
    ex.add_visual(panel, point)
    return scene, figure, panel, state


def _table_row(label: bytes, value: bytes) -> None:
    IMGUI.table_next_row(0, 0.0)
    IMGUI.table_set_column_index(0)
    IMGUI.text_unformatted(label, None)
    IMGUI.table_set_column_index(1)
    IMGUI.text_unformatted(value, None)


def _gui_callback_factory(state: GuiCimguiState):
    def gui_callback(_gui, _view, _user_data) -> None:
        open_window = ctypes.c_bool(True)
        if IMGUI.begin(b"Raw cimgui", ctypes.byref(open_window), 0):
            version = IMGUI.get_version() or b"unknown"
            IMGUI.text_unformatted(b"Dear ImGui " + version, None)
            table_flags = IMGUI_TABLE_FLAGS_ROW_BG | IMGUI_TABLE_FLAGS_BORDERS_INNER
            if IMGUI.begin_table(b"status", 2, table_flags, ImVec2(0.0, 0.0), 0.0):
                _table_row(b"binding", b"datoviz/imgui.h")
                _table_row(b"visual", b"retained point")
                IMGUI.end_table()
            if IMGUI.slider_float(
                b"Diameter", ctypes.byref(state.diameter_px), 8.0, 90.0, b"%.1f", 0
            ):
                _upload(state)
            IMGUI.checkbox(b"ImGui demo", ctypes.byref(state.show_demo))
        IMGUI.end()

        if state.show_demo.value:
            IMGUI.show_demo_window(ctypes.byref(state.show_demo))

    return gui_callback


def _configure_view(view, state: GuiCimguiState, *, gui: bool = True) -> None:
    if not gui:
        return
    overlay = dvz.dvz_view_gui(view, None)
    if not overlay:
        return
    if dvz.dvz_view_set_gui_callback(view, _gui_callback_factory(state), None) != 0:
        raise RuntimeError("dvz_view_set_gui_callback() failed")


def main() -> None:
    scene, figure, _panel, state = _build_scene()

    def configure(view) -> None:
        _configure_view(view, state)

    ex.run_with_view(scene, figure, "Raw cimgui GUI", configure)


if __name__ == "__main__":
    main()
/*
 * Copyright (c) 2021 Cyrille Rossant and contributors. All rights reserved.
 * Licensed under the MIT license. See LICENSE file in the project root for details.
 * SPDX-License-Identifier: MIT
 */

/* gui_cimgui - This example shows raw cimgui widgets controlling a Datoviz visual.
 *
 * Scenario: features_gui_cimgui
 * Style: features, native GUI/app
 *
 * Build:  just example-c features/gui_cimgui
 * Run:    ./build/examples/c/features/gui_cimgui
 *
 * What to look for: four point positions and colors are uploaded once, while the GUI slider
 * rewrites the diameter_px attribute for all points. The raw cimgui window also displays a small
 * status table and can open the Dear ImGui demo. Move the diameter slider and compare the live
 * marker sizes with the unchanged positions and colors; this demonstrates how advanced users can
 * mix direct imgui calls with retained Datoviz data updates.
 */



/*************************************************************************************************/
/*  Includes                                                                                     */
/*************************************************************************************************/

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

#include "datoviz/app.h"
#include "datoviz/gui.h"
#include "datoviz/imgui.h"
#include "datoviz/scene.h"
#include "example_common.h"
#include "example_style.h"



/*************************************************************************************************/
/*  Constants                                                                                    */
/*************************************************************************************************/

#define WIDTH  EXAMPLE_WINDOW_WIDTH
#define HEIGHT EXAMPLE_WINDOW_HEIGHT
#define POINT_COUNT 4u



/*************************************************************************************************/
/*  Structs                                                                                      */
/*************************************************************************************************/

typedef struct GuiCimguiState
{
    DvzVisual* point;
    float diameter_px;
    bool show_demo;
} GuiCimguiState;



/*************************************************************************************************/
/*  Helpers                                                                                      */
/*************************************************************************************************/

/**
 * Upload a uniform point diameter_px.
 *
 * @param state cimgui example state
 */
static void _gui_cimgui_upload(GuiCimguiState* state)
{
    if (state == NULL || state->point == NULL)
        return;

    float diameters[POINT_COUNT] = {
        state->diameter_px,
        state->diameter_px,
        state->diameter_px,
        state->diameter_px,
    };
    if (dvz_visual_set_data(state->point, "diameter_px", diameters, POINT_COUNT) != 0)
        dvz_fprintf(stderr, "gui_cimgui: failed to upload point diameters\n");
}



/**
 * Build the raw cimgui panel.
 *
 * @param gui Datoviz GUI overlay
 * @param view app view
 * @param user_data cimgui example state
 */
static void _gui_cimgui_callback(DvzGui* gui, DvzView* view, void* user_data)
{
    (void)gui;
    (void)view;
    GuiCimguiState* state = (GuiCimguiState*)user_data;
    if (state == NULL)
        return;

    bool open = true;
    if (igBegin("Raw cimgui", &open, 0))
    {
        igText("Dear ImGui %s", igGetVersion());
        if (igBeginTable(
                "status", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInner,
                (ImVec2){0.0f, 0.0f}, 0.0f))
        {
            igTableNextRow(0, 0.0f);
            (void)igTableSetColumnIndex(0);
            igText("binding");
            (void)igTableSetColumnIndex(1);
            igText("datoviz/imgui.h");
            igTableNextRow(0, 0.0f);
            (void)igTableSetColumnIndex(0);
            igText("visual");
            (void)igTableSetColumnIndex(1);
            igText("retained point");
            igEndTable();
        }
        if (igSliderFloat("Diameter", &state->diameter_px, 8.0f, 90.0f, "%.1f", 0))
            _gui_cimgui_upload(state);
        (void)igCheckbox("ImGui demo", &state->show_demo);
    }
    igEnd();

    if (state->show_demo)
        igShowDemoWindow(&state->show_demo);
}



/*************************************************************************************************/
/*  Main                                                                                         */
/*************************************************************************************************/

int main(int argc, char** argv)
{
    int ret = 1;
    DvzScene* scene = NULL;
    DvzApp* app = NULL;

    scene = dvz_scene();
    EXAMPLE_CHECK(scene != NULL, "dvz_scene() failed");

    DvzFigure* figure = dvz_figure(scene, WIDTH, HEIGHT, 0);
    DvzPanel* panel = figure != NULL ? dvz_panel_full(figure) : NULL;
    DvzVisual* point = dvz_point(scene, 0);
    EXAMPLE_CHECK(
        figure != NULL && panel != NULL && point != NULL, "failed to create scene objects");
    example_graphite_cyan_set_panel_background(panel);

    GuiCimguiState state = {
        .point = point,
        .diameter_px = 44.0f,
    };
    vec3 positions[POINT_COUNT] = {
        {-0.60f, -0.30f, 0.0f},
        {-0.20f, +0.30f, 0.0f},
        {+0.20f, -0.30f, 0.0f},
        {+0.60f, +0.30f, 0.0f},
    };
    DvzColor colors[POINT_COUNT] = {
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_SECONDARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_WARNING),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
    };
    DvzVisualDataUpdate updates[] = {
        {.attr_name = "position", .data = positions, .item_count = POINT_COUNT},
        {.attr_name = "color", .data = colors, .item_count = POINT_COUNT},
    };
    EXAMPLE_CHECK(dvz_visual_set_data_many(point, updates, 2) == 0, "failed to upload point data");
    _gui_cimgui_upload(&state);
    EXAMPLE_CHECK(dvz_panel_add_visual(panel, point, NULL) == 0, "dvz_panel_add_visual() failed");

    app = dvz_app(scene);
    EXAMPLE_CHECK(app != NULL, "dvz_app() failed (no GPU or display?)");

    DvzView* view = dvz_view_window(app, figure, WIDTH, HEIGHT, "gui_cimgui");
    EXAMPLE_CHECK(view != NULL, "dvz_view_window() failed (GLFW unavailable?)");

    DvzGui* gui = dvz_view_gui(view, NULL);
    EXAMPLE_CHECK(gui != NULL, "dvz_view_gui() failed");
    dvz_view_set_gui_callback(view, _gui_cimgui_callback, &state);

    if (example_png_capture_requested(argc, argv))
    {
        DvzAppCaptureConfig capture = {0};
        EXAMPLE_CHECK(
            example_png_capture_config("feature_gui_cimgui", &capture),
            "failed to configure PNG capture");
        EXAMPLE_CHECK(
            example_run_with_capture(
                app, view, example_frame_count_any_or_default(argc, argv, 4), &capture),
            "PNG capture failed");
    }
    else
    {
        dvz_app_run(app, example_frame_count(argc, argv));
    }
    ret = 0;

cleanup:
    if (app != NULL)
        dvz_app_destroy(app);
    if (scene != NULL)
        dvz_scene_destroy(scene);
    return ret;
}
Example details
  • ID: features_gui_cimgui
  • Category: feature
  • Lane: features
  • Status: supported
  • Source: examples/c/features/gui_cimgui.c
  • Approved adaptation starter: no
  • Python source: examples/python/gallery/features/gui_cimgui.py
  • Python adaptation: Available; direct-engine adaptation
  • Browser support: Native only
  • Browser note: raw cimgui access requires the native ImGui app path
  • Browser capability tags: gui
  • Validation: smoke+interaction+screenshot

Data

Field Value
kind synthetic