@param.depends watch=True not working Part2

Hi, all. I use param. And I want to change graph axis when I click the table. the situation is below.

  1. display the graph and change screen from graph to table.
  2. I clicked the table But it is no response.
  3. I modified the table.
  4. I clicked the table , too . the graph axis has changed. ( It works well )

I want to know , when I am in 2 situation (I clicked the table But it is no response. ) , change the graph axis. What should I modify the code? Could someone tell me?

I have read the related document (below) . But I couldn’t figure out my problem.
Someone help me?
@param.depends with watch=True not working - Param - HoloViz Discourse

Screen shot 1 select v2 , But didn’t change graph x-axis

20220309_0001

Screen shot 2 change the table value

20220309_0002

Screen shot 3 click again . graph axis was changed . It works fine.

Screen shot 4 graph axis was changed . It works fine.

My simple code

from typing import Any
from bokeh.models.annotations import Title
import hvplot.pandas  # noqa
import numpy as np
import pandas as pd
import panel as pn
import param
import holoviews as hv
from numpy.random import *

###  FLAG_FIRST =  {'first' : 0, 'string' : '---'} # Progress bar 計算用
###  FLAG_FIRST['first'] = 1

# https://discourse.holoviz.org/t/formating-text-in-a-python-panel/1291

pn.extension('tabulator')

INDICES = ["v1", "v2", "v3", "v4"]
memory = set([])
#------------------------------------------------------------------------------------
# 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
    int_list                = param.ListSelector(default=[3, 5], objects=[1, 3, 5, 7, 9], precedence=0.5)    
    Autocomplete_area = pn.widgets.AutocompleteInput(name='Autocomplete Input', options=INDICES, placeholder='Write something here')    
    table_widget =  pn.widgets.Tabulator()
    Toggle_tf = param.Boolean() # <--- 20211220 add Toggle_tf param
    from holoviews import opts
    from holoviews import streams   
    selection = streams.Selection1D()   
    memory = set([])    
    first_flag = False ## Tap First Flag   

    def __init__(self, **params):
        super().__init__(**params)
        self.table_widget = pn.widgets.Tabulator.from_param(self.param.table)        
        self.table_widget.style.applymap(self.color_negative_red,subset='T3').apply(self.highlight_max,subset='T3')
        self.Toggle = pn.widgets.Toggle.from_param(self.param.Toggle_tf,name='Push Toggle', button_type='default') # <<-- 20211220 add button_widget  

        # https://discourse.holoviz.org/t/param-depends-with-watch-true-not-working/921/3
        # This works fine for me as long as you call super in the constructor:

        # https://pandas.pydata.org/pandas-docs/stable/user_guide/style.html 色の付け方等
        # https://panel.holoviz.org/api/panel.widgets.html?highlight=tabulator#panel.widgets.tables.Tabulator
        # https://panel.holoviz.org/reference/widgets/Tabulator.html#widgets-gallery-tabulator

    def color_negative_red(self,val):
        """
        Takes a scalar and returns a string with
        the css property `'color: red'` for negative
        strings, black otherwise.
        """
        color = 'red' if val > 60 else 'blue'
        return 'color: %s' % color

    def highlight_max(self,s):
        '''
        highlight the maximum in a Series yellow.
        '''
        is_max = s == s.max()
        return ['background-color: yellow' if v else '' for v in is_max]

    def change_data(self) :
        size = np.random.randint(10, 15)
        nums = np.random.randint(5, 10)                      # <--- 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
        test = pd.DataFrame(
        {i: np.arange(1, 100, size) for i in INDICES}) # numpy.arange([start, ]stop, [step, ]dtype = None)
        for i in range(len(test)) :
            for j in range(len(INDICES)) :
                test.iloc[i][j]=test.iloc[i][j]*(rand()+1)+np.random.randint(15)
        TEST = [ str('T'+str(i)) for i in range(len(test)) ]   # <--- 2021/12/06  Make a new indices list 
        temp_A = test.T
        temp_A.columns = TEST
        self.table = temp_A
        self.param.x.objects = INDICES   # change selector x lists
        self.param.y.objects = INDICES   # change selector y lists
        self.Autocomplete_area.options = INDICES
        print ('change_data')

    # Definition click random button
    #@param.depends("random", watch=True, on_init=True)
    @param.depends('Toggle_tf',watch=True, on_init=True)
    def _fill_table_with_random_data(self):
        if hasattr(self, "tabs") : 
            self.memory=[]
            self.first_flag = True                 
            if self.Toggle_tf == True :       
                self.change_data()
                self.Toggle.name = 'Finish'
                self.Toggle.button_type = 'primary'           
            else :
                self.Toggle.name = 'Push Toggle'
                self.Toggle.button_type = 'default'
                 
        else :
            self.change_data()

    # 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)]
        self.Autocomplete_area.value = self.x
        self.memory=[]  
        self.first_flag = True            

    @param.depends("Autocomplete_area.value",watch=True)
    def _set_table_selection_with_Autocomplete_area(self):
        OBJECT = (self.Autocomplete_area.options)
        sel = self.Autocomplete_area.value
        if (sel in OBJECT) : 
            self.table_widget.selection = [OBJECT.index(self.Autocomplete_area.value)]
            self.memory=[]
            self.first_flag = True              

    # Definition select x,y value or click random button
