Adding Images in DearPyGui

Learn every way to load, display, and manipulate images in your DearPyGui applications
Heads up! You've already completed this tutorial.

DearPyGui handles images a little differently than most Python GUI frameworks. Instead of simply pointing to a file and placing it on screen, you first load image data into a texture, and then you display that texture using a widget. This two-step process can be a bit confusing at first, but it gives you a lot of flexibility — including the ability to update images on the fly.

In this tutorial, we'll walk through every way to add images to a DearPyGui application, starting with the simplest approach and building up to more advanced techniques.

Understanding the Texture Registry

Before we display any image, we need to understand a core concept: the texture registry. In DearPyGui, all image data lives inside a texture registry. Think of it as a central storage area where your app keeps all its image data. Widgets then reference textures from this registry by their tag.

Here's the basic flow:

  1. Load image data (from a file or from code).
  2. Register that data as a texture in the texture registry.
  3. Display the texture using an image widget (or other display method).

Let's see this in practice.

Loading Images with dpg.load_image()

DearPyGui provides a built-in helper function, dpg.load_image(), that loads an image file and returns the data in exactly the format the texture registry expects.

python
width, height, channels, data = dpg.load_image("my_image.png")

This function returns four values:

  • width — the image width in pixels
  • height — the image height in pixels
  • channels — the number of color channels (3 for RGB, 4 for RGBA)
  • data — a flat list of pixel values, normalized to floats between 0.0 and 1.0

The load_image() function supports common formats like PNG, JPG, and BMP. If the file can't be found or loaded, it returns None — so it's good practice to check for that.

python
result = dpg.load_image("my_image.png")
if result is None:
    print("Failed to load image!")
else:
    width, height, channels, data = result

Now let's put this data to use.

Method 1: Static Textures

A static texture is the simplest and most common way to display an image. Once created, its pixel data cannot be changed. This makes it perfect for icons, logos, backgrounds, or any image that doesn't need to update.

python
import dearpygui.dearpygui as dpg

dpg.create_context()

width, height, channels, data = dpg.load_image("my_image.png")

with dpg.texture_registry():
    dpg.add_static_texture(
        width=width,
        height=height,
        default_value=data,
        tag="my_texture"
    )

with dpg.window(label="Image Example", width=600, height=400):
    dpg.add_image("my_texture")

dpg.create_viewport(title="Static Image", width=700, height=500)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

Let's break down what's happening:

  1. We load the image file with dpg.load_image().
  2. Inside a texture_registry block, we create a static texture and give it the tag "my_texture".
  3. In our window, we use dpg.add_image() and pass the texture's tag.

That's it! The image appears in your window.

Sizing the Image

By default, dpg.add_image() displays the image at its original pixel dimensions. You can override this with the width and height parameters:

python
dpg.add_image("my_texture", width=200, height=150)

This scales the image to 200×150 pixels in the UI, regardless of the original image dimensions.

Displaying a Portion of the Image with UV Coordinates

You can also display just a portion of the texture using UV coordinates. UV coordinates range from (0, 0) at the top-left corner to (1, 1) at the bottom-right corner.

python
# Display only the top-left quarter of the image
dpg.add_image("my_texture", uv_min=(0, 0), uv_max=(0.5, 0.5))

This is useful for sprite sheets or when you want to crop an image without modifying the source file.

Method 2: Dynamic Textures

What if you need to update an image after it's been created? That's where dynamic textures come in. A dynamic texture works exactly like a static texture, except you can modify its pixel data at any time.

python
import dearpygui.dearpygui as dpg

dpg.create_context()

# Create a 200x200 red image (RGBA)
tex_width, tex_height = 200, 200
texture_data = []
for y in range(tex_height):
    for x in range(tex_width):
        texture_data.extend([1.0, 0.0, 0.0, 1.0])  # Red, fully opaque

with dpg.texture_registry():
    dpg.add_dynamic_texture(
        width=tex_width,
        height=tex_height,
        default_value=texture_data,
        tag="dynamic_texture"
    )

def make_blue(sender, app_data):
    new_data = []
    for y in range(tex_height):
        for x in range(tex_width):
            new_data.extend([0.0, 0.0, 1.0, 1.0])  # Blue
    dpg.set_value("dynamic_texture", new_data)

with dpg.window(label="Dynamic Texture", width=400, height=400):
    dpg.add_image("dynamic_texture")
    dpg.add_button(label="Change to Blue", callback=make_blue)

dpg.create_viewport(title="Dynamic Image", width=500, height=500)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

The key line is dpg.set_value("dynamic_texture", new_data). This replaces all the pixel data in the texture, and the displayed image updates immediately.

