はじめに
社内のDBから情報を抜き取り表示することを検討している。設計データはスカラー以外にもベクトルやマップがあり、このデータを一つの表で表すには、次元に応じた表示が必要だと考える。スカラーは重要な要素なので、こちらをベースに次元数が上がるパラメータはクリックして別のところに表示をと思い、表の中にボタンを作ることを考えた。
課題
streamlitはpythonで出来るのが持ち味だが、aggをカスタマイズするのにJava Scriptの知識が必要になる。大変だ。。。
結果
取り合えず動画をみていただくと、出来栄えが分かるだろう。
ポイント
- ヘッダーのグループ化:ヘッダーとchildrenの組み合わせで作り込み可能
- ボタン:いくつかイベントにバリエーションを持たせてみた。特にボタンを押したときのセルへの値反映が苦労
- レンダーの切り替え:セルの値に応じてレンダーを切り替える部分だが、cellrendereselectorが上手く機能しなかったので、クラス内でparam.valueの値に応じた切り替えを実装
ちなみにこの動画を参考
コード
とりあえず色々書いてあるので、試してみてください。
いまだに分からないところが、cellrendererに関数、クラスを渡すことができるが、両者の違いは何か?関数だとHTMLが上手く渡せなかった。クラスだとできている。。。
import streamlit as st
import pandas as pd
from st_aggrid import AgGrid,JsCode
##########設定###########################
df = pd.read_excel("tmp.xlsx")
df=df.dropna()
st.set_page_config(
page_title="agg cheat",
layout="wide", )
### 全部hello ###
SimpleComp=JsCode('''
function(){
return 'hello'}
''')
### 元の値を返す ###
SimpleComp1=JsCode('''
function(p){
return p.value}
''')
ClassComp = JsCode('''
class ClassComp {
init(params) {
this.params=params;
this.eGui = document.createElement('div');
this.eGui.innerHTML = `
<button class="btn-dollar">$</button>
<button class="btn-at">@</button>
<span>${params.value}</span>
`;
this.btnDollar=this.eGui.querySelector('.btn-dollar');
this.btnAt=this.eGui.querySelector('.btn-at');
this.btnDollar.onclick=()=>alert('$$$ '+params.value);
this.btnAt.onclick=()=>alert('@@@ '+params.value);
}
getGui() {
return this.eGui;
}
refresh() {
return false;
}
destroy() { }
};
''')
ClassComp1 = JsCode('''
class ClassComp1 {
init(params) {
this.params=params;
this.eGui = document.createElement('div');
this.eGui.innerHTML = `
<button class="btn-dollar">${params.btnText}</button>
<span>${params.value}</span>
`;
this.btnDollar=this.eGui.querySelector('.btn-dollar');
this.btnDollar.onclick=()=>alert('$$$ '+params.value);
}
getGui() {
return this.eGui;
}
refresh() {
return false;
}
destroy() { }
};
''')
### セレクタ込み ###
### 上手くいかない ###
Select=JsCode('''
function(p){
if (p.value==="C"){
return '<a href="https://www.google.com">p.value</a>'
}else{
return p.value
}}''')
Select1=JsCode('''
class ClassComp1k {
init(params) {
this.params=params;
if (this.params.value.match("C")){
this.eGui = document.createElement('div');
this.eGui.innerHTML = `
<button class="btn-dollar">${params.value}</button>
<span>${params.value}</span>`;
this.btnDollar=this.eGui.querySelector('.btn-dollar');
this.btnDollar.onclick=()=>{
alert('$$$ '+params.value);
if(this.params.getValue() .match('clicked')) {
this.params.setValue('C');
} else {
this.params.setValue('C clicked');
} }
}else{
this.eGui = document.createElement('div');
this.eGui.innerHTML = `
<span>${params.value}</span>`;}
}
getGui() {
return this.eGui;
}
refresh() {
return false;
}
destroy() { }
};
''')
#########################################
st.markdown("test")
gridOP={
"defaultColDef": {
"sortable": "true",
"resizable": "true",
"filter": "true",
"editable":True,},
"columnDefs":[
{"headerName":"",
"children":[
{"field":"index",
"pinned":"left"}]},
{"headerName":"Gr1",
"children":[
{"field":"data1","cellRenderer":SimpleComp
},{"field":"data2","checkboxSelection":True,},
{"headerName":"Gr2",
"children":[{"field":"data3",
"cellRenderer":Select1
}]}]},
{"headerName":"Gr3",
"children":[
{"field":"data4","cellRenderer":ClassComp},
{"field":"data5","cellRenderer":ClassComp1,"cellRendererParams":{"btnText":"Amazing"}},
{"field":"data6","cellRenderer":JsCode('''function(p){
return 'Age is ' + p.value
}''')
}]}]}
tmp=AgGrid(df,gridOptions=gridOP,
layout="wide",
key="jitan",
allow_unsafe_jscode = True
)
tmp['data']
tmp["selected_rows"]
##### 仮置き ###########
Select2=JsCode('''
class BtnCellRenderer {
init(params) {
this.params = params;
if (this.params.value==="C"){
this.eGui = document.createElement('div');
this.eGui.innerHTML = `
<span>
<button id='click-button'
class='btn-simple'
style='color: ${this.params.color}; background-color: ${this.params.background_color}'>Click!</button>
</span>
`;
this.eButton = this.eGui.querySelector('#click-button');
this.btnClickedHandler = this.btnClickedHandler.bind(this);
this.eButton.addEventListener('click', this.btnClickedHandler);
}else{
this.eGui = document.createElement('div');
this.eGui.innerHTML = `
<span>${params.value}</span>`;}
}
getGui() {
return this.eGui;
}
refresh() {
return true;
}
destroy() {
if (this.eButton) {
this.eGui.removeEventListener('click', this.btnClickedHandler);
}
}
btnClickedHandler(event) {
if (confirm('Are you sure you want to CLICK?') == true) {
if(this.params.getValue() == 'clicked') {
this.refreshTable('');
} else {
this.refreshTable('clicked');
}
console.log(this.params);
console.log(this.params.getValue());
}
}
refreshTable(value) {
this.params.setValue(value);
}
};
''')
ちなみにエクセルに書かれている情報は以下のとおり

前回記事のようにヘッダー名をデータ中に書く必要はない。。
おしまい
この記事が参考になったら幸いです。ぜひHP内のアフィを活用ください。。。(お金に余裕あれば)

コメント