Skip to content

GUI Viewport

This example shows a Datoviz render viewport embedded inside a GUI window.

Preview

GUI Viewport

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_viewport (build and run), or rerun ./build/examples/c/features/gui_viewport
Python Available; direct-engine adaptation python3 -m examples.python.gallery.features.gui_viewport
Browser Native only dockable GUI viewports require native ImGui and an offscreen native source view

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

The application hosts a GUI window while a separate Datoviz figure renders a small point scene at a fixed source size. The viewport presents that live render target inside the GUI instead of as the whole native window. Compare the embedded plot area with the surrounding controls; this layout is useful for tools where plots, parameter widgets, and diagnostics need to share the same application surface.

Source

#!/usr/bin/env python3
"""Native GUI window hosting an embedded Datoviz render viewport."""

from __future__ import annotations

import ctypes

import numpy as np

import datoviz as dvz

from examples.python.gallery import common as ex


HOST_WIDTH = ex.WIDTH
HOST_HEIGHT = ex.HEIGHT
SOURCE_WIDTH = 640
SOURCE_HEIGHT = 480
POINT_COUNT = 5


class GuiViewportState:
    def __init__(self, point) -> None:
        self.viewport = None
        self.point = point
        self.diameter_px = ctypes.c_float(34.0)
        self.show_points = ctypes.c_bool(True)
        self.show_demo = ctypes.c_bool(False)


def _positions() -> np.ndarray:
    return np.array(
        [
            [-0.68, -0.45, 0.0],
            [-0.28, +0.28, 0.0],
            [+0.00, -0.05, 0.0],
            [+0.34, +0.48, 0.0],
            [+0.72, -0.25, 0.0],
        ],
        dtype=np.float32,
    )


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


