zauberzeug / nicegui

Create web-based user interfaces with Python. The nice way.
https://nicegui.io
MIT License
8.63k stars 521 forks source link

ui.menu submenu click does not open dialog. #1852

Closed muratbedir closed 10 months ago

muratbedir commented 10 months ago

Description


@ui.page('/')
def main_page() -> None:
    menu()

    with ui.column().classes('absolute-center items-center') :        
        ui.label("SMS test uygulaması").classes('text-2xl text-positive')
        # telefon=ui.input("Telefon Numarası giriniz", validation={'11 haneli telefon girini 0530112233 gibi': lambda value: (  btnyolla.disable()if len(value) != 11   else btnyolla.enable()  ), "11 haneli değer girdiniz":  lambda value: (len(value)==11 )}).classes('text-xl')
        #telefon=ui.input("Telefon Numarası giriniz", validation={'11 haneli telefon girini 0530112233 gibi': lambda value: (  btnyolla.disable() if len(value) < 10 or len(value) >11   else 1==1 ), "11 haneli değer girdiniz":  lambda value: ( btnyolla.enable() if len(value)==11 else 1==1 )}).classes('text-xl')
        with ui.input("Telefon Numarası giriniz",  validation={'11 haneli telefon girini 0530112233 gibi': lambda value: (  False if len(str(value)) < 11 or len(str(value)) >11   else True ) },on_change=lambda e: badge.set_text(int(len(str(e.value))))).classes('text-xl').props('standout') as telefon:
            badge = ui.badge('0', color='red').props('floating outlined')
        sms=ui.textarea("SMS Metnini giriniz",placeholder="SMS test metnini giriniz",value=  datetime.datetime.now().strftime( "%d-%b-%Y-%H:%M:%S"), validation={'Min 5 , Max 160 Char giriniz': lambda value: (len(value) < 160) and (len(value) > 5) }).classes('text-xl').props('standout')
        ui.timer(1.0, lambda: sms.set_value(f'{datetime.datetime.now().strftime( "%d-%b-%Y-%H:%M:%S")}'))
        secim=ui.radio({1:'Standart SMS',2:'OTP SMS'},value=1).props('standout') 
        btnyolla= ui.button("SMS yolla", color='red', icon='send', on_click=lambda: (smsyolla(telefon,sms,secim) )).props('rounded glossy').classes('q-pa-md q-gutter-sm').style('background: #FF0080; color: white ; font-size: 100%; font-weight: 400')
        #btnyolla.disable()
        ui.label(f'Merhaba sisteme {app.storage.user["username"]}! ile logon olunmuştur').classes('text-xl').props('clearable') #.style("background: #FF0080; color: white")
        ui.button(on_click=lambda: (app.storage.user.clear(), ui.open('/login')), icon='logout').props('outline round')

        ui.button('menu',on_click=lambda: (hakkinda())).props('outline round')

@ui.page('/subpage')
def test_page() -> None:
    ui.label('This is a sub page.')
@ui.page('/about')
def about() -> None :
    hakkinda().open()
@ui.page('/login')
def login() -> Optional[RedirectResponse]:
    def try_login() -> None:  # local function to avoid passing username and password as arguments
        if is_user_valid (username.value,password.value):
            app.storage.user.update({'username': username.value, 'authenticated': True})
            ui.open(app.storage.user.get('referrer_path', '/'))  # go back to where the user wanted to go
        else:
            ui.notify('Wrong username or password', color='negative')

    if app.storage.user.get('authenticated', False):
        return RedirectResponse('/')
    with ui.card().classes('absolute-center'):
        ui.label("SMS test uygulaması").classes('text-2xl text-positive')
        username = ui.input('GIG Domain kullanıcı adını giriniz').on('keydown.enter', try_login)
        password = ui.input('Şifreyi giriniz', password=True, password_toggle_button=True).on('keydown.enter', try_login)
        ui.button('Log in', on_click=try_login)

def smsyolla(telno,sms,secim) :
    if len(telno.value) == 11:
        if secim.value ==1 :
            sendsms(telno.value,sms.value)
            ui.notify("SMS GİTTİ",type='info',position="center")
        else:
            sendotpsms(telno.value,sms.value)
            ui.notify("SMS GİTTİ",type='info',position="center")
    else   :
        ui.notify("Telefon numarası hatalı 11 hane ve numerik olmalı, mesaj gönderilemedi",type="negative",position="center")

def hakkinda():
    with ui.dialog() as dialog, ui.card():
        ui.label('(c) 2023  A.Ş. ')
        ui.button('Kapa', on_click=lambda:  ui.open("/"))
    return dialog

