Pagination in st.columns not updated at clicking once

  1. Are you running your app locally or is it deployed? - locally running
  2. If your app is deployed: no
    a. Is it deployed on Community Cloud or another hosting platform?
    b. Share the link to the public deployed app.
  3. Share the link to your app’s public GitHub repository (including a requirements file).
  4. Share the full text of the error message (not a screenshot).
  5. Share the Streamlit and Python versions.
    streamlit 1.30.0
    streamlit-pagination 0.0.3
    python 3.9.6

Hi, I am really new in streamlit. Currently I am trying to apply pagination to st.columns list. but in my current code the list of n-page is being updated when the page button is clicked twice. If I call recursive function it just adds columns below the first collections of columns. is there any way to refresh the list of columns only? not restarting the hole process due to background processes to be done.

Please anyone give me some insight and help to cope this issue.

import streamlit as st
import pandas as pd
import os
import hashlib
from concurrent.futures import ThreadPoolExecutor
import time
import json
import math
import requests
from streamlit_pagination import pagination_component
st.markdown('<style>' + """iframe{
    height:100px;
    width:500px;
}""" + '</style>', unsafe_allow_html=True)

slack_webhook_url = ""

headers = {
    "Content-type": "application/json"
}
table_header = ["title", "upload status", "analysis_status", "re-analysis", "download"]
PAGE_SIZE = 3

os.makedirs('uploaded_files', exist_ok=True)
os.makedirs('results', exist_ok=True)

executor = ThreadPoolExecutor(max_workers=5)

@st.cache_resource
def load_shared_state():
    if os.path.exists('shared_state.json'):
        with open('shared_state.json', 'r') as f:
            return json.load(f)
    else:
        return {}

shared_state = load_shared_state()

if 'uploaded_files' not in st.session_state:
    st.session_state['uploaded_files'] = shared_state.get('uploaded_files', {})
if 'upload_status' not in st.session_state:
    st.session_state['upload_status'] = shared_state.get('upload_status', {})
if 'analysis_status' not in st.session_state:
    st.session_state['analysis_status'] = shared_state.get('analysis_status', {})
if 'analysis_futures' not in st.session_state:
    st.session_state['analysis_futures'] = shared_state.get('analysis_futures', {})
if 'analysis_results' not in st.session_state:
    st.session_state['analysis_results'] = shared_state.get('analysis_results', {})

def update_shared_state(updated_state):
    filtered_state = {key: updated_state[key] for key in ["uploaded_files", "analysis_status", "analysis_futures", "analysis_results"] if key in updated_state}

    with open('shared_state.json', 'w') as f:
        json.dump(filtered_state, f)
    
def get_file_hash(uploaded_file):
    content = uploaded_file.getvalue()
    return hashlib.md5(content).hexdigest()

def perform_analysis(file_path):
    result_path = f"results/{os.path.basename(file_path).split('.')[0]}_result.csv"
    pd.DataFrame().to_csv(result_path)
    return result_path

def handle_file(uploaded_file):
    global shared_state
    file_hash = get_file_hash(uploaded_file)
    file_path = f"uploaded_files/{file_hash}.mp4"
    with open(file_path, "wb") as f:
        f.write(uploaded_file.getbuffer())
    st.session_state['uploaded_files'][uploaded_file.name] = file_path
    st.session_state['upload_status'][uploaded_file.name] = 'Uploaded'
    future = executor.submit(perform_analysis, file_path)
    
    st.session_state['analysis_status'][uploaded_file.name] = 'Analyzing'
    st.session_state['analysis_futures'][uploaded_file.name] = future
    shared_state.update(st.session_state)
    future.add_done_callback(lambda x: analysis_done_callback(uploaded_file.name, x.result()))

def download_results(filename):
    with open(st.session_state['analysis_results'][filename], "rb") as f:
        st.download_button(label='Download Results', data=f, file_name=f'{filename}_results.csv')

def reanalyze_file(filename):
    st.session_state.page = 'Collections'

    file_path = st.session_state['uploaded_files'][filename]
    future = executor.submit(perform_analysis, file_path)
    st.session_state['analysis_status'][filename] = 'Analyzing'
    st.session_state['analysis_futures'][filename] = future
    future.add_done_callback(lambda x: analysis_done_callback(filename.name, x.result()))