#    @param.depends("x", "y", "random")
    @param.depends("x", "y", "selection.index" , 'Toggle_tf')
    def plot(self):        
        index = self.selection.index
        if self.first_flag == True or self.Toggle_tf == True :
            index = []
            self.first_flag = False
        print ('debug', index)
        df = pd.DataFrame ({"x": self.table.loc[self.x], "y": self.table.loc[self.y]})
        for i in index:
            self.memory.append(i)  # add previous indices
        df2 = df.loc[~df.index.isin(self.memory)]  # remove all past indices  
        new_graph = hv.Scatter(df2, "x", "y").opts(size=5, color="yellow")  # plot              
        #相関係数を計算
        x_corr = df2['x'].to_numpy().tolist() 
        y_corr = df2['y'].to_numpy().tolist()
        # 相関行列を計算
        coef = np.corrcoef(x_corr, y_corr)
        # 相関行列を表示
        print('20220105 Debug correlation value : ',coef)
        a, b = np.polyfit(x_corr, y_corr, 1)  
        if b>0 :
            plot_title = 'Rxy='+str(coef[0][1])[0:4] + ' : y='+str(a)[0:5]+'x+'+str(b)[0:5] 
        else :
            plot_title = 'Rxy='+str(coef[0][1])[0:5] + ' : y='+str(a)[0:5]+'x'+str(b)[0:6]               
        graph_1 = self.table.T.hvplot.scatter(
            x=self.x, y=self.y, color="red", grid=True, xlim=(0, 100), ylim=(0, 100) 
        ).opts(toolbar='right',size=8, title=plot_title, tools=['tap','box_select', 'lasso_select'])
        graph_2 =  hv.Slope(a, b).opts(color='green', line_width=4 , line_dash='dashed')
        # Declare points as source of selection stream   
        self.selection.source=graph_1        

        plot = graph_1 * graph_2 * new_graph
        return plot

    # Definition update data table
    @param.depends("table")
    def table_list(self):
        ret_table = pn.pane.DataFrame(self.table.style.applymap(self.color_negative_red).apply(self.highlight_max))
        return ret_table

    # https://holoviews.org/reference/apps/bokeh/selection_stream.html
    # https://holoviews.org/reference/containers/bokeh/DynamicMap.html

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

        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()

Could some tell me how to change graph-axis when I click the table first.

Best regards,

Hi @kazux68k,

I would go back to the original code supplied and add the modifcations piece by piece till it breaks. I made a start with just changing the table to tabulator and that functions as you would like (I think) as on startup I’m able to go directly to the table, highlight a row and see the graph update so I think something else does not appear to be getting initialised on startup from the modifications made.

Thanks, Carl.

1 Like

Thank you for checking my code. I upload my situation. at first , I clicked v3 . but screen didn’t changed. but After changing [v5,T1] value and clicked v1, I can change screen from table to graph. I want to change screen when I clicked v3.

But it your means you can change first click? If you can work fine, may be my version issue??
my version is below.
panel 0.12.6
param 1.12.0

At first , Can I double check your situation? Can I take you can change from table to graph first attempt?

Best regards,

Hi @kazux68k,

I’m not at my machine, what I done was went right back to the original post part1. The code there uses table rather than tabulator.

