pythonpython-3.xuser-interfacehandlernicegui

how can I get the name of svg element when i click on it in nicegui


I have a NiceGUI page with an SVG image consisting of two elements (a circle and a square). I need to check which element of this image was clicked when the SVG image is clicked, so that I can handle it.

I want the event "circle clicked" to be triggered when the circle is clicked, and the event "square clicked" to be triggered when the square is clicked. I am trying to implement this through a handler, but I don't know what to write in the handler function. I am attaching the code.

My method 1:

from nicegui import ui

def my_handler(event_args):
    print("Button clicked!")

with ui.row():
    svg_element = ui.html('''
        <svg width="200" height="200" style="border: 1px solid black;">
            <circle cx="50" cy="50" r="40" fill="red" class="clickable-circle"/>
            <rect x="100" y="10" width="80" height="80" fill="blue" class="clickable-square"/>
        </svg>
    ''')

    svg_element.on('click', js_handler='(click) => { console.log(event.target.clickable-circle); }')

ui.run(port = 8082)

My method 2:

from nicegui import ui


def my_handler(event_args):
    element = event_args.sender
    # ???????


with ui.row():
    svg_element = ui.html('''
        <svg width="200" height="200" style="border: 1px solid black;">
            <circle cx="50" cy="50" r="40" fill="red" class="clickable-circle"/>
            <rect x="100" y="10" width="80" height="80" fill="blue" class="clickable-square"/>
        </svg>
    ''')

    svg_element.on('click', handler=my_handler)

ui.run(port=8082)

Thank you all for your help.


Solution

  • If you want to use JavaScript then you have to use the same name event in

    (event) => { console.log(event.target) } 
    

    I think you could use it to emit custom event with information what object was clicked

    emitEvent(`clicked_${event.tagerg.tagName}`)
    

    and it should send event clicked_circle or clicked_rect (and maybe even clicked_svg) to Python and it can use it to execute function with ui.on() instead of element_svg.on()

    ui.on('clicked_circle', handler=my_handler_for_circle)
    ui.on('clicked_rect', handler=my_handler_for_rect)
    

    You may also use it with parameters

    ui.on('clicked_circle', handler=lambda event:my_handler(event, "circle"))
    ui.on('clicked_rect',   handler=lambda event:my_handler(event, "rect"))
    

    Maybe you could even use emitEvent to send some extra information but this would need to check it on internet because I never used it before.


    UPDATE:

    emitEvent may sends also args (for example name of clicked tag) and it allows to create only one event which will send different value(s) in args

    emitEvent("clicked_svg", {target: event.target.tagName});
    

    and it will need only one ui.on()

    ui.on('clicked_svg', handler=on_click_svg)
    

    and it will need only one function

    def on_click_svg(event):
        print("--- clicked svg ---")
        print('event:', event)
        print('event.args["target"]:', event.args['target']) 
        if event.args['target'] == 'circle':
            print('>>> clicked circle <<<')
        if event.args['target'] == 'rect':
            print('>>> clicked rect <<<')
    

    Doc: Custom Event - NiceGUI


    Full working code used for tests.

    from nicegui import ui
    
    def my_handler(event, target):
        print('--- my_handler ---')
        print('event:', event)
        print('event.args["target"]:', event.args['target']) 
        print('target:', target)
    
    def on_click_svg(event):
        print("--- clicked svg ---")
        print('event:', event)
        print('event.args["target"]:', event.args['target']) 
        if event.args['target'] == 'circle':
            print('>>> clicked circle <<<')
        if event.args['target'] == 'rect':
            print('>>> clicked rect <<<')
    
    with ui.row():
        svg_element = ui.html('''
            <svg width="200" height="200" style="border: 1px solid black;">
                <circle cx="50" cy="50" r="40" fill="red" class="clickable-circle"/>
                <rect x="100" y="10" width="80" height="80" fill="blue" class="clickable-square"/>
            </svg>
        ''')
    
        ui.on('clicked_circle', handler=lambda event:my_handler(event, 'circle'))
        ui.on('clicked_rect',   handler=lambda event:my_handler(event, 'rect'))
        ui.on('clicked_svg',    handler=on_click_svg)
    
        svg_element.on('click', js_handler='''(event) => { 
                       console.log(event.target.tagName); 
                       emitEvent(`clicked_${event.target.tagName}`, {target: event.target.tagName});
                       emitEvent("clicked_svg", {target: event.target.tagName});
                       }''')
    
    ui.run(port = 8082)