Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Terminal Emulator Widget

imgui_bundle.imgui_terminal (Python only) embeds a terminal emulator in your ImGui application: a real shell rendered as an ImGui widget, with colors, scrollback, text selection, and job control.

A zsh shell running inside an ImGui child window.

A zsh shell running inside an ImGui child window.

Install

pip install "imgui-bundle[terminal]"    # adds the pyte VT100 emulator

Quick start

from imgui_bundle import hello_imgui, imgui_ctx, immapp
from imgui_bundle.imgui_terminal import TerminalView, LocalShellTransport

mono_font = None
view = TerminalView()
LocalShellTransport().start(view)       # spawns $SHELL behind a pty (POSIX)

def load_fonts():
    global mono_font
    mono_font = hello_imgui.load_font("fonts/Inconsolata-Medium.ttf", 16.0)

def gui():
    with imgui_ctx.push_font(mono_font, 0.0):   # the widget uses the active font
        view.render_in_child("terminal")

params = hello_imgui.RunnerParams()
params.callbacks.load_additional_fonts = load_fonts
params.callbacks.show_gui = gui
params.fps_idling.enable_idling = False  # repaint while shell output streams in
immapp.run(params)

Full demo: demo_terminal_pyte.py, also available in the Widgets tab of the demo explorer.

Architecture: widget vs transport

The module separates the widget from the byte source:

keyboard -> ImGui -> transport -> shell/remote -> view.feed -> pyte -> ImGui draws it

Remote shell (SSH, websocket, robot companion computer...)

The same widget drives a remote shell: wire the callbacks to your channel and feed incoming bytes.

view = TerminalView()
view.on_input  = lambda data: channel.send(data)          # keystrokes -> remote
view.on_resize = lambda cols, rows: channel.resize(cols, rows)
# in a background reader:  for chunk in channel: view.feed(chunk)

Runnable remote demos (in demos_python/demos_terminal/):

This websocket path is also how a Pyodide / browser build would work: there is no local pty in the browser, but the widget itself is pure Python and drives a remote shell over a websocket.

Features

Limitations

pyte is a solid VT100 emulator, not a complete xterm. Known gaps: no alternate screen buffer (full-screen apps leave their frames in scrollback), no application cursor keys mode, no bracketed paste, no mouse reporting to programs, and double-width (CJK/emoji) glyphs may overlap. Glyphs missing from your font render as replacement boxes (ImGui does no OS font fallback); use a well-covered monospace font.

The local-shell transport is POSIX only; on Windows, adapt it with pywinpty (ConPTY) or use a remote transport.