I checked to see if it had the functionality you were looking for it seems it does. I made a start with changing it to tabulator just like you have but without all the other modifications and the functionality remained consistent with part1. So I suspect a further change maybe with the auto input or the addition of the line may also be playing a part in the issue your experiencing. If you go back to code part 1 and build in your changes one at a time you’ll hit the issue I think. I don’t think it is a versioning problem as when I ran your latest code what you describe seems pretty accurate only addition I can add there is if you load and hit the toggle button then it all works. So I mean that’s what I’d do go back to where it was all working, change a bit at a time see where it starts to break down.

1 Like

Hi @carl , Thank you for your quick response. I had no problem when I use Dataframe ( How do I delete my selected point dynamically from other points in a mapview using holoviews Datalink - HoloViews - HoloViz Discourse
But I want to add color to Dataframe , so I changed from Datatable to tabulator. And I met this problem. At first I want to change my modifications to Part I code. And there is some problem. I confirm you again.
you are very kind to me. Thanks well

Best regards,

I tried Part I , I works well. And I delete watch = true , it doesn’t work.
I think so , too. watch = true means watching parameter , so correspond to the value, fill_alpha has changed. below is a short movie.

When I use Dataframe , it is no problem. this problem is caused by tabulator. How do you think about it?
I think I have to make more easily example? How do you think about it?

Best regards,

Hi @kazux68k,

Don’t know if it’s correct but In your main code try reverting

table_widget =  pn.widgets.Tabulator()

to

table_widget = param.Parameter()

and leave everything else the same

3 Likes

Thanks carl. I follow your advice. my code run what I want.

this is the final code

from typing import Any
from bokeh.models.annotations import Title
import hvplot.pandas  # noqa
import numpy as np
import pandas as pd
import panel as pn
import param
import holoviews as hv
from numpy.random import *

###  FLAG_FIRST =  {'first' : 0, 'string' : '---'} # Progress bar 計算用
###  FLAG_FIRST['first'] = 1

# https://discourse.holoviz.org/t/formating-text-in-a-python-panel/1291

pn.extension('tabulator')

INDICES = ["v1", "v2", "v3", "v4"]
memory = set([])
#------------------------------------------------------------------------------------
# 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
    int_list                = param.ListSelector(default=[3, 5], objects=[1, 3, 5, 7, 9], precedence=0.5)    
    Autocomplete_area = pn.widgets.AutocompleteInput(name='Autocomplete Input', options=INDICES, placeholder='Write something here')    
    ## table_widget =  pn.widgets.Tabulator()  # <--- 20220314 carl's suggestion https://discourse.holoviz.org/t/param-depends-watch-true-not-working-part2/3446/6
    table_widget =  param.Parameter() # <--- 20220314 carl's suggestion
    Toggle_tf = param.Boolean() # <--- 20211220 add Toggle_tf param
    from holoviews import opts
    from holoviews import streams   
    selection = streams.Selection1D()   
    memory = set([])    
    first_flag = False ## Tap First Flag   

    def __init__(self, **params):
        super().__init__(**params)
        self.table_widget = pn.widgets.Tabulator.from_param(self.param.table)        
        self.table_widget.style.applymap(self.color_negative_red,subset='T3').apply(self.highlight_max,subset='T3')
        self.Toggle = pn.widgets.Toggle.from_param(self.param.Toggle_tf,name='Push Toggle', button_type='default') # <<-- 20211220 add button_widget  

        # https://discourse.holoviz.org/t/param-depends-with-watch-true-not-working/921/3
        # This works fine for me as long as you call super in the constructor:

        # https://pandas.pydata.org/pandas-docs/stable/user_guide/style.html 色の付け方等
        # https://panel.holoviz.org/api/panel.widgets.html?highlight=tabulator#panel.widgets.tables.Tabulator
        # https://panel.holoviz.org/reference/widgets/Tabulator.html#widgets-gallery-tabulator

    def color_negative_red(self,val):
        """
        Takes a scalar and returns a string with
        the css property `'color: red'` for negative
        strings, black otherwise.
        """
        color = 'red' if val > 60 else 'blue'
        return 'color: %s' % color

    def highlight_max(self,s):
        '''
        highlight the maximum in a Series yellow.
        '''
        is_max = s == s.max()
        return ['background-color: yellow' if v else '' for v in is_max]

    def change_data(self) :
        size = np.random.randint(10, 15)
        nums = np.random.randint(5, 10)                      # <--- 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
        test = pd.DataFrame(
        {i: np.arange(1, 100, size) for i in INDICES}) # numpy.arange([start, ]stop, [step, ]dtype = None)
        for i in range(len(test)) :
            for j in range(len(INDICES)) :
                test.iloc[i][j]=test.iloc[i][j]*(rand()+1)+np.random.randint(15)
        TEST = [ str('T'+str(i)) for i in range(len(test)) ]   # <--- 2021/12/06  Make a new indices list 
        temp_A = test.T
        temp_A.columns = TEST
        self.table = temp_A
        self.param.x.objects = INDICES   # change selector x lists
        self.param.y.objects = INDICES   # change selector y lists
        self.Autocomplete_area.options = INDICES
        print ('change_data')

    # Definition click random button
    #@param.depends("random", watch=True, on_init=True)
    @param.depends('Toggle_tf',watch=True, on_init=True)
    def _fill_table_with_random_data(self):
        if hasattr(self, "tabs") : 
            self.memory=[]
            self.first_flag = True                 
            if self.Toggle_tf == True :       
                self.change_data()
                self.Toggle.name = 'Finish'
                self.Toggle.button_type = 'primary'           
            else :
                self.Toggle.name = 'Push Toggle'
                self.Toggle.button_type = 'default'
                 
        else :
            self.change_data()

    # 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)]
        self.Autocomplete_area.value = self.x
        self.memory=[]  
        self.first_flag = True            

    @param.depends("Autocomplete_area.value",watch=True)
    def _set_table_selection_with_Autocomplete_area(self):
        OBJECT = (self.Autocomplete_area.options)
        sel = self.Autocomplete_area.value
        if (sel in OBJECT) : 
            self.table_widget.selection = [OBJECT.index(self.Autocomplete_area.value)]
            self.memory=[]
            self.first_flag = True              

    # Definition select x,y value or click random button
#    @param.depends("x", "y", "random")
    @param.depends("x", "y", "selection.index" , 'Toggle_tf')
    def plot(self):        
        index = self.selection.index
        if self.first_flag == True or self.Toggle_tf == True :
            index = []
            self.first_flag = False
        print ('debug', index)
        df = pd.DataFrame ({"x": self.table.loc[self.x], "y": self.table.loc[self.y]})
        for i in index:
            self.memory.append(i)  # add previous indices
        df2 = df.loc[~df.index.isin(self.memory)]  # remove all past indices  
        new_graph = hv.Scatter(df2, "x", "y").opts(size=5, color="yellow")  # plot              
        #相関係数を計算
        x_corr = df2['x'].to_numpy().tolist() 
        y_corr = df2['y'].to_numpy().tolist()
        # 相関行列を計算
        coef = np.corrcoef(x_corr, y_corr)
        # 相関行列を表示
        print('20220105 Debug correlation value : ',coef)
        a, b = np.polyfit(x_corr, y_corr, 1)  
        if b>0 :
            plot_title = 'Rxy='+str(coef[0][1])[0:4] + ' : y='+str(a)[0:5]+'x+'+str(b)[0:5] 
        else :
            plot_title = 'Rxy='+str(coef[0][1])[0:5] + ' : y='+str(a)[0:5]+'x'+str(b)[0:6]               
        graph_1 = self.table.T.hvplot.scatter(
            x=self.x, y=self.y, color="red", grid=True, xlim=(0, 100), ylim=(0, 100) 
        ).opts(toolbar='right',size=8, title=plot_title, tools=['tap','box_select', 'lasso_select'])
        graph_2 =  hv.Slope(a, b).opts(color='green', line_width=4 , line_dash='dashed')
        # Declare points as source of selection stream   
        self.selection.source=graph_1        

        plot = graph_1 * graph_2 * new_graph
        return plot

    # Definition update data table
    @param.depends("table")
    def table_list(self):
        ret_table = pn.pane.DataFrame(self.table.style.applymap(self.color_negative_red).apply(self.highlight_max))
        return ret_table

    # https://holoviews.org/reference/apps/bokeh/selection_stream.html
    # https://holoviews.org/reference/containers/bokeh/DynamicMap.html

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

        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()
2 Likes