Panel table doesn't update

Hi , all.

Now I am making with a DataTable and scatter in FastListTemplate. at this time . I want change DataTable value when I push random button. Now , When I push the button , the value changed at Graph tab, But doesn’t change Datatable.

Here is a very simplified version of my code. When I click on the button, I expect graph value and DataTable value changes. But now Graph value changes. so It seems to have worked , however, the Table still show original value ← [This is my problem].

Can you help me find what I’m missing ?

##[My Simple code]

import pandas as pd;
import panel as pn
from bokeh.plotting import ColumnDataSource, figure
import param
from panel.template import DarkTheme,DefaultTheme
import random
pn.extension()
# import hvplot.pandas
source = ColumnDataSource(
    data=dict(
        v1=list(range(0,10,1)),
        v2=list(range(0,20,2)),
        v3=list(range(0,30,3)),
        v4=list(range(0,40,4)),
    )
)
x_sel = pn.widgets.Select(value='v1', options=['v1','v2','v3','v4'], name='x')
y_sel = pn.widgets.Select(value='v2', options=['v1','v2','v3','v4'], name='y')
button_random  = pn.widgets.Button(name='random')
First_Flag = True
State_value = 0     # State --- 0 (Graph,List)
# ----------------------------------------------------------------------
#   Selcetion Table
# ----------------------------------------------------------------------
@pn.depends()
class ReactiveTable(param.Parameterized):
    table = param.DataFrame(pd.DataFrame(source.data).T)
    v1 = param.Number()    
    def __init__(self, **params):
        super().__init__(**params)
        self.table_widget = pn.Param(self.param.table)[0]
    @param.depends('table_widget.selection')
    def update_params_with_selected_row_values(self , fst = First_Flag ):
        param_test = self.table_widget.selection
        if len(param_test) != 0 :
            if First_Flag == False :
                if  template.main[0].active == 1 :
                    self.v1 += 1
                    print ('20211118 Table list selection: ', param_test,x_sel.options[param_test[0]], 'exe ' , self.v1 , 'times')
                    x_sel.value = x_sel.options[param_test[0]]
                    change_tab(0)        
        return self.table_widget.selection  
rt = ReactiveTable()
# ----------------------------------------------------------------------
#   index search
# ----------------------------------------------------------------------
def search_index(lst, value):
    return [i for i, x in enumerate(lst) if x == value]
# ----------------------------------------------------------------------
#   Graph tab
# ----------------------------------------------------------------------
@pn.depends(x_sel,y_sel)
def autompg_plot(xval, yval, fst = First_Flag ):
    if First_Flag == False :
        if  template.main[0].active == 0 :
            rt.table_widget.selection =search_index(x_sel.options,xval)
            print ('20211118 Graph selection: ',rt.table_widget.selection)  
            # change_tab(1)  
    p1 = figure(width=350, height=350, name='Scatter',title='Graph1' , title_location = 'below')
    p1.scatter(x=x_sel.value , y=y_sel.value, source=source, line_alpha=1, color="red")
    return p1
# ----------------------------------------------------------------------
#   Table tab
# ----------------------------------------------------------------------
@pn.depends()
def test_list():
    ret_view = pn.Column(pn.Row(rt.param.v1),rt.table_widget, rt.update_params_with_selected_row_values)
    return ret_view
#---------------------------------------------------------------------------------------------------#
# Treat random button                                                                               #
#---------------------------------------------------------------------------------------------------#
def click_random(click):
    rand_num = random.randint(5,10)
    r_v1=[]
    r_v2=[]
    r_v3=[]
    r_v4=[]
    for i in range(rand_num) :
        r_v1.append(random.randint(1,100))
        r_v2.append(random.randint(1,100))
        r_v3.append(random.randint(1,100))
        r_v4.append(random.randint(1,100))
    replace_df = pd.DataFrame({'v1':r_v1,'v2':r_v2,'v3':r_v3,'v4':r_v4})
    source.data.update(replace_df)  
button_random .on_click(click_random)
# ----------------------------------------------------------------------
#   main screen
# ----------------------------------------------------------------------
select_tab = 0
def main_tabs(sel_tab = select_tab):
    ret_main_tabs = pn.Tabs(
        ('Graph', pn.Row(pn.Column('## Table_Update', x_sel, y_sel), autompg_plot)),
        ('List', test_list),
        ('Random',button_random),
        )
    ret_main_tabs.active = sel_tab
    return ret_main_tabs
