# --- Nuova Classe: ClickableDataFrame ---
class ClickableDataFrame:
def __init__(self, df: pd.DataFrame, key: str, fields_to_extract: list = None, extract_index: bool = True, **kwargs):
"""
Inizializza una tabella Ag-Grid cliccabile che gestisce gli eventi di doppio click.
Args:
df (pd.DataFrame): Il DataFrame da visualizzare.
key (str): Una chiave unica per il widget Streamlit.
Questa chiave verrà usata anche come prefisso per le chiavi in st.session_state.
fields_to_extract (list, optional): Una lista di nomi di colonne (stringhe) da estrarre
dalla riga doppio-cliccata e salvare in session_state.
Se None o vuota, salva l'intera riga (e.data). Defaults to None.
extract_index (bool): Se True, l'indice posizionale della riga (e.rowIndex)
verrà salvato in session_state. Defaults to True.
**kwargs: Argomenti aggiuntivi da passare direttamente a AgGrid (es. height, width, etc.).
"""
self.df = df
self.key = key
self.fields_to_extract = fields_to_extract
self.extract_index = extract_index
self.aggrid_kwargs = kwargs
# Inizializza lo stato della sessione per questa istanza se non esiste già
if f'{self.key}_double_clicked_index' not in st.session_state:
st.session_state[f'{self.key}_double_clicked_index'] = None
st.session_state[f'{self.key}_double_clicked_data'] = None
self._configure_grid()
def _configure_grid(self):
"""
Configura le opzioni di Ag-Grid, inclusa la logica JavaScript per il doppio click.
"""
gb = GridOptionsBuilder.from_dataframe(self.df)
gb.configure_selection('single', use_checkbox=False) # Nessuna checkbox di selezione
# JavaScript per gestire l'evento di doppio click sulla riga.
# Questa funzione invierà l'indice e l'intera riga di dati a Streamlit.
# La logica di estrazione dei campi specifici avverrà in Python.
on_double_click_js = JsCode("""
function(e) {{
if (e.event.detail === 2) {{ // Controlla se è un doppio click (detail === 2)
Streamlit.setComponentValue({{
index: e.rowIndex, // Indice posizionale della riga
data: e.data // Tutti i dati della riga
}});
}}
}};
""")
gb.on_row_double_clicked(on_double_click_js)
self.grid_options = gb.build()
def display(self):
"""
Visualizza la tabella Ag-Grid e gestisce la risposta dal componente JavaScript.
"""
grid_response = AgGrid(
self.df,
gridOptions=self.grid_options,
data_return_mode='AS_INPUT', # Restituisce i dati così come sono stati forniti
update_mode='MODEL_CHANGED', # Aggiorna la griglia quando il modello interno cambia (es. selezione)
fit_columns_on_grid_load=True,
allow_unsafe_jscode=True, # Permette l'esecuzione del codice JavaScript personalizzato
enable_enterprise_modules=False, # Imposta su True se hai la licenza Ag-Grid Enterprise
key=self.key, # Chiave unica per il widget AgGrid
**self.aggrid_kwargs
)
# Processa la risposta inviata dal codice JavaScript
# La risposta personalizzata di JsCode è disponibile in grid_response.custom_response
if grid_response.custom_response:
clicked_index = grid_response.custom_response.get('index')
clicked_data = grid_response.custom_response.get('data')
if clicked_index is not None and clicked_data is not None:
# Salva l'indice se richiesto
if self.extract_index:
st.session_state[f'{self.key}_double_clicked_index'] = clicked_index
else:
st.session_state[f'{self.key}_double_clicked_index'] = None # Reset se non richiesto
# Estrai solo i campi specificati, altrimenti salva l'intera riga
if self.fields_to_extract:
extracted_data = {
field: clicked_data.get(field)
for field in self.fields_to_extract
if field in clicked_data # Assicurati che il campo esista nei dati della riga
}
st.session_state[f'{self.key}_double_clicked_data'] = extracted_data
else:
st.session_state[f'{self.key}_double_clicked_data'] = clicked_data
else:
# Resetta lo stato se la risposta è incompleta (non dovrebbe accadere con il JS attuale)
st.session_state[f'{self.key}_double_clicked_index'] = None
st.session_state[f'{self.key}_double_clicked_data'] = None
def get_double_clicked_data(self):
"""
Restituisce l'indice e i dati (o i campi estratti) della riga doppio-cliccata.
Restituisce (None, None) se nessuna riga è stata doppio-cliccata o se lo stato è stato resettato.
"""
return (
st.session_state.get(f'{self.key}_double_clicked_index'),
st.session_state.get(f'{self.key}_double_clicked_data')
)
# --- 2. Creazione e Visualizzazione della Tabella Cliccabile ---
st.subheader("Tabella Principale Prodotti")
# Istanzia la nostra nuova classe ClickableDataFrame
# Specifichiamo i campi che vogliamo estrarre dalla riga doppio-cliccata.
# Se non specifichi 'fields_to_extract', verrà salvata l'intera riga.
# Se 'extract_index' è False, l'indice non verrà salvato.
clickable_table = ClickableDataFrame(
df=df_parent,
key='parent_table', # Chiave unica per questa istanza della tabella
fields_to_extract=['ID Prodotto', 'Nome Prodotto', 'Dettagli Ordini'], # Campi da estrarre
extract_index=True, # Salva l'indice posizionale
height=300,
width='100%'
)
# Visualizza la tabella
clickable_table.display()
if st.session_state.get(f'{clickable_table.key}_double_clicked_index') is not None:
show_details_dialog()
@st.dialog("Exploded data", width="large")
def show_details_dialog():
st.subheader(f"Dettagli Ordini per Riga (Indice Posizionale): {double_clicked_index}")
try:
child_df_json = double_clicked_data.get('Dettagli Ordini')
if child_df_json:
child_df = pd.read_json(child_df_json)
st.dataframe(child_df, use_container_width=True, hide_index=True)
st.markdown(f"Questi sono gli ordini associati alla **riga con indice posizionale {double_clicked_index}** (Prodotto ID: {product_id_from_clicked_data}).")
else:
st.warning("I dettagli degli ordini non sono disponibili per la riga selezionata. Assicurati che 'Dettagli Ordini' sia incluso in 'fields_to_extract' o che 'fields_to_extract' sia None.")
except Exception as e:
st.error(f"Errore nel caricamento dei dettagli: {e}")
st.write("Assicurati che i dati dei dettagli siano in un formato JSON valido.")
METODO ALTERNATIVO COL DOPPIO CLIC. Gestisci il popup che si apre con @st.dialog("Exploded data", width="large")
https://docs.streamlit.io/develop/api-reference/execution-flow/st.dialog#stexperimental_dialog
La tabella è creata col comando AgGrid()
Details
METODO ALTERNATIVO COL DOPPIO CLIC. Gestisci il popup che si apre con @st.dialog("Exploded data", width="large")
https://docs.streamlit.io/develop/api-reference/execution-flow/st.dialog#stexperimental_dialog
La tabella è creata con una nuova classe che eredita da AgGrid
Details