def menu():
    with ui.row().classes('w-full items-center'):
        result = ui.label().classes('mr-auto')
        with ui.button(icon='menu'):
            with ui.menu() as menu:
                ui.menu_item('Hakkında', on_click=lambda:(hakkinda()) )
                ui.menu_item('Google 2', lambda:  ui.open("https://www.google.com") )
                ui.menu_item('Menu item 3', lambda: result.set_text('Selected item 3'))
return dialog

Hakkında menu item does not open hakkinda() dialogbox , when I try below sequence dialog box opens via menu

When select Hakkında menu item then thats quits then I click sandwich menu icon dialog box opens I thing this is a bug or I am new to nicegui then I am making smoneting wrong.

but

ui.button('menu',on_click=lambda: (hakkinda())).props('outline round')

is open dialog box without any problem.

thanks for help.

*I found nicegui after a lot of searches, before nicegui I used to streamlit but streamlit not customisations options like nicegui.

falkoschindler commented 10 months ago

@muratbedir Sorry, can you, please, reduce this example to the bare minimum? In its current form the app isn't even starting without modification due to missing storage entries, all of which probably isn't needed for reproducing the issue. I guess you only need a single button and a dialog?

muratbedir commented 10 months ago

Hi Falco, thanks for help, in using below code you can reproduce problem.

Problem is : when pressed "open dialog" its open dialog without any problem via ui.button('open dialog',on_click=lambda: (about())).props('outline round') code

but when I select about from menu via ui.button('open dialog',on_click=lambda: (about())).props('outline round') dialog box does not open but strangely I presses hamburger menu icon its openning.

Thanks for help.

from nicegui import app, ui

@ui.page('/')
def main_page() -> None:
    menu()

    with ui.column().classes('absolute-center items-center') :        
        ui.label("SMS test uygulaması").classes('text-2xl text-positive')
        ui.button('open dialog',on_click=lambda: (about())).props('outline round')

@ui.page('/subpage')
def test_page() -> None:
    ui.label('This is a sub page.')

def about():
    with ui.dialog() as dialog, ui.card():
        ui.label('(c) 2023  A.Ş. ')
        ui.button('close', on_click=lambda:  ui.open("/"))

    return dialog

def menu():
    with ui.row().classes('w-full items-center'):
        result = ui.label().classes('mr-auto')
        with ui.button(icon='menu'):
            with ui.menu() as menu:
                ui.menu_item('About', on_click=lambda:(about()) )
                ui.menu_item('Google 2', lambda:  ui.open("https://www.google.com") )
                ui.menu_item('Menu item 3', lambda: result.set_text('Selected item 3'))

ui.run(storage_secret='THIS_NEEDS_TO_BE_CHANGED',port=7654)
falkoschindler commented 10 months ago

Thanks! So the problem boils down to this:

def about():
    with ui.dialog() as dialog, ui.card():
        ui.label('About')
    return dialog

ui.button('About', on_click=about)  # works
with ui.button('Menu'):
    with ui.menu():
        ui.menu_item('About', on_click=about)  # doesn't work

Very strange... I'll look into it.

falkoschindler commented 10 months ago

Oh, I see. It's because NiceGUI's event handler creates new elements in the same context like the element which causes the event:

I'd recommend creating the dialog once and opening/closing it when needed:

with ui.dialog() as about, ui.card():
    ui.label('About')
    ui.button('Close', on_click=about.close)

ui.button('About', on_click=about.open)
with ui.button('Menu'):
    with ui.menu():
        ui.menu_item('About', on_click=about.open)
muratbedir commented 10 months ago

I understand problem, after your recommended modification, its working well, I share modified code in below for reference.

But in this case, I need to pass about dialogbox's reference to menu() function because its scope different.

from nicegui import app, ui

@ui.page('/')
def main_page() -> None:
    with ui.dialog() as about, ui.card():
        ui.label('About')
        ui.button('Close', on_click=about.close)
    menu(about)
    with ui.column().classes('absolute-center items-center') :        
        ui.label("SMS test uygulaması").classes('text-2xl text-positive')
        ui.button('open dialog',on_click=about.open).props('outline round')

@ui.page('/subpage')
def test_page() -> None:
    ui.label('This is a sub page.')

def menu(about):
    with ui.row().classes('w-full items-center'):
        result = ui.label().classes('mr-auto')
        with ui.button(icon='menu'):
            with ui.menu() as menu:
                ui.menu_item('About', on_click=about.open )
                ui.menu_item('Google 2', lambda:  ui.open("https://www.google.com") )
                ui.menu_item('Menu item 3', lambda: result.set_text('Selected item 3'))

ui.run(storage_secret='THIS_NEEDS_TO_BE_CHANGED',port=7654)