hoffstadt/DearPyGui

Doc example generates "Alias already exists" exception

jadczak opened this issue · 7 comments

Version of Dear PyGui

Version: 1.10.1
Operating System: Windows 11

My Issue/Question

The first code example under Runtime Adding and Deleting in the documentation generates an "Alias already exists" exception when Add Buttons is clicked twice in a row without clicking Delete Buttons

To Reproduce

Copy the first code example from the Runtime Adding and Deleting and click Add Buttons twice.

Expected behavior

The example code does no generate a runtime exception.
or
The documentation describes why an exception may be generated for the example code.

Screenshots/Video

too-many-buttons

Standalone, minimal, complete and verifiable example

import dearpygui.dearpygui as dpg

dpg.create_context()

def add_buttons():
    global new_button1, new_button2
    new_button1 = dpg.add_button(label="New Button", before="delete_button", tag="new_button1")
    new_button2 = dpg.add_button(label="New Button 2", parent="secondary_window", tag="new_button2")


def delete_buttons():
    dpg.delete_item("new_button1")
    dpg.delete_item("new_button2")


with dpg.window(label="Tutorial", pos=(200, 200)):
    dpg.add_button(label="Add Buttons", callback=add_buttons)
    dpg.add_button(label="Delete Buttons", callback=delete_buttons, tag="delete_button")

with dpg.window(label="Secondary Window", tag="secondary_window", pos=(100, 100)):
    pass

dpg.create_viewport(title='Custom Title', width=600, height=400)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

The example code does no generate a runtime exception.

What should it do on the second call then? Add more buttons or ignore the call or something else?

The documentation describes why an exception may be generated for the example code.

IMHO - While I'm all for having as detailed and thorough documentation as possible, explaining every possible error on every corner would turn it into a nearly unreadable mess. Personally I'd also prefer to have as clean doc as possible.

The error message in the exception is, to me, pretty clear about what has happened, and doesn't need further explanation.

This example uses tag parameters described in the Tag System section of the doc. That section clearly says,

It is the user’s responsibility to make sure aliases are unique.

What should it do on the second call then? Add more buttons or ignore the call or something else?

I'm indifferent on what should happen here from a dearpygui perspective. My issue is that the example code results in confusing behavior.

I click Add buttons once and a button is added to the Tutorial window and the Secondary window (No problem here, makes sense).
I click Add buttons again and no button is added to the Secondary window (ok sure, we already have a button) but a button is added to the Tutorial window (wait, what?) and an exception is thrown (sure, something seems to have gone wrong, I guess?)

Why did adding new_button1 to the Tutorial window twice throw an exception, but still add the button to the UI?
Is it because new_button1 has the before keyword while new_button2 doesn't?
Is it because new_button2 has the parent keyword while new_button1 doesn't?
Is it something completely unrelated?

I don't know, I'm reading the documentation because I don't know how this stuff is supposed to work.

but a button is added to the Tutorial window (wait, what?)

Wow 🤣. This is interesting. And inconsistent, I agree.

Sooo... I think I've found the cause. The explanation will be highly technical though.

When a new widget is being added in DPG, first of all DPG creates a C++ object that represents that widget. After that, it finds the insertion point in the widget tree corresponding to the parent and before parameters. And finally, it adds that C++ object to the tree.

The problem is, the alias (passed in the tag argument) gets registered at object creation, and if it already exists, the object still gets created and goes all the way through insertion into the widget tree. A Python exception is thrown on a duplicate alias, but it's not recognized until C++ code gets back into the Python interpreter.

Good catch! :)

So if we fix that issue with exception handling in C++, the 2nd click on "Add buttons" will fail immediately, without adding anything to the window.

For future reference...

common_constructor() gets called to create the widget object. If tag is specified in the DPG call, it gets parsed into the alias variable, and AddAlias() gets called with that value. AddAlias() can do mvThrowPythonError() but this does not affect further control flow. So we need to add a quick-exit path here.

Thanks for digging into this and also for the explanation of what's going on.