def _upload(state: GuiViewportState) -> None:
    diameter = state.diameter_px.value if state.show_points.value else 0.0
    diameters = np.full(POINT_COUNT, diameter, 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 = dvz.dvz_scene()
    if not scene:
        raise RuntimeError("dvz_scene() failed")

    source_figure = dvz.dvz_figure(scene, SOURCE_WIDTH, SOURCE_HEIGHT, 0)
    host_figure = dvz.dvz_figure(scene, HOST_WIDTH, HOST_HEIGHT, 0)
    if not source_figure or not host_figure:
        raise RuntimeError("dvz_figure() failed")

    source_panel = dvz.dvz_panel_full(source_figure)
    host_panel = dvz.dvz_panel_full(host_figure)
    if not source_panel or not host_panel:
        raise RuntimeError("dvz_panel_full() failed")
    dvz.dvz_panel_set_background_color(source_panel, ex.BG)
    dvz.dvz_panel_set_background_color(host_panel, dvz.DvzColor(11, 13, 16, 255))

    point = dvz.dvz_point(scene, 0)
    if not point:
        raise RuntimeError("dvz_point() failed")
    state = GuiViewportState(point)
    _upload(state)
    if dvz.dvz_visual_set_data_many(
        point,
        {
            "position": _positions(),
            "color": _colors(),
        },
    ) != 0:
        raise RuntimeError("dvz_visual_set_data_many(point) failed")
    ex.set_filled_point_style(point)
    ex.add_visual(source_panel, point)
    return scene, source_figure, host_figure, source_panel, state


def _gui_callback_factory(state: GuiViewportState):
    def gui_callback(gui, _view, _user_data) -> None:
        if state.viewport:
            dvz.dvz_gui_viewport_window(state.viewport, b"Datoviz viewport", None, 0)

        changed = False
        if dvz.dvz_gui_begin(gui, b"Viewport controls", None, 0):
            changed |= dvz.dvz_gui_slider_float(
                gui, b"Diameter", ctypes.byref(state.diameter_px), 4.0, 80.0
            )
            changed |= dvz.dvz_gui_checkbox(gui, b"Show points", ctypes.byref(state.show_points))
            dvz.dvz_gui_checkbox(gui, b"ImGui demo", ctypes.byref(state.show_demo))
        dvz.dvz_gui_end(gui)

        if state.show_demo.value:
            dvz.dvz_gui_demo(gui, ctypes.byref(state.show_demo))
        if changed:
            _upload(state)

    return gui_callback


def _configure_gui_viewport(view, scene, source_figure, source_panel, state: GuiViewportState) -> None:
    gui = dvz.dvz_view_gui(view, None)
    if not gui:
        raise RuntimeError("dvz_view_gui() failed")

    config = dvz.dvz_gui_viewport_config()
    config.viewport_flags = dvz.DVZ_GUI_VIEWPORT_FLAGS_FORWARD_INPUT
    viewport = dvz.dvz_gui_viewport(gui, source_figure, ctypes.byref(config))
    if not viewport:
        raise RuntimeError("dvz_gui_viewport() failed")
    state.viewport = viewport

    panzoom = dvz.dvz_panzoom(scene, None)
    if not panzoom:
        raise RuntimeError("dvz_panzoom() failed")
    if dvz.dvz_panel_bind_controller(source_panel, panzoom, dvz.DVZ_DIM_MASK_XY) != 0:
        raise RuntimeError("dvz_panel_bind_controller() failed")
    router = dvz.dvz_gui_viewport_input(viewport)
    if not router:
        raise RuntimeError("dvz_gui_viewport_input() failed")
    if dvz.dvz_panel_connect_input(source_panel, router) != 0:
        raise RuntimeError("dvz_panel_connect_input() failed")

    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, source_figure, host_figure, source_panel, state = _build_scene()
    app = dvz.dvz_app(scene)
    if not app:
        raise RuntimeError("dvz_app() failed")
    try:
        view = dvz.dvz_view_window(app, host_figure, HOST_WIDTH, HOST_HEIGHT, b"GUI Viewport")
        if not view:
            raise RuntimeError("dvz_view_window() failed")
        _configure_gui_viewport(view, scene, source_figure, source_panel, state)
        ex.run_app(app, view)
    finally:
        dvz.dvz_app_destroy(app)
        dvz.dvz_scene_destroy(scene)


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_viewport - This example shows a Datoviz render viewport embedded inside a GUI window.
 *
 * Scenario: features_gui_viewport
 * Style: features, native GUI/app
 *
 * Build:  just example-c features/gui_viewport
 * Run:    ./build/examples/c/features/gui_viewport
 *
 * What to look for: the application hosts a GUI window while a separate Datoviz figure renders a
 * small point scene at a fixed source size. The viewport presents that live render target inside
 * the GUI instead of as the whole native window. Compare the embedded plot area with the surrounding
 * controls; this layout is useful for tools where plots, parameter widgets, and diagnostics need to
 * share the same application surface.
 */



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

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

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



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

#define HOST_WIDTH    EXAMPLE_WINDOW_WIDTH
#define HOST_HEIGHT   EXAMPLE_WINDOW_HEIGHT
#define SOURCE_WIDTH  640u
#define SOURCE_HEIGHT 480u
#define POINT_COUNT   5u



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

typedef struct GuiViewportState
{
    DvzGuiViewport* viewport;
    DvzVisual* point;
    float diameter_px;
    bool show_points;
    bool show_demo;
} GuiViewportState;



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

/**
 * Upload the point diameters controlled by the host GUI.
 *
 * @param state GUI viewport state
 * @return true on success
 */
static bool _gui_viewport_upload(GuiViewportState* state)
{
    if (state == NULL || state->point == NULL)
        return false;

    const float diameter_px = state->show_points ? state->diameter_px : 0.0f;
    float diameters[POINT_COUNT] = {diameter_px, diameter_px, diameter_px, diameter_px, diameter_px};
    return dvz_visual_set_data(state->point, "diameter_px", diameters, POINT_COUNT) == 0;
}



/**
 * Build the dockable GUI viewport and its controls.
 *
 * @param gui GUI overlay
 * @param view host GLFW view
 * @param user_data GUI viewport state
 */
static void _gui_viewport_callback(DvzGui* gui, DvzView* view, void* user_data)
{
    (void)view;
    GuiViewportState* state = (GuiViewportState*)user_data;
    if (state == NULL || state->viewport == NULL)
        return;

    (void)dvz_gui_viewport_window(state->viewport, "Datoviz viewport", NULL, 0);

    bool changed = false;
    if (dvz_gui_begin(gui, "Viewport controls", NULL, 0))
    {
        changed |= dvz_gui_slider_float(gui, "Diameter", &state->diameter_px, 4.0f, 80.0f);
        changed |= dvz_gui_checkbox(gui, "Show points", &state->show_points);
        (void)dvz_gui_checkbox(gui, "ImGui demo", &state->show_demo);
    }
    dvz_gui_end(gui);

    if (state->show_demo)
        dvz_gui_demo(gui, &state->show_demo);
    if (changed && !_gui_viewport_upload(state))
        dvz_fprintf(stderr, "gui_viewport: failed to upload point sizes\n");
}



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

int main(int argc, char** argv)
{
    int ret = 1;
    DvzScene* scene = NULL;
    DvzApp* app = NULL;
    DvzView* host_view = NULL;
    DvzPanel* source_panel = NULL;
    DvzGuiViewport* viewport = NULL;
    bool host_alive = false;

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

    DvzFigure* source_figure = dvz_figure(scene, SOURCE_WIDTH, SOURCE_HEIGHT, 0);
    DvzFigure* host_figure = dvz_figure(scene, HOST_WIDTH, HOST_HEIGHT, 0);
    EXAMPLE_CHECK(source_figure != NULL && host_figure != NULL, "dvz_figure() failed");

    source_panel = dvz_panel_full(source_figure);
    DvzPanel* host_panel = dvz_panel_full(host_figure);
    EXAMPLE_CHECK(source_panel != NULL && host_panel != NULL, "dvz_panel_full() failed");
    example_graphite_cyan_set_panel_background(source_panel);
    dvz_panel_set_background_color(host_panel, dvz_color_from_unit(0.045f, 0.050f, 0.064f, 1.0f));

    DvzVisual* point = dvz_point(scene, 0);
    EXAMPLE_CHECK(point != NULL, "dvz_point() failed");
    GuiViewportState state = {
        .point = point,
        .diameter_px = 34.0f,
        .show_points = true,
    };
    vec3 positions[POINT_COUNT] = {
        {-0.68f, -0.45f, 0.0f}, {-0.28f, +0.28f, 0.0f}, {+0.00f, -0.05f, 0.0f},
        {+0.34f, +0.48f, 0.0f}, {+0.72f, -0.25f, 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),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_MINOR_TICK),
    };
    EXAMPLE_CHECK(_gui_viewport_upload(&state), "failed to upload point sizes");
    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");
    EXAMPLE_CHECK(
        dvz_panel_add_visual(source_panel, point, NULL) == 0, "dvz_panel_add_visual() failed");

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

    host_view = dvz_view_window(app, host_figure, HOST_WIDTH, HOST_HEIGHT, "gui_viewport");
    EXAMPLE_CHECK(host_view != NULL, "dvz_view_window() failed (GLFW unavailable?)");

    DvzGui* gui = dvz_view_gui(host_view, NULL);
    EXAMPLE_CHECK(gui != NULL, "dvz_view_gui() failed");

    DvzGuiViewportConfig viewport_config = dvz_gui_viewport_config();
    viewport_config.viewport_flags = DVZ_GUI_VIEWPORT_FLAGS_FORWARD_INPUT;
    viewport = dvz_gui_viewport(gui, source_figure, &viewport_config);
    EXAMPLE_CHECK(viewport != NULL, "dvz_gui_viewport() failed");
    state.viewport = viewport;

    DvzController* panzoom = dvz_panzoom(scene, NULL);
    EXAMPLE_CHECK(panzoom != NULL, "dvz_panzoom() failed");
    EXAMPLE_CHECK(
        dvz_panel_bind_controller(source_panel, panzoom, DVZ_DIM_MASK_XY) == 0,
        "dvz_panel_bind_controller() failed");
    EXAMPLE_CHECK(
        dvz_panel_connect_input(source_panel, dvz_gui_viewport_input(viewport)) == 0,
        "dvz_panel_connect_input() failed");

    dvz_view_set_gui_callback(host_view, _gui_viewport_callback, &state);
    if (example_png_capture_requested(argc, argv))
    {
        DvzAppCaptureConfig capture = {0};
        EXAMPLE_CHECK(
            example_png_capture_config("feature_gui_viewport", &capture),
            "failed to configure PNG capture");
        EXAMPLE_CHECK(
            example_run_with_capture(
                app, host_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:
    host_alive = host_view != NULL && dvz_view_canvas(host_view) != NULL;
    if (host_alive)
        (void)dvz_view_set_gui_callback(host_view, NULL, NULL);
    if (source_panel != NULL && viewport != NULL && host_alive)
        (void)dvz_panel_connect_input(source_panel, NULL);
    if (viewport != NULL && host_alive)
        dvz_gui_viewport_destroy(viewport);
    if (app != NULL)
        dvz_app_destroy(app);
    if (scene != NULL)
        dvz_scene_destroy(scene);
    return ret;
}
Example details
  • ID: features_gui_viewport
  • Category: feature
  • Lane: features
  • Status: supported
  • Source: examples/c/features/gui_viewport.c
  • Approved adaptation starter: no
  • Python source: examples/python/gallery/features/gui_viewport.py
  • Python adaptation: Available; direct-engine adaptation
  • Browser support: Native only
  • Browser note: dockable GUI viewports require native ImGui and an offscreen native source view
  • Browser capability tags: gui, native-view
  • Validation: smoke+interaction+screenshot

Data

Field Value
kind synthetic