import datetime as dt import numpy as np import pandas as pd import panel as pn np.random.seed(7) pn.extension('tabulator', css_files=[pn.io.resources.CSS_URLS['font-awesome']]) df = pd.DataFrame({ 'int': [1, 2, 3], 'float': [3.14, 6.28, 9.42], 'str': ['A', 'B', 'C'], 'bool': [True, False, True], 'date': [dt.date(2019, 1, 1), dt.date(2020, 1, 1), dt.date(2020, 1, 10)], 'datetime': [dt.datetime(2019, 1, 1, 10), dt.datetime(2020, 1, 1, 12), dt.datetime(2020, 1, 10, 13)] }, index=[1, 2, 3]) df_widget = pn.widgets.Tabulator(df, buttons={'Print': ""}) df_widget from bokeh.models.widgets.tables import NumberFormatter, BooleanFormatter bokeh_formatters = { 'float': NumberFormatter(format='0.00000'), 'bool': BooleanFormatter(), } pn.widgets.Tabulator(df, formatters=bokeh_formatters) tabulator_formatters = { 'float': {'type': 'progress', 'max': 10}, 'bool': {'type': 'tickCross'} } pn.widgets.Tabulator(df, formatters=tabulator_formatters) from bokeh.models.widgets.tables import CheckboxEditor, NumberEditor, SelectEditor, DateEditor, TimeEditor bokeh_editors = { 'float': NumberEditor(), 'bool': CheckboxEditor(), 'str': SelectEditor(options=['A', 'B', 'C', 'D']), } pn.widgets.Tabulator(df[['float', 'bool', 'str']], editors=bokeh_editors) tabulator_editors = { 'int': None, 'float': {'type': 'number', 'max': 10, 'step': 0.1}, 'bool': {'type': 'tickCross', 'tristate': True, 'indeterminateValue': None}, 'str': {'type': 'autocomplete', 'values': True}, 'date': 'date', 'datetime': 'datetime' } edit_table = pn.widgets.Tabulator(df, editors=tabulator_editors) edit_table edit_table.on_edit(lambda e: print(e.column, e.row, e.old, e.value)) custom_df = pd._testing.makeMixedDataFrame().iloc[:3, :] pn.widgets.Tabulator(custom_df, widths={'index': 70, 'A': 50, 'B': 50, 'C': 70, 'D': 130}) pn.widgets.Tabulator(custom_df, widths=130) pn.widgets.Tabulator(custom_df, widths={'index': '5%', 'A': '15%', 'B': '15%', 'C': '25%', 'D': '40%'}, sizing_mode='stretch_width') pn.widgets.Tabulator(custom_df, layout='fit_data', width=400) pn.widgets.Tabulator(custom_df, layout='fit_data_stretch', width=400) pn.widgets.Tabulator(custom_df, layout='fit_data_fill', width=400) pn.widgets.Tabulator(custom_df, layout='fit_data_table') pn.widgets.Tabulator(custom_df, layout='fit_columns', width=650) pn.widgets.Tabulator(df.iloc[:, :2], header_align='center', text_align={'int': 'center', 'float': 'left'}, widths=150) style_df = pd.DataFrame(np.random.randn(4, 5), columns=list('ABCDE')) styled = pn.widgets.Tabulator(style_df) def color_negative_red(val): """ Takes a scalar and returns a string with the css property `'color: red'` for negative strings, black otherwise. """ color = 'red' if val < 0 else 'black' return 'color: %s' % color def highlight_max(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] styled.style.applymap(color_negative_red).apply(highlight_max) styled sel_df = pd.DataFrame(np.random.randn(3, 5), columns=list('ABCDE')) select_table = pn.widgets.Tabulator(sel_df, selection=[0, 2]) select_table select_table.selection = [1] select_table.selected_dataframe pn.widgets.Tabulator(sel_df, selection=[0, 2], selectable='checkbox') select_table = pn.widgets.Tabulator(sel_df, selectable_rows=lambda df: list(range(0, len(df), 2))) select_table def click(event): print(f'Clicked cell in {event.column!r} column, row {event.row!r} with value {event.value!r}') select_table.on_click(click) # Optionally we can also limit the callback to a specific column # select_table.on_click(click, column='A') wide_df = pd._testing.makeCustomDataframe(3, 10, r_idx_names=['index']) pn.widgets.Tabulator(wide_df, frozen_columns=['index'], width=400) date_df = pd._testing.makeTimeDataFrame().iloc[:5, :2] agg_df = pd.concat([date_df, date_df.median().to_frame('Median').T, date_df.mean().to_frame('Mean').T]) agg_df.index= agg_df.index.map(str) pn.widgets.Tabulator(agg_df, frozen_rows=[-2, -1], height=200) from bokeh.sampledata.periodic_table import elements periodic_df = elements[['atomic number', 'name', 'atomic mass', 'metal', 'year discovered']].set_index('atomic number') content_fn = lambda row: pn.pane.HTML( f'', sizing_mode='stretch_width' ) periodic_table = pn.widgets.Tabulator( periodic_df, height=350, layout='fit_columns', sizing_mode='stretch_width', row_content=content_fn, embed_content=True ) periodic_table periodic_table.expanded pn.widgets.Tabulator(date_df.iloc[:3], groups={'Group 1': ['A', 'B'], 'Group 2': ['C', 'D']}) from bokeh.sampledata.autompg import autompg pn.widgets.Tabulator(autompg, groupby=['yr', 'origin'], height=240) from bokeh.sampledata.population import data as population_data pop_df = population_data[population_data.Year == 2020].set_index(['Location', 'AgeGrp', 'Sex'])[['Value']] pn.widgets.Tabulator(value=pop_df, hierarchical=True, aggregators={'Sex': 'sum', 'AgeGrp': 'sum'}, height=200) large_df = pd._testing.makeCustomDataframe(100000, 5) pn.widgets.Tabulator(large_df, pagination='remote', page_size=3) medium_df = pd._testing.makeCustomDataframe(1000, 5) pn.widgets.Tabulator(medium_df, pagination='local', page_size=3) filter_table = pn.widgets.Tabulator(pd._testing.makeMixedDataFrame()) filter_table filter_table.add_filter((0, 3), 'A') slider = pn.widgets.RangeSlider(start=0, end=3, name='A Filter') filter_table.add_filter(slider, 'A') select = pn.widgets.MultiSelect(options=['foo1', 'foo2', 'foo3', 'foo4', 'foo5'], name='C Filter') filter_table.add_filter(select, 'C') pn.Row( pn.Column(slider, select), filter_table ) select.value = ['foo1', 'foo2'] filter_table.current_view import sqlite3 from bokeh.sampledata.movies_data import movie_path con = sqlite3.Connection(movie_path) movies_df = pd.read_sql('SELECT Title, Year, Genre, Director, Writer, imdbRating from omdb', con) movies_df = movies_df[~movies_df.Director.isna()] movies_table = pn.widgets.Tabulator(movies_df, pagination='remote', page_size=4) director_filter = pn.widgets.TextInput(name='Director filter', value='Chaplin') def contains_filter(df, pattern, column): if not pattern: return df return df[df[column].str.contains(pattern)] movies_table.add_filter(pn.bind(contains_filter, pattern=director_filter, column='Director')) pn.Row(director_filter, movies_table) tabulator_editors = { 'float': {'type': 'number', 'max': 10, 'step': 0.1}, 'bool': {'type': 'tickCross', 'tristate': True, 'indeterminateValue': None}, 'str': {'type': 'autocomplete', 'values': True} } header_filter_table = pn.widgets.Tabulator( df[['float', 'bool', 'str']], height=140, width=400, layout='fit_columns', editors=tabulator_editors, header_filters=True ) header_filter_table header_filter_table.filters movie_filters = { 'Title': {'type': 'input', 'func': 'like', 'placeholder': 'Enter title'}, 'Year': {'placeholder': 'Enter year'}, 'Genre': {'type': 'input', 'func': 'like', 'placeholder': 'Enter genre'}, 'Director': {'type': 'input', 'func': 'like', 'placeholder': 'Enter director'}, 'Writer': {'type': 'input', 'func': 'like', 'placeholder': 'Enter writer'}, 'imdbRating': {'type': 'number', 'func': '>=', 'placeholder': 'Enter minimum rating'} } filter_table = pn.widgets.Tabulator( movies_df, pagination='remote', layout='fit_columns', page_size=4, sizing_mode='stretch_width', header_filters=movie_filters ) filter_table download_df = pd.DataFrame(np.random.randn(4, 5), columns=list('ABCDE')) download_table = pn.widgets.Tabulator(download_df) filename, button = download_table.download_menu( text_kwargs={'name': 'Enter filename', 'value': 'default.csv'}, button_kwargs={'name': 'Download table'} ) pn.Row( pn.Column(filename, button), download_table ) button_table = pn.widgets.Tabulator(df, buttons={ 'print': '', 'check': '' }) string = pn.widgets.StaticText() button_table.on_click( lambda e: string.param.update(value=f'Clicked {e.column!r} on row {e.row}') ) pn.Row(button_table, string) stream_df = pd.DataFrame(np.random.randn(5, 5), columns=list('ABCDE')) stream_table = pn.widgets.Tabulator(stream_df, layout='fit_columns', width=450, height=400) stream_table def stream_data(follow=True): stream_df = pd.DataFrame(np.random.randn(5, 5), columns=list('ABCDE')) stream_table.stream(stream_df, follow=follow) pn.state.add_periodic_callback(stream_data, period=1000, count=5); stream_data(follow=False) patch_table = pn.widgets.Tabulator(df[['int', 'float', 'str', 'bool']]) patch_table patch_table.patch({ 'bool': [ (0, False), (2, False) ], 'int': [ (slice(0, 2), [3, 2]) ] }) df = pd.DataFrame({ 'int': [1, 2, 3], 'float': [3.14, 6.28, 9.42], 'str': ['A', 'B', 'C'], 'bool': [True, False, True], 'date': [dt.date(2019, 1, 1), dt.date(2020, 1, 1), dt.date(2020, 1, 10)] }, index=[1, 2, 3]) pn.widgets.Tabulator(df, configuration={ 'resizableColumns': False, 'headerSort': False })