Dynamic textures are ideal for:

  • Live previews (e.g., a color picker preview)
  • Procedurally generated graphics
  • Video frames or camera feeds
  • Any image that changes in response to user interaction

Performance Tip

Generating large texture data in pure Python (as we did above with nested loops) can be slow. For real applications, consider using NumPy to create and manipulate pixel data, then convert it to a flat list:

python
import numpy as np

# Create a 200x200 green image
pixels = np.zeros((200, 200, 4), dtype=np.float32)
pixels[:, :, 1] = 1.0  # Green channel
pixels[:, :, 3] = 1.0  # Alpha channel
texture_data = pixels.flatten().tolist()

This is dramatically faster for larger images.

Method 3: Raw Textures

Raw textures are a more advanced option designed for maximum performance. Unlike static and dynamic textures, raw textures accept data as a flat array and give you direct control over the pixel format. They support formats like dpg.mvFormat_Float_rgba, dpg.mvFormat_Float_rgb, and integer-based formats.

python
import dearpygui.dearpygui as dpg
import array

dpg.create_context()

tex_width, tex_height = 100, 100

# Using the array module for more efficient storage
raw_data = array.array('f')  # 'f' = float
for y in range(tex_height):
    for x in range(tex_width):
        raw_data.extend([0.0, 1.0, 0.0, 1.0])  # Green, RGBA

with dpg.texture_registry():
    dpg.add_raw_texture(
        width=tex_width,
        height=tex_height,
        default_value=raw_data,
        format=dpg.mvFormat_Float_rgba,
        tag="raw_texture"
    )

with dpg.window(label="Raw Texture", width=300, height=300):
    dpg.add_image("raw_texture")

dpg.create_viewport(title="Raw Texture Example", width=400, height=400)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

The big advantage of raw textures is that when you modify the underlying array.array (or NumPy array) data, the texture updates automatically — you don't need to call dpg.set_value(). The texture reads directly from the buffer.

python
def update_raw():
    """Modify the buffer in place — the texture updates automatically."""
    for i in range(0, len(raw_data), 4):
        raw_data[i] = 1.0      # Red
        raw_data[i + 1] = 0.0  # Green off

This makes raw textures the best choice for high-performance scenarios where you're updating image data every frame, such as real-time visualizations or video playback.

Available Formats

Format Constant Description
dpg.mvFormat_Float_rgba 4 floats per pixel (RGBA)
dpg.mvFormat_Float_rgb 3 floats per pixel (RGB)

Method 4: Image Buttons

Sometimes you want an image that the user can click — like an icon button or a clickable thumbnail. DearPyGui provides dpg.add_image_button() for exactly this purpose.

python
import dearpygui.dearpygui as dpg

dpg.create_context()

width, height, channels, data = dpg.load_image("button_icon.png")

with dpg.texture_registry():
    dpg.add_static_texture(
        width=width,
        height=height,
        default_value=data,
        tag="button_texture"
    )

def on_click(sender, app_data):
    print("Image button clicked!")

with dpg.window(label="Image Button", width=400, height=300):
    dpg.add_image_button(
        "button_texture",
        width=64,
        height=64,
        callback=on_click
    )

dpg.create_viewport(title="Image Button Example", width=500, height=400)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

The add_image_button() widget behaves just like a regular button — it supports callbacks, can be enabled/disabled, and responds to clicks — but it renders your texture instead of text.

You can also use UV coordinates with image buttons to display a cropped portion of a texture, which is great for toolbar icons stored in a single sprite sheet:

python
dpg.add_image_button(
    "sprite_sheet",
    width=32,
    height=32,
    uv_min=(0.0, 0.0),
    uv_max=(0.25, 0.25),
    callback=on_click
)

Method 5: Images on the Drawing API

DearPyGui includes a powerful drawing API that lets you draw shapes, lines, text, and images onto a canvas. This is useful when you need precise control over image positioning, or when you're building something like a game, diagram editor, or custom visualization.

python
import dearpygui.dearpygui as dpg

dpg.create_context()

width, height, channels, data = dpg.load_image("my_image.png")

with dpg.texture_registry():
    dpg.add_static_texture(
        width=width,
        height=height,
        default_value=data,
        tag="draw_texture"
    )

with dpg.window(label="Drawing Canvas", width=600, height=500):
    with dpg.drawlist(width=500, height=400):
        dpg.draw_image(
            "draw_texture",
            pmin=(50, 50),
            pmax=(250, 250)
        )

dpg.create_viewport(title="Drawing API Image", width=700, height=600)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

With dpg.draw_image(), you specify:

  • pmin — the top-left corner position on the canvas
  • pmax — the bottom-right corner position on the canvas

