2025, Nov 28 07:00

Fixing Dear PyGui callbacks: get user_data instead of the button tag by using the correct signature

Learn why Dear PyGui callbacks print a button tag instead of user_data and how to fix it by declaring sender, app_data, user_data. Includes example code and tips

Passing data into UI callbacks sounds trivial until the value you expect isn’t the one you get. In Dear PyGui, a common gotcha is printing the button tag instead of your custom payload when using user_data. The issue stems from the callback function signature, not from how you attach the data.

Minimal example that reproduces the issue

The following snippet wires a callback to each button and attempts to pass an integer index as user_data. The printed value ends up being the button’s tag rather than the index.

import dearpygui.dearpygui as dpg
for idx in range(N):
    with dpg.group(horizontal=True):
        dpg.add_button(label="add", tag="btn_add_" + str(idx))
        dpg.set_item_callback(item="btn_add_" + str(idx), callback=on_add_click)
        dpg.set_item_user_data(item="btn_add_" + str(idx), user_data=idx)
def on_add_click(payload):
    print(f"on_add_click: {payload}")

What’s really happening

Dear PyGui passes three positional arguments into a callback: sender, app_data, and user_data. If the function only declares a single parameter, that parameter receives the first positional argument, which is the sender. For a button, the sender is its tag. That’s why the printout shows something like btn_add_0 instead of the integer you attempted to pass.

The library’s behavior is documented in the official item callbacks guide: https://dearpygui.readthedocs.io/en/latest/documentation/item-callbacks.html.

The fix

Declare the callback with three parameters so the third one receives the user_data you supplied. You can ignore sender and app_data if you don’t need them.

def on_add_click(sender, app_data, payload):
    print(f"on_add_click: {payload}")

You can also attach the callback and user_data directly at creation time, avoiding separate setter calls.

import dearpygui.dearpygui as dpg
def on_add_click(sender, app_data, payload):
    print(f"on_add_click: {payload}")
for idx in range(N):
    with dpg.group(horizontal=True):
        dpg.add_button(
            label="add",
            tag="btn_add_" + str(idx),
            callback=on_add_click,
            user_data=idx,
        )

Why this matters

Understanding the exact callback signature saves time and prevents subtle bugs where the wrong value silently flows through your logic. It reduces confusion when debugging UI behavior and makes your event handling predictable. Once the function signature matches Dear PyGui’s calling convention, user_data behaves consistently, whether it’s an integer index or any other payload.

Takeaways

When you expect to consume user_data in a Dear PyGui callback, always define the function with three positional parameters: sender, app_data, and user_data. Print or process only the third one if that’s all you need. To keep the code tidy, supplying callback and user_data directly in add_button works just as well as setting them afterward.