DearPyGui is a Python GUI framework built on Dear ImGui (Immediate Mode Graphical User Interface). Unlike traditional retained-mode frameworks, DearPyGui rebuilds the UI every frame, enabling smooth real-time updates and GPU-accelerated rendering for high-performance applications.
Key Features
- GPU acceleration: Hardware-accelerated rendering via OpenGL
- Real-time performance: Handles thousands of data points at 60+ FPS
- Immediate mode: Simplified state management
- Built-in plotting: Fast line plots, scatter plots, heatmaps
- Cross-platform: Windows, macOS, Linux
- Pure Python: No compilation or external dependencies
When to Choose DearPyGui vs Streamlit
Use DearPyGui for:
- Real-time data streaming (>10 Hz updates)
- Hardware control interfaces
- Complex interactive visualizations
- Desktop applications requiring high performance
Use Streamlit for:
- Web-based dashboards
- Sharing with non-technical users
- Rapid prototyping
- Periodic data updates (<1 Hz)
Getting Started
import dearpygui.dearpygui as dpg
import numpy as np
dpg.create_context()
# Create window
with dpg.window(label="Neural Data Viewer", width=800, height=600):
dpg.add_text("Live spike rate monitor")
# Add plot
with dpg.plot(label="Firing Rate", height=300, width=-1):
dpg.add_plot_legend()
dpg.add_plot_axis(dpg.mvXAxis, label="Time (s)")
dpg.add_plot_axis(dpg.mvYAxis, label="Rate (Hz)", tag="y_axis")
# Create data series
dpg.add_line_series([], [], label="Neuron 1", parent="y_axis", tag="series1")
# Setup viewport
dpg.create_viewport(title="Neural Monitor", width=900, height=700)
dpg.setup_dearpygui()
dpg.show_viewport()
# Main loop
dpg.start_dearpygui()
dpg.destroy_context()
Real-Time Data Streaming
import dearpygui.dearpygui as dpg
import numpy as np
from collections import deque
import time
# Buffer for streaming data
BUFFER_SIZE = 1000
time_buffer = deque(maxlen=BUFFER_SIZE)
data_buffer = deque(maxlen=BUFFER_SIZE)
start_time = time.time()
def update_data():
"""Callback to update data stream."""
current_time = time.time() - start_time
# Simulate incoming neural data
new_value = np.random.randn() + 5 * np.sin(2 * np.pi * 0.5 * current_time)
time_buffer.append(current_time)
data_buffer.append(new_value)
# Update plot
dpg.set_value("series", [list(time_buffer), list(data_buffer)])
dpg.set_axis_limits("x_axis", current_time - 10, current_time)
dpg.create_context()
# Create window with controls
with dpg.window(label="Real-Time Neural Recording", width=1000, height=700, tag="main"):
# Controls
with dpg.group(horizontal=True):
dpg.add_button(label="Start", callback=lambda: dpg.set_value("recording", True))
dpg.add_button(label="Stop", callback=lambda: dpg.set_value("recording", False))
dpg.add_button(label="Clear", callback=lambda: [time_buffer.clear(), data_buffer.clear()])
dpg.add_checkbox(label="Recording", tag="recording", default_value=True)
dpg.add_slider_float(label="Threshold", default_value=0.0, min_value=-5.0, max_value=5.0, tag="threshold")
# Plot
with dpg.plot(label="Neural Signal", height=500, width=-1):
dpg.add_plot_legend()
dpg.add_plot_axis(dpg.mvXAxis, label="Time (s)", tag="x_axis")
with dpg.plot_axis(dpg.mvYAxis, label="Voltage (μV)", tag="y_axis"):
dpg.add_line_series([], [], label="Signal", tag="series")
dpg.add_hline_series([0], label="Threshold", tag="threshold_line")
# Statistics
with dpg.group(horizontal=True):
dpg.add_text("Mean: ", tag="mean_text")
dpg.add_text("Std: ", tag="std_text")
dpg.add_text("Peaks: ", tag="peaks_text")
def update_loop():
"""Main update loop."""
while dpg.is_dearpygui_running():
if dpg.get_value("recording"):
update_data()
# Update threshold line
threshold = dpg.get_value("threshold")
dpg.set_value("threshold_line", [[0], [threshold]])
# Update statistics
if len(data_buffer) > 0:
data_array = np.array(data_buffer)
mean_val = np.mean(data_array)
std_val = np.std(data_array)
peaks = np.sum(data_array > threshold)
dpg.set_value("mean_text", f"Mean: {mean_val:.2f}")
dpg.set_value("std_text", f"Std: {std_val:.2f}")
dpg.set_value("peaks_text", f"Peaks: {peaks}")
dpg.render_dearpygui_frame()
time.sleep(0.016) # ~60 FPS
dpg.create_viewport(title="Neural Monitor", width=1100, height=800)
dpg.setup_dearpygui()
dpg.show_viewport()
update_loop()
dpg.destroy_context()
Multi-Channel Visualization
import dearpygui.dearpygui as dpg
import numpy as np
N_CHANNELS = 8
SAMPLE_RATE = 30000
DISPLAY_DURATION = 1.0 # seconds
def create_multichannel_viewer():
dpg.create_context()
with dpg.window(label="Multi-Channel Recording", width=1200, height=800, tag="main"):
# Channel plots
for ch in range(N_CHANNELS):
with dpg.plot(label=f"Channel {ch}", height=80, width=-1):
dpg.add_plot_axis(dpg.mvXAxis, label="", no_tick_labels=True, tag=f"x_axis_{ch}")
with dpg.plot_axis(dpg.mvYAxis, label="", tag=f"y_axis_{ch}"):
dpg.add_line_series([], [], tag=f"series_{ch}")
dpg.set_axis_limits(f"x_axis_{ch}", 0, DISPLAY_DURATION)
dpg.set_axis_limits(f"y_axis_{ch}", -100, 100)
dpg.create_viewport(title="Multi-Channel Viewer", width=1300, height=900)
dpg.setup_dearpygui()
dpg.show_viewport()
# Simulate data streaming
t = np.linspace(0, DISPLAY_DURATION, int(SAMPLE_RATE * DISPLAY_DURATION))
while dpg.is_dearpygui_running():
for ch in range(N_CHANNELS):
# Generate synthetic neural-like signal
signal = (np.random.randn(len(t)) * 10 +
30 * np.sin(2 * np.pi * (ch + 1) * 0.5 * t))
dpg.set_value(f"series_{ch}", [list(t), list(signal)])
dpg.render_dearpygui_frame()
time.sleep(0.1)
dpg.destroy_context()
create_multichannel_viewer()
Hardware Control Interface
import dearpygui.dearpygui as dpg
def create_control_panel():
dpg.create_context()
with dpg.window(label="Experiment Control", width=600, height=400):
dpg.add_text("Stimulation Parameters")
dpg.add_separator()
with dpg.group():
dpg.add_slider_float(label="Frequency (Hz)", default_value=10.0,
min_value=0.1, max_value=100.0, tag="freq")
dpg.add_slider_float(label="Amplitude (V)", default_value=5.0,
min_value=0.0, max_value=10.0, tag="amp")
dpg.add_slider_float(label="Duration (ms)", default_value=100.0,
min_value=1.0, max_value=1000.0, tag="dur")
dpg.add_separator()
with dpg.group(horizontal=True):
dpg.add_button(label="Start Stimulation", callback=start_stim, width=200)
dpg.add_button(label="Stop", callback=stop_stim, width=200)
dpg.add_separator()
dpg.add_text("Status: Idle", tag="status")
dpg.create_viewport(title="Control Panel", width=700, height=500)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()
def start_stim():
freq = dpg.get_value("freq")
amp = dpg.get_value("amp")
dur = dpg.get_value("dur")
dpg.set_value("status", f"Status: Running at {freq} Hz, {amp} V")
# Send to hardware...
def stop_stim():
dpg.set_value("status", "Status: Stopped")
# Stop hardware...
create_control_panel()
Performance Considerations
DearPyGui can handle:
- 1000+ data points per series at 60 FPS
- Multiple plots simultaneously
- Real-time updates with minimal latency
Optimization tips:
- Use
dequewithmaxlenfor rolling buffers - Update only visible data ranges
- Batch multiple series updates
- Use appropriate sleep times in render loop
When to Use DearPyGui
Best for:
- Closed-loop experimental control
- Real-time neural data monitoring
- Signal processing applications
- Hardware interfaces
- Desktop-only applications
Not ideal for:
- Web deployment
- Mobile applications
- Non-technical end users
- Simple static dashboards