The image is stretched (or shrunk) to fill the rectangle defined by these two points. You can also use uv_min and uv_max to display a portion of the texture.

Layering Images

One powerful feature of the drawing API is layering. You can draw multiple images on top of each other, combine them with shapes and text, and control the draw order:

python
with dpg.drawlist(width=500, height=400):
    # Background image
    dpg.draw_image("background_texture", pmin=(0, 0), pmax=(500, 400))
    # Foreground character
    dpg.draw_image("character_texture", pmin=(100, 100), pmax=(200, 200))
    # Label on top
    dpg.draw_text((100, 210), "Player 1", size=20, color=(255, 255, 255))

Items are drawn in order, so later items appear on top of earlier ones.

Method 6: Images on Node Editors and Plots

You can also place images onto plots and inside node editors, using the same texture-based approach.

Images on Plots

Placing an image on a plot is useful for heatmaps, annotated charts, or background reference images:

python
with dpg.plot(label="Plot with Image", width=500, height=400):
    dpg.add_plot_axis(dpg.mvXAxis, label="X")
    with dpg.plot_axis(dpg.mvYAxis, label="Y"):
        dpg.add_image_series(
            "my_texture",
            bounds_min=(0, 0),
            bounds_max=(10, 10)
        )

The bounds_min and bounds_max parameters define the position in plot coordinates (not pixel coordinates), so the image scales and pans along with the plot axes.

Using Pillow (PIL) to Load Images

While dpg.load_image() works well for basic image loading, you might want to use Pillow for more advanced image manipulation — resizing, filtering, compositing, or loading formats that load_image() doesn't support.

Here's how to bridge Pillow and DearPyGui:

python
import dearpygui.dearpygui as dpg
from PIL import Image
import numpy as np

dpg.create_context()

# Load and process with Pillow
img = Image.open("my_image.png").convert("RGBA")
img = img.resize((200, 200))  # Resize as needed

# Convert to DearPyGui-compatible format
img_array = np.array(img, dtype=np.float32) / 255.0  # Normalize to 0.0–1.0
texture_data = img_array.flatten().tolist()

with dpg.texture_registry():
    dpg.add_static_texture(
        width=200,
        height=200,
        default_value=texture_data,
        tag="pil_texture"
    )

with dpg.window(label="Pillow Image", width=400, height=400):
    dpg.add_image("pil_texture")

dpg.create_viewport(title="Pillow + DearPyGui", width=500, height=500)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

The critical step is normalizing the pixel values. Pillow stores pixels as integers (0–255), but DearPyGui textures expect floats (0.0–1.0). Dividing by 255.0 handles this conversion.

Showing the Texture Registry (Debugging)

When you're working with multiple textures, it can be hard to keep track of what's loaded. DearPyGui provides a built-in debug tool that shows you everything in the texture registry:

python
dpg.show_item_registry()

You can also make the texture registry visible with a simple call:

python
with dpg.texture_registry(show=True):
    dpg.add_static_texture(width=w, height=h, default_value=data, tag="tex1")
    dpg.add_static_texture(width=w2, height=h2, default_value=data2, tag="tex2")

Setting show=True on the texture registry opens a window that displays thumbnails of all registered textures. This is extremely helpful during development.

Quick Reference: Choosing the Right Approach

Here's a summary to help you decide which method to use:

Method Best For Can Update?
Static Texture + add_image() Icons, logos, static images No
Dynamic Texture + add_image() Live previews, procedural images Yes (via set_value())
Raw Texture + add_image() High-performance updates, video Yes (automatic from buffer)
Image Button Clickable icons, toolbars No (use dynamic texture if needed)
Drawing API Precise positioning, layering, games No (redraw to update)
Plot Image Series Data visualization overlays No (use dynamic texture if needed)

Summary

Adding images to DearPyGui follows a consistent pattern: load your pixel data, register it as a texture, then display it with a widget. The flexibility of this approach means you can use the same texture data in multiple places — an image widget, a button, a drawing canvas, or a plot — without duplicating the data.

Start with static textures and dpg.add_image() for most use cases. When you need images that change, reach for dynamic textures. And when performance is critical, raw textures give you the fastest path to getting pixels on screen.

The most important thing is to experiment. Try loading different images, changing their sizes, combining the drawing API with image widgets, and updating textures in response to user actions. The texture system in DearPyGui is powerful, and once you're comfortable with the pattern, you'll find images easy to work with across your entire application.

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Create GUI Applications with Python & Qt6 by Martin Fitzpatrick

(PySide6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!

More info Get the book

Martin Fitzpatrick

Adding Images in DearPyGui was written by Martin Fitzpatrick.

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.