Hello. I’m trying to get custom keybindings working with Tabulator. The goal is to use up/down arrows to navigate the table and use left/right arrows to change the value of the mark
column between -1,0,1. The code is working but there are a couple issues:
- Left/right arrows change the
mark
column but they also scroll the table. Can the key-based scrolling be disabled?
2. The tabulatorCorrected.configuration
option is not working at all. For example, the header sort is still enabled. Am I using the wrong keywords?
Working example:
import panel as pn
import numpy as np
import pandas as pd
import panel as pn
pn.extension('tabulator', css_files=[pn.io.resources.CSS_URLS['font-awesome']])
script = """
<script>
const doc = window.parent.document;
buttons = Array.from(doc.querySelectorAll('button[type=button]'));
const left_button = buttons.find(el => el.innerText === 'LEFT');
const right_button = buttons.find(el => el.innerText === 'RIGHT');
const down_button = buttons.find(el => el.innerText === 'DOWN');
const up_button = buttons.find(el => el.innerText === 'UP');
doc.addEventListener('keydown', function(e) {
switch (e.keyCode) {
case 37: // (37 = left arrow)
left_button.click();
break;
case 39: // (39 = right arrow)
right_button.click();
break;
case 40: // (40 = down arrow)
down_button.click();
break;
case 38: // (38 = up arrow)
up_button.click();
break;
}
});
</script>
"""
html = pn.pane.HTML(script)
rows = 50
cols = 30
df = pd.DataFrame(
np.random.rand(rows, cols),
columns=[f'col_{i}' for i in range(cols)],
)
df['mark'] = [0] * rows
tab_config = {
# 'columnDefaults': {
# 'resizable': False,
'headerSort': False, # corrected (remove columnDefaults key)
# },
'keybindings': False,
# 'keybindings': {
# "navUp": False,
# "navDown": False,
# "navRight": False,
# "navLeft": False,
# "scrollPageUp": False,
# "scrollPageDown": False,
# },
}
table = pn.widgets.Tabulator(
df,
disabled=True,
selectable=True,
configuration=tab_config,
frozen_columns=['index', 'mark'],
width=600,
height=400,
)
def highlight(row, column):
if row[column] == -1:
return ['background-color: lightcoral'] * len(row)
elif row[column] == 0:
return [''] * len(row)
elif row[column] == 1:
return ['background-color: lightgreen'] * len(row)
table.style.apply(highlight, column='mark', axis=1)
button_left = pn.widgets.Button(name="LEFT")
button_right = pn.widgets.Button(name="RIGHT")
button_down = pn.widgets.Button(name="DOWN")
button_up = pn.widgets.Button(name="UP")
def callback_up(event):
print('callback_up', event)
if len(table.selection) > 0:
row_index = [i - 1 for i in sorted(table.selection)]
if row_index[0] >= 0:
table.selection = row_index
def callback_down(event):
print('callback_down')
if len(table.selection) > 0:
row_index = [i + 1 for i in sorted(table.selection)]
if row_index[-1] < len(df):
table.selection = row_index
def callback_left(event):
print('callback_left', event)
if len(table.selection) > 0:
for i in table.selection:
val = table.value.loc[i, 'mark']
if val > -1:
table.patch({'mark': [(i, val - 1)]})
def callback_right(event):
print('callback_right')
if len(table.selection) > 0:
for i in table.selection:
val = table.value.loc[i, 'mark']
if val < 1:
table.patch({'mark': [(i, val + 1)]})
button_up.on_click(callback_up)
button_down.on_click(callback_down)
button_left.on_click(callback_left)
button_right.on_click(callback_right)
pn.Column(
pn.Column(button_left,
button_left.param.clicks,
button_right,
button_right.param.clicks,
visible=False),
pn.Column(button_up,
button_up.param.clicks,
button_down,
button_down.param.clicks,
visible=False),
html,
table,
).servable()
Thanks to @Hoxbro for the keybinding example.