Search code examples
pythonfrontendnicegui

Is it possible to use widgets in NiceGUI Python?


I recently got acquainted with the NiceGUI library for python and decided to rewrite the Todo List demo in one kind of class.

One list works well, when adding a second one there are difficulties: one of the lists may not respond to the fact that I mark the task as completed, until I delete or add a task there, then the second list stops responding.

Here is the source code: https://github.com/zauberzeug/nicegui/blob/main/examples/todo_list/main.py And here is my code:

from nicegui import ui
from dataclasses import dataclass
from typing import List


@dataclass
class TodoItem:
    name: str
    done: bool = False


class TodoListComponent:
    def __init__(self, title: str, items: List[TodoItem] = None):
        self.title = title
        if items:
            self.items = items
        else:
            self.items = []

    def view(self):
        with ui.card().classes('w-80 items-stretch'):
            ui.label(self.title).classes('text-semibold test-2xl')
            self.on_change()
            add_input = ui.input('New item').classes('mx-12')
            add_input.on('keydown.enter', lambda: (self.add(add_input.value),
                                                   add_input.set_value('')))

    def add(self, name, done=False):
        self.items.append(TodoItem(name=name))
        self.on_change.refresh()

    def remove(self, item):
        self.items.remove(item)
        self.on_change.refresh()

    @ui.refreshable
    def on_change(self):
        print(self.title, self.items) # when I mark a task from the first list as completed, there may be information from the second
        if not self.items:
            ui.label('List is empty.').classes('mx-auto')
            return
        done_items = sum(item.done for item in self.items)
        ui.linear_progress(done_items / len(self.items), show_value=False)
        with ui.row().classes('justify-center w-full'):
            ui.label(f'Completed {done_items}')
            ui.label(f'Remaining: {len(self.items) - done_items}')
        for item in self.items:
            with ui.row().classes('items-center'):
                ui.checkbox(value=item.done, on_change=self.on_change.refresh).bind_value(item, 'done')
                ui.input(value=item.name).classes('flex-grow').bind_value(item, 'name')
                ui.button(on_click=lambda item=item: self.remove(item), icon='delete')\
                    .props('flat fab-mini color=grey')


todos = TodoListComponent('My Friday')
todos.add('Order pizza', done=True)
todos.add('New NiceGUI Release')
todos.add('Clean the house')
todos.add('Call mom')
todos.view()

todos_2 = TodoListComponent('My Monday')
todos_2.add('Order pizza', done=True)
todos_2.add('New NiceGUI Release')
todos_2.add('Clean the house')
todos_2.add('Call mom')
todos_2.view()

ui.run()

I'm just wondering if it's possible to create widgets on NiceGUI in the same way as it is done on javascript frameworks (or some alternatives in python)


Solution

  • I'm not sure why, but replacing

    on_change=self.on_change.refresh
    

    with

    on_change=lambda: self.on_change.refresh()
    

    seems to resolve the issue with refreshing the wrong list. I'll probably file a bug report, since this is rather unexpected.

    Edit: Here it is.