def analysis_done_callback(filename, result_path):
    shared_state['analysis_status'][filename] = 'Analysis Complete'
    shared_state['analysis_results'][filename] = result_path
    if st.session_state:
        st.session_state.update(shared_state) 
    else:
        for k, v in shared_state.items():
            st.session_state[k] = v
    data = {
        "text": f"{filename}."
    }
    # res = requests.post(slack_webhook_url, headers=headers, data=json.dumps(data))
    update_shared_state(st.session_state)
    print(data["text"])


if 'page' not in st.session_state:
    st.session_state.page = 'Upload page'

if st.sidebar.button('Upload page'):
    st.session_state.page = 'Upload page'
if st.sidebar.button('Collections'):
    st.session_state.page = 'Collections'

    
if 'current_page' not in st.session_state:
    st.session_state['current_page'] = 1
    
def display_page_buttons(total_pages, current_page):
    num_buttons = min(5, total_pages)
    button_cols = st.columns([1 for _ in range(num_buttons + 2)])

    # 'Previous' 버튼
    if current_page > 1 and button_cols[0].button("Previous", key="prev"):
        st.session_state['current_page'] = current_page - 1

   
    start_page = max(1, current_page - (num_buttons // 2))
    end_page = min(total_pages, start_page + num_buttons - 1)
    start_page = max(1, end_page - num_buttons + 1)  # Adjust start_page if end_page is at the limit

    for i, page_num in enumerate(range(start_page, end_page + 1), start=1):
        if button_cols[i].button(str(page_num), key=f"page-{page_num}"):
            st.session_state['current_page'] = page_num

    if current_page < total_pages and button_cols[-1].button("Next", key="next"):
        st.session_state['current_page'] = current_page + 1
    
    st.write(f"Page {st.session_state['current_page']} of {total_pages}")



def main():
    
    if st.session_state.page == 'Upload page':
        st.title("Upload your video")
        uploaded_file = st.file_uploader("Choose a file", type=["mp4"])
        if uploaded_file is not None:
            handle_file(uploaded_file)
    
    elif st.session_state.page == 'Collections':
        st.title("list of analysis")
        sorted_files = sorted(st.session_state['uploaded_files'].items(), key=lambda item: os.path.getmtime(item[1]), reverse=True)

        total_files = len(sorted_files)
        current_page = st.session_state['current_page']
        start_index = (current_page - 1) * PAGE_SIZE
        end_index = start_index + PAGE_SIZE

        global shared_state
        
        cols = st.columns([2, 1, 1, 1, 1])
        for col, field in zip(cols, table_header):
            col.write("**" + field + "**")
        
        total_pages = (len(sorted_files) - 1) // PAGE_SIZE + 1
        current_page = st.session_state['current_page']
        for filename, file_path in sorted_files[start_index:end_index]:
            if filename in st.session_state['analysis_futures']:
                future = st.session_state['analysis_futures'][filename]
                if future.done():
                    st.session_state['analysis_status'][filename] = 'Analysis Complete'
                    st.session_state['analysis_results'][filename] = future.result()
                    shared_state = st.session_state
            
            cols = st.columns([2, 1, 1, 1, 1])
            cols[0].write(filename)
            cols[1].write(st.session_state['upload_status'][filename])
            cols[2].write(st.session_state['analysis_status'][filename])

            with cols[3]:
                reanalyze_button = st.button(label='Reanalyze', key=f'reanalyze-{filename}')
                if reanalyze_button:
                    reanalyze_file(filename)
                    cols[4].empty()

            with cols[4]:
                if st.session_state['analysis_status'][filename] == 'Analysis Complete':
                    download_results(filename)

    display_page_buttons(total_pages, current_page)

        # layout = {'color': "primary", 'style': {'margin-top': '10px', 'display': 'flex','justify-content': 'center'}}
        # selected_page = pagination_component(math.ceil(total_files / PAGE_SIZE) + 1, layout=layout) + 1
        # st.session_state['current_page'] = selected_page

if __name__ == "__main__":
    main()

Hi @ddoron9

Typically, if you’re encountering an issue where 2 clicks are needed to properly run a session state variable then it is worth to try and use callback functions where essentially you’ll define a callback function and apply that to on_click parameter of st.button() or on_change parameter of other input widgets like st.text_input.

Additional info and code snippet is available on the Docs page:

Hope this helps!