# ----------------------------------------------------------------------
#   make template and run server
# ----------------------------------------------------------------------
template = pn.template.FastListTemplate(
    title='Panel Sample',
    site='Panel',
    main=main_tabs(select_tab),
    theme = DarkTheme ,
    theme_toggle = True ,
)
# template.servable();
# ----------------------------------------------------------------------
#   index search
# ----------------------------------------------------------------------
def change_tab(tab_number):
    template.main[0].active = tab_number
First_Flag = False
pn.serve(template)

##Screen Shot 1 [Initial condition]

##Screen Shot 2 [After click random button. Graph has changed, But DataTable hasn’t changed]

You have to update the rt.table when you call click_random like this:

def click_random(click):
    ...   #  Same as your example
    source.data.update(replace_df)
    rt.table = replace_df
1 Like

I think a lot of the problems could be related to you using a lot of different methods instead of one, like a Parameterized class, bokeh’s ColumnDataSource, panel functions and global values. I would stick with one method like this:

import hvplot.pandas  # noqa
import numpy as np
import pandas as pd
import panel as pn
import param

INDICES = ["v1", "v2", "v3", "v4"]


class ReactiveTable(pn.viewable.Viewer):
    table = param.DataFrame()
    count = param.Integer()
    random = param.Action(lambda x: x.param.trigger("random"), label="Random")
    x = param.ObjectSelector(INDICES[0], objects=INDICES, label="x")
    y = param.ObjectSelector(INDICES[1], objects=INDICES, label="y")
    table_widget = param.Parameter()  # to get table_widget.selection to work in depends

    def __init__(self, **params):
        super().__init__(**params)
        self.table_widget = pn.Param(self.param.table)[0]

    @param.depends("random", watch=True, on_init=True)
    def _fill_table_with_random_data(self):
        size = np.random.randint(5, 10)
        self.table = pd.DataFrame(
            {i: np.random.randint(1, 100, size) for i in INDICES}
        ).T

    @param.depends("table_widget.selection", watch=True)
    def _set_x_with_table_selection(self):
        if self.table_widget.selection:
            self.x = INDICES[self.table_widget.selection[0]]
        else:
            self.x = INDICES[0]

        self.count += 1

    @param.depends("x", watch=True)
    def _set_table_selection_with_x(self):
        self.table_widget.selection = [INDICES.index(self.x)]

    @param.depends("x", "y", "random")
    def plot(self):
        return self.table.T.hvplot.scatter(
            x=self.x, y=self.y, color="red", grid=True, xlim=(0, 100), ylim=(0, 100)
        )

    def __panel__(self):
        # Layout
        graph_layout = pn.Row(
            pn.Column(
                pn.pane.Markdown("## Update table"),
                self.param.x,
                self.param.y,
                self.param.random,
            ),
            pn.panel(self.plot, sizing_mode="scale_both"),
        )
        list_layout = pn.Column(self.table_widget, self.param.count, self.param.random)

        self.tabs = pn.Tabs(
            ("Graph", graph_layout),
            ("List", list_layout),
            active=self.tabs.active if hasattr(self, "tabs") else 0,
        )

        return pn.template.FastListTemplate(
            site="Panel",
            main=self.tabs,
            title="Panel Sample",
            theme="dark",
        )


if __name__ == "__main__":
    app = ReactiveTable()
    app.show(port=5007)
elif __name__.startswith("bokeh"):
    app = ReactiveTable()
    app.servable()

1 Like

Thank you for your quick replay and teach sophisticated code. I think I want to make my code like as you. However I execute your code. I met a different error. [The depends decorator only accepts string types referencing a parameter or parameter instances, found bool type instead]

this error makes senses, because I use bool type parameter ( watch and on_init ). I want to delete this error , could you teach me one thing? I want to make my code one method like as you.

I’m sorry for bothering you . But I want why the error occur in my condition.


@Hoxbro
This error was caused by my param version. I update param version from 1.10.1 to 1.12.0. Error disappeared. Thanks for everything. I aim I have to use one method.

2 Likes

