Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] visible property for TableColumns #11423

Closed
charlesbluca opened this issue Jul 15, 2021 · 2 comments · Fixed by #11501
Closed

[FEATURE] visible property for TableColumns #11423

charlesbluca opened this issue Jul 15, 2021 · 2 comments · Fixed by #11501

Comments

@charlesbluca
Copy link

Is your feature request related to a problem? Please describe.

I am trying to use a CheckboxButtonGroup widget to toggle the visibility of columns in a DataTable. My approach to this is to use some CustomJS that adds or removes columns from the table in question upon clicking the button:

# example.py

from datetime import date
from random import randint

from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import (
        ColumnDataSource,
        DataTable,
        DateFormatter,
        TableColumn,
        CheckboxButtonGroup,
        CustomJS
    )
from bokeh.plotting import curdoc

data = dict(
        dates=[date(2014, 3, i+1) for i in range(10)],
        downloads=[randint(0, 100) for i in range(10)],
    )
source = ColumnDataSource(data)

columns = [
        TableColumn(field="dates", title="Date", formatter=DateFormatter()),
        TableColumn(field="downloads", title="Downloads"),
    ]
data_table = DataTable(source=source, columns=columns, width=400, height=280)

column_choice = CheckboxButtonGroup(
    labels=[
        "Date",
        "Downloads"
    ],
    active=[0, 1],
)
column_choice.js_on_click(
    CustomJS(
        args=dict(
            table=data_table, columns=columns
        ),
        code="""
            var visible_columns = []
            for (var i = 0; i < this.active.length; i++) {
                visible_columns.push(columns[this.active[i]])
            }
            table.columns = visible_columns;
        """,
    )
)

curdoc().add_root(column(column_choice, data_table))
$ bokeh serve --show example.py

This works fine, and the table responds as expected. However, I get serialization errors in console every time I click the button - I reported this bug in #11422.

Describe the solution you'd like

An alternative method to toggle column visibility would be to have a visible property on TableColumns, similar to the one for DataTables. This would allow me to toggle the visibility of the columns using this property, which would be simpler than what I am currently doing and would potentially circumvent the serialization errors I encountered in #11422.

Describe alternatives you've considered
An alternative suggested by @bryevdv to try and avoid the serialization errors was to use a hidden DataTable in the document as a "bank" for the columns I want to toggle on and off for the main table. I tried this approach out but encountered the same serialization errors.

Additional context
This issue came up while working on dask/distributed#4614.

@bryevdv
Copy link
Member

bryevdv commented Jul 16, 2021

Here is a minimal POC diff that gets this minimally working

base ❯ git diff
diff --git a/bokeh/models/widgets/tables.py b/bokeh/models/widgets/tables.py
index df9bd0ea0..44f490646 100644
--- a/bokeh/models/widgets/tables.py
+++ b/bokeh/models/widgets/tables.py
@@ -618,6 +618,10 @@ class TableColumn(Model):
     The default sorting order. By default ``ascending`` order is used.
     """)

+    visible = Bool(True, help="""
+    Whether this column shold be displayed or not.
+    """)
+
 @abstract
 class TableWidget(Widget):
     ''' Abstract base class for data table (data grid) widgets.
diff --git a/bokehjs/src/lib/models/widgets/tables/data_table.ts b/bokehjs/src/lib/models/widgets/tables/data_table.ts
index 0c273d4b2..9cebedfca 100644
--- a/bokehjs/src/lib/models/widgets/tables/data_table.ts
+++ b/bokehjs/src/lib/models/widgets/tables/data_table.ts
@@ -136,6 +136,10 @@ export class DataTableView extends WidgetView {

     this.connect(this.model.source.selected.change, () => this.updateSelection())
     this.connect(this.model.source.selected.properties.indices.change, () => this.updateSelection())
+
+    for (const column of this.model.columns) {
+      this.connect(column.change, () => this.render())
+    }
   }

   override remove(): void {
@@ -254,7 +258,7 @@ export class DataTableView extends WidgetView {
   }

   override render(): void {
-    const columns: ColumnType[] = this.model.columns.map((column) => {
+    const columns: ColumnType[] = this.model.columns.filter((column) => column.visible).map((column) => {
       return {...column.toColumn(), parent: this}
     })

diff --git a/bokehjs/src/lib/models/widgets/tables/table_column.ts b/bokehjs/src/lib/models/widgets/tables/table_column.ts
index 22df913c0..18ed904da 100644
--- a/bokehjs/src/lib/models/widgets/tables/table_column.ts
+++ b/bokehjs/src/lib/models/widgets/tables/table_column.ts
@@ -18,6 +18,7 @@ export namespace TableColumn {
     editor: p.Property<CellEditor>
     sortable: p.Property<boolean>
     default_sort: p.Property<Sort>
+    visible: p.Property<boolean>
   }
 }

@@ -39,6 +40,7 @@ export class TableColumn extends Model {
       editor:       [ Ref(StringEditor), () => new StringEditor() ],
       sortable:     [ Boolean, true ],
       default_sort: [ Sort, "ascending" ],
+      visible:      [ Boolean, true ],
     }))
   }

Some issues:

  • This won't respond by connecting or disconnecting signals if columns itself is modified (but if visible works the best pattern would be to add all columns up front and manage visibility anyway)
  • This doesn't currently seem to update layout in "fit columns" mode

Here is a minimal example to test with


from bokeh.io import curdoc
from bokeh.layouts import row
from bokeh.models import ColumnDataSource, DataTable, TableColumn, Toggle
from bokeh.plotting import figure

x=[3,4,6,12,10,1]
y=[7,1,3,4,1,6]

source = ColumnDataSource(data=dict(x=x, y=y))

plot_figure = figure(title='Data-Table',height=450, width=600,
              tools="save,reset", toolbar_location="below")

plot_figure.scatter('x', 'y', source=source, size=10)

columns = [
    TableColumn(field="x", title="x"),
    TableColumn(field="y", title="y")
]
data_table = DataTable(
    source=source,
    columns=columns,
    index_position=None,
    # width=80,
    autosize_mode="fit_columns",
    editable=True,
)

toggle = Toggle()

def cb(attr, old, new):
    columns[0].visible = toggle.active

toggle.on_change('active', cb)

layout=row(plot_figure, data_table, toggle)

curdoc().add_root(layout)
curdoc().title = "Data-Table Bokeh Server"

@bryevdv
Copy link
Member

bryevdv commented Aug 11, 2021

Try: invalidate_layout

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants