LoginSignup
3
3

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-09-26

Streamlit で DataFrame widget ありますが,

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

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

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

が, なんかバインディング 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)

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

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

Screenshot from 2022-09-26 22-17-59.png

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

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

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

Boolean 値を Checkbox で表示

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

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

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

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

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

check on/off の反映

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

のように 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

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

Editable

これも configure_default_columnconfigure_columneditable で設定できます!

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 設定が必要です.

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

おかしくなるケース

  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

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

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

  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 時点の最新版では作られるようになったっぽい)

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

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

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

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

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

Cell に色をつける

ありがとうございます.

その他見た目を調整

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

EoL.

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3