@Hoxbro
I’m sorry for bothering you. I have one more question. I want to change number of items each random button clicks. when we can see update table the number is less than first. but we can’t see update table all when the number is more than first. I want to see all data table no matter what the number of samples.

[code modify points]

[First state : number of items 6]

[Second State : number of items 5 : we can see all data table]

20211206_006_OK

[Third State : number of items 9 : we can’t see all data table]

Could you teach me what I need to change?

I don’t think it is possible or at least easy to do. Try take a look at: https://github.com/holoviz/panel/issues/2854

1 Like

Great Thanks. it works what I wanted.

This is my final code

from bokeh.models.annotations import Title
import hvplot.pandas  # noqa
import numpy as np
import pandas as pd
import panel as pn
import param

INDICES = ["v1", "v2", "v3", "v4"]

#------------------------------------------------------------------------------------
# Definition ReactiveTable Class
#------------------------------------------------------------------------------------
class ReactiveTable(pn.viewable.Viewer):
    table = param.DataFrame()  # x,y table
    count = param.Integer()    # number of items
    random = param.Action(lambda x: x.param.trigger("random"), label="Random")
    x = param.ObjectSelector(INDICES[0], objects=INDICES, label="x")  # selector x
    y = param.ObjectSelector(INDICES[1], objects=INDICES, label="y")  # selector y
    table_widget = param.Parameter()  # to get table_widget.selection to work in depends

    def __init__(self, **params):
        super().__init__(**params)
        self.table_widget = pn.Param(self.param.table)[0]

    # Definition click random button
    @param.depends("random", watch=True, on_init=True)
    def _fill_table_with_random_data(self):
        size = np.random.randint(5, 15)
        nums = np.random.randint(5, 15)                      # <--- 2021/12/06  Change nums of items 
        self.count = nums                                    # <--- 2021/12/06  Display nums of items
        INDICES = [ str('v'+str(i)) for i in range(nums) ]   # <--- 2021/12/06  Make a new indices list
        self.table = pd.DataFrame(
            {i: np.random.randint(1, 100, size) for i in INDICES}
        ).T
        self.param.x.objects = INDICES   # change selector x lists
        self.param.y.objects = INDICES   # change selector y lists

    # Definition click table widget
    @param.depends("table_widget.selection", watch=True)
    def _set_x_with_table_selection(self):
        OBJECT = (self.param.x.objects)
        if self.table_widget.selection:
            self.x = OBJECT[self.table_widget.selection[0]]
            if hasattr(self, "tabs") : self.tabs.active = 0  # if it is not initial , self has tabs objects. so change active tab=0 
        else:
            self.x = OBJECT[0]
    
        # self.count += 1 # <--- 2021/12/06 Comment out

    # Definition select x value
    @param.depends("x", watch=True)
    def _set_table_selection_with_x(self):
        OBJECT = (self.param.x.objects)
        self.table_widget.selection = [OBJECT.index(self.x)]

    # Definition select x,y value or click random button
    @param.depends("x", "y", "random")
    def plot(self):
        return self.table.T.hvplot.scatter(
            x=self.x, y=self.y, color="red", grid=True, xlim=(0, 100), ylim=(0, 100) 
        ).opts(title=str(self.table[0][0]))

    # Definition update data table
    @param.depends("table")
    def table_list(self):
        return pn.pane.DataFrame(self.table, sizing_mode="fixed")

    # Definition panel layout
    def __panel__(self):
        # Layout
        graph_layout = pn.Row(
            pn.Column(
                pn.pane.Markdown("## Update table"),
                self.param.x,
                self.param.y,
                self.param.random,
            ),
            pn.panel(self.plot, sizing_mode="fixed"),
        )
        list_layout = pn.Column(self.table_widget, self.param.count, self.param.random)

        self.tabs = pn.Tabs(
            ("Graph", graph_layout),
            ("List", list_layout),
            active=self.tabs.active if hasattr(self, "tabs") else 0,
        )

        return pn.template.FastListTemplate(
            site="Panel",
            main=self.tabs,
            title="Panel Sample",
            theme="dark",
        )

# run app
if __name__ == "__main__":
    app = ReactiveTable()
    app.show(port=5007)
elif __name__.startswith("bokeh"):
    app = ReactiveTable()
    app.servable()
3 Likes