pythonplotlynicegui

How do I get Plotly to work well with NiceGUI?


I know this is simple, and yet I can't figure it out. All I want to do is take an input list of number, bind it to a dictionary, and create a plot that refreshes when I click a button. I've been able to do this to some degree with the below code, but problems arise as the dictionary returns dictionary data NOT in a list for some reason.

from nicegui import ui
import plotly.graph_objects as go
import numpy as np

coords = {'x': [], 'y': []}
ui.input(label='X',value=[1, 2, 3, 4]).bind_value_to(coords,'x')
ui.input(label='Y',value=[1, 2, 3, 4]).bind_value_to(coords,'y')

fig = go.Figure(go.Scatter(x=coords['x'], y=coords['y']))
fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))
plot = ui.plotly(fig).classes('w-full h-40')

def update_plt():
    fig.data = []
    x2 = coords['x']
    y2 = coords['y']
    fig.add_trace(go.Scatter(x=x2, y=y2))
    plot.update()
    print(fig.data)

ui.button('Refresh', on_click=update_plt)
ui.run()

Solution

  • I don't know what dictionary you mean but I found two problems in code.

    First:

    if you change values in input on page then it returns it as string "1,2,3" instead of list [1,2,3] but as for me it is natural - in most GUIs input returns string and you have to convert it to expected format on your own.

    Because if you don't change values in input on page then it returns original list [1,2,3,4] instead of string "1,2,3,4" so it needs to check type of data isinstance(x2, str) before converting to list

        if isinstance(x2, str):
            x2 = [int(x) for x in x2.split(',')]
        if isinstance(y2, str):
            y2 = [int(y) for y in y2.split(',')]
    

    Maybe it would need to put it in try/except because someone may put text instead of numbers.

    Second:

    If you want to get data x,y from Scatter then you should get fig.data[0].x, fig.data[0].y but I would get directly x2, y2


    And another problem: you have to check if you have the same number of values x and values y because someone may delete/add only x and forget y

    if len(x2) == len(y2):
    

    Other idea: you can replace values in Scatter without creating it again

        #fig.data = []
        #fig.add_trace(go.Scatter(x=x2, y=y2))
    
        fig.data[0].x = x2
        fig.data[0].y = y2
    

    Full working code:

    from nicegui import ui
    import plotly.graph_objects as go
    import numpy as np
    
    coords = {'x': [], 'y': []}
    
    ui.input(label='X',value=[1, 2, 3, 4]).bind_value_to(coords,'x')
    ui.input(label='Y',value=[1, 2, 3, 4]).bind_value_to(coords,'y')
    
    fig = go.Figure(go.Scatter(x=coords['x'], y=coords['y']))
    fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))
    plot = ui.plotly(fig).classes('w-full h-40')
    
    def update_plt():
        #fig.data = []
        x2 = coords['x']
        y2 = coords['y']
    
        if isinstance(x2, str):
            x2 = [int(x) for x in x2.split(',') if x.strip()]  # use strip to skip empty places
        if isinstance(y2, str):
            y2 = [int(y) for y in y2.split(',') if y.strip()] 
    
        if len(x2) == len(y2):
            #fig.add_trace(go.Scatter(x=x2, y=y2))
            fig.data[0].x = x2
            fig.data[0].y = y2
    
            plot.update()
            print(f"{fig.data = }")
            print(f"{fig.data[0] = }")
            print(f"{fig.data[0].x = }")
            print(f"{fig.data[0].y = }")
    
    
    ui.button('Refresh', on_click=update_plt)
    ui.run()