mwerezak / DearPyGui-Obj

An object-oriented wrapper around DearPyGui
MIT License
45 stars 3 forks source link

Add UserWidget class to streamline creating custom widgets #27

Closed mwerezak closed 3 years ago

mwerezak commented 3 years ago

Allow users to easily reuse the widget object API.

Goal is something like:

@dataclass
class Person:
    firstname: str
    lastname: str

class PersonInfo(UserWidget):
    def __setup_widget__(self, person: Person):
        Separator()
        with group_horizontal():
            self.selected_chk = CheckBox()
            Button(arrow=ButtonArrow.Up, callback=self.move_up)
            Button(arrow=ButtonArrow.Down, callback=self.move_down)
            Text(f'First name: {person.firstname}')
            Text(f'Last name: {person.lastname}')

    @property
    def selected(self) -> bool:
        return self.selected_chk.value

with Window('Person List Example'):
    container = Group()

    Separator()

    remove_btn = Button('Remove Selected')
    add_btn = Button('Add Person')
    fname_inp = InputText('First name')
    lname_inp = InputText('Last name')

@remove_btn.callback
def callback():
    for child in container.iter_children():
        if child.selected:
            child.delete()

@add_btn.callback
def callback():
    person = Person(fname_inp.value, lname_inp.value)
    PersonInfo.add_to(container, person)

if __name__ == '__main__':
    start_gui()
Amorano commented 3 years ago

def setup_widget(self, person: Person): --> def setup_widget(self, person: Person, **kw):

strip the KW you want from the init of the widget, and pass a clean kwarg dict with only DPG stuff you allow for change at init i.e. things like "parent" to the DPG API for widget create.

mwerezak commented 3 years ago

Thanks, it works! Here's the full implementation:

class UserWidget(Widget, ItemWidget):
    def __init__(self, *args: Any, parent: str = None, before: str = None, name_id: str = None, **kwargs: Any):
        super().__init__(parent=parent, before=before, name_id=name_id)
        self.__setup_content__(*args, **kwargs)
        dpgcore.end()

    def __setup_add_widget__(self, dpg_args) -> None:
        for kw in ('before', 'parent'):
            if dpg_args[kw] is None:
                del dpg_args[kw]

        dpgcore.add_group(self.id, **dpg_args)

    @abstractmethod
    def __setup_content__(self, *args, **kwargs) -> None:
        """The contents of the UserWidget should be added here."""
        ...

I thought my issues had something to do with the fact that I was "runtime" adding the PersonInfo (using add_to()). But it seems like even if you use parent the group still goes on the parent stack and can have other widgets added like normal? So I had misattributed that. In reality it was my kw handling causing the problem.

mwerezak commented 3 years ago

Implemented UserWidget. I also have plans to implement a similar thing for custom containers where you can specify which part of the custom widget will hold any children that get added. But that won't be possible until 0.7 so for now I'm calling this complete.