Streamlit st_aggrid でデータテーブル表 UI の調整メモ

Streamlit で DataFrame widget ありますが,

https://docs.streamlit.io/library/api-reference/data/st.dataframe

あんまり機能ありません.

また, 将来的に他の UI framework 使いたいときになるべく同じの使いたい...

aggrid(JavaScript ライブラリ. 通常利用は MIT ライセンスで商用利用などもできる)が使える streamlit 用のバインディングがあります!

https://github.com/PablocFonseca/streamlit-aggrid

が, なんかバインディング API がややこしい...

単にテーブルデータ表示したい + alpha くらい(bool 値は checkbox で表示とか)をしたい...

デフォルトだと sortable で filterable で煩わしいので, off にしたい...

streamlit も st_aggrid もアクティブに開発されていますので, ↓でカスタム対応しているようなものはそのうち標準機能として使えたりするかもしれません.

## 基本

GridOption で制御します.
st_aggrid ですと初期オプションを pandas DataFrame から自動で作ってくれる機能があります.

```
data = JSON のデータ
df = pandas.DataFrame(data)
gd = GridOptionsBuilder.from_dataframe(df, sortable=False, filterable=False)
```

https://github.com/PablocFonseca/streamlit-aggrid/blob/main/st_aggrid/grid_options_builder.py

https://streamlit-aggrid.readthedocs.io/en/docs/GridOptionsBuilder.html

あとは `configure_default_column` でカラム全体か, `configure_colum` でカラム指定で調整します.

## チェックボックスをつける.

![Screenshot from 2022-09-26 22-17-59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36275/6ab154e2-2270-be85-8be3-72c777fc1e79.png)


https://stackoverflow.com/questions/72633449/add-checkbox-in-dataframe

`configure_selection(selection_mode='single', use_checkbox=True)`

一見, API 名がへんてこです. レイアウトも最初の column の先頭につくのでちょっとわかりずらいです.
`configure_selection` で `use_checkbox` でいけます.
内部実装をみると, 最初の column に checkbox をつけるような実装になっています.
したがってこれは row をチェックボックスで選択するしない, みたいな意味合いでした.

`selection_mode` を multiple にすると複数 row をチェック on/off できます.

### Boolean 値を Checkbox で表示

column の boolean 値を Checkbox で表示したいときもあります.

https://blog.ag-grid.com/binding-boolean-values-to-checkboxes-in-ag-grid/

`cellRenderer` に自前で JSCode をつけて見た目を制御するしかないです...

st_aggrid では, cellRenderer に渡ったコードを JS として直接評価するようです. `params.value` に cell の値が渡ってきます.

```
  data = { 
      'Enabled': [True, False, False],
      ...
  }

  ...

  checkboxRenderer = JsCode('''
  function(params) {
    var value = "";
    if (params.value == true) {
      value = "checked"
    }
    return '<input type="checkbox" ' + value + '>'
  }
  ''')

  gd.configure_column('Enabled', cellRenderer=checkboxRenderer)
```

のように checkbox widget を返すようにしました.
とりあえず `<input "checkbox">` で直接 HTML 要素を返してもスタイル適用していいかんじにやってくれます.

![Screenshot from 2022-09-26 22-09-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36275/5969d60b-1743-c326-0526-d2fecf90d4f1.png)

あと, AgGrid で `allow_unsafe_jscode=True` を指定必要でした.
(Streamlit は React を利用しているので, React 要素を記述(できるのかしらん)にしたほうがより安全かもはしれません)

頑張れば URL とかも表示できるようになります.

https://github.com/PablocFonseca/streamlit-aggrid/issues/17

より頑張れば React のコードも渡せるかもしれません...

### check on/off の反映

ただ, これだと表示の設定しか変わらないので, ブラウザでチェック on/off しても, テーブルの値を取得してもセルの値は変わりません.

https://stackoverflow.com/questions/41706848/how-to-use-a-checkbox-for-a-boolean-data-with-ag-grid

のように onclick イベントを追加して内部ステートをアップデートするようにします!

```
checkboxRenderer = JsCode(
      """
  function(params) {

    var input = document.createElement('input');
    input.type="checkbox";
    input.checked=params.value;
    input.addEventListener('click', function (event) {
      params.value=!params.value;
      var colId = params.column.colId;
      params.node.setDataValue(colId, params.value); 
    });
    return input;

  }
  """
)
```

こんな感じです. 

`params.value` を変更しただけでは streamlit 側には反映されないです.
`params.node.setDataValue()` も呼ぶ必要があります.
(`params` のデータには自身を含んだ row データが入っているので, 自身の位置を知るには column id だけわかればよい)

ただチェック入れるたびにリロードするようになるので, 大きめのテーブルデータを扱っているなどで処理低下する場合は manual update にするのも検討したほうがよいでしょう.

https://streamlit-aggrid.readthedocs.io/en/docs/AgGrid.html
(`GridUpdateMode.MANUAL` を AgGrid コンストラクタで指定する)


## sort off

なんか最新版では off できなくなったっぽい...?


```
gd.configure_default_column(filterable=False,sortable=False)
```

上の `from_dataframe` でも指定できます(`from_dataframe` の実装内部で `configure_default_column` を呼んでいる)


## Filter off

sort off と同様です.

ただ, 最初の column が数値データの場合は, どうしてもフィルター機能(三本線アイコン)が残ってしまうようです...(バグ?)

![Screenshot from 2022-09-26 20-39-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36275/28d43de8-7d52-e328-aaf5-f4828881cd4d.png)

フィルターアイコンが煩わしい(ユーザーに操作させたくない)場合は, 無理やり column のデータを文字列にすればとりあえずは解決します...

## Editable

これも `configure_default_column` か `configure_column` で `editable` で設定できます!

```
gd.configure_column('X', filterable=False,sortable=False, editable=True)
```

### Cell の値の取得

ユーザーが Cell 値をいじったりしたあとに入力値を得たい.

`AgGrid()` で返ってくるオブジェクトの `"data"` でいけました.

```
grid_table = AgGrid(...)

# "data" に Pandas DataFrame が帰ってくる
df = grid_table["data"] 
```

### Table のリロード

↑にあるように df をなんか処理してそれを反映したい...

AgGrid() に `reload_data=True` 設定が必要です.


https://discuss.streamlit.io/t/ag-grid-editing-behavior-when-using-key-and-reload-data/22382

基本は `st.session_state` に DataFrame の情報を保存かなと思いますが, たとえばボタンで更新の場合, コードの評価は上から順に行われるため, 更新ボタンを下に配置するとうまくいきません(最初の変更が反映されない, 再描画になりレイアウトが一時的に崩れる)

おかしくなるケース

```python
  if not "df" in st.session_state: 
      df = pd.DataFrame(data)
      st.session_state["df"] = df
  
  df = st.session_state["df"]
  
  gd = GridOptionsBuilder.from_dataframe(df)
  gd_options = gd.build()
  
  table = AgGrid(df, gridOptons=gd_options, reload_data=True)
  
  if st.button("update"):
      st.write("Bora")
  
      df.iat[2, 2] += 1

```

アップデートさせたいときに `st.experimental_rerun()` で強制的にリフレッシュさせるか, https://docs.streamlit.io/library/api-reference/control-flow/st.experimental_rerun

```python
  if st.button("update"):
      st.write("Bora")
  
      df.iat[2, 2] += 1
      st.experimental_rerun() # force reload
```

 以下のようにボタンを先に配置(df の編集を AgGrid を呼ぶまえに先に行う)すればいけます!


```pythohn
  if not "df" in st.session_state: 
      df = pd.DataFrame(data)
      st.session_state["df"] = df
  
  df = st.session_state["df"]
  
  gd = GridOptionsBuilder.from_dataframe(df)
  gd_options = gd.build()

  if st.button("update"):
      st.write("Bora")
  
      table.data.iat[2, 2] += 1
  
  table = AgGrid(df, gridOptons=gd_options, reload_data=True)
  

```


~~ちなみに AgGrid() で `key` 引数指定しても `st.session_state` には該当のステートは自動生成されませんでした :cry:~~
(2022/11/20 時点の最新版では作られるようになったっぽい)




## 浮動小数点数のフォーマット

https://discuss.streamlit.io/t/ag-grid-formatting-floating-point-values/23821

`configure_column(YourColumnNameHeader, type=[“numericColumn”,“numberColumnFilter”,“customNumericFormat”], precision=1)`

でいけます. `precision` には小数点以下の桁数を指定します. それ以上の凝ったことをやる場合は cellRenderer で html で頑張るしかないです
(ただ表示はうまくいくだろうが, 入力がうまくできなかも?)


## テーブルの幅を自動 fit させる

https://stackoverflow.com/questions/72624323/how-to-set-a-max-column-length-for-streamlit-aggrid

`AgGrid(..., fit_columns_on_grid_load=True, ...)` でいけます.


## Cell に色をつける

https://qiita.com/Gyutan/items/1cbff90d31828b25c05d

ありがとうございます.

## その他見た目を調整

手動で aggrid の設定 JSON を調整 and cellRenderer などで調整でしょうか...


EoL.