Export a streamlit app as PDF

Hi streamlit community

I’ve already tried multiple solutions to find a way to export my entire app as a pdf and I looked for this in several topics on this subject with no success. Does anyone know how to do it?

Thank you!

Hi @pborges,

Thanks for sharing this question!

So this might be challenging due to the dynamic nature of Streamlit apps. You could use the browser’s print to PDF feature to save the page as PDF although it might not capture the dynamic portions of your app correctly.

Do you have an example of how you want the output to look like and also can you share the app in question?

1 Like


Thanks for you quick answer.
I would want that it looks like at least like it is displayed in the screen to the user. This is my app:

import streamlit as st
from streamlit_gsheets import GSheetsConnection
import pandas as pd
from datetime import datetime
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
import re
import streamlit_authenticator as stauth
import pickle
from pathlib import Path

page_title=‘CAPEX - Despesa de Capital’,
testing authentication


names = [“”, “”]
usernames = [“”, “”]

load hashed passwords

file_path = Path(file).parent / “hashed_pw.pkl”
with file_path.open(“rb”) as file:
hashed_passwords = pickle.load(file)

authenticator = stauth.Authenticate(names, usernames, hashed_passwords,
“sales_dashboard”, “abcdef”, cookie_expiry_days=1)

name, authentication_status, username = authenticator.login(“Login de acesso: Gestão de Recurso para Investimento - GCINFRA”, “main”)

if authentication_status == False:
st.error(“Usuário/senha está incorreto”)

if authentication_status == None:
st.warning(“Por favor, insira usuário e senha”)

if authentication_status:

#ending authetincation

url = " "

# Centered title using HTML tags
st.markdown("<h1 style='text-align: center;'>GESTÃO DE RECURSO PARA INVESTIMENTO</h1>", unsafe_allow_html=True)

# Adding a centered subtitle with larger font size using HTML
    <div style='text-align: center; font-size: 36px;'>
        <b>SOF - DCOL - GCINFRA</b>
""", unsafe_allow_html=True)

st.markdown("<br>", unsafe_allow_html=True)
st.markdown("<br>", unsafe_allow_html=True)
st.markdown("<br>", unsafe_allow_html=True)

st.sidebar.image('index.png', width=150)
st.sidebar.title(f"Bem-vindo, {name}")
conn = st.connection("gsheets", type=GSheetsConnection)
df = conn.read(spreadsheet=url, usecols=list(range(13)))

def custom_sort(value):
    if isinstance(value, (int, float)):
        return value  # Return numbers as they are for sorting
        return -np.inf  # Assign -np.inf for non-numeric entries to place them at the end

# Apply custom sorting to the DataFrame
df['Sort_Values'] = df['VALOR TOTAL DO INVESTIMENTO'].apply(lambda x: custom_sort(x))
df = df.sort_values(by='Sort_Values', ascending=False).drop(columns='Sort_Values')

# Define the list of "UNIDADE" values and add "Todos" as an option
desired_unidade = df["UNIDADE"].unique().tolist()
desired_unidade.insert(0, "Todos")

unidade = st.sidebar.multiselect("UNIDADE", desired_unidade, default=desired_unidade[0])

# Define the list of "FONTE DE RECURSO" values and add "Todos" as an option
desired_fonte = df["FONTE DE RECURSO"].unique().tolist()
desired_fonte.insert(0, "Todos")

fonte = st.sidebar.multiselect("FONTE DE RECURSO", desired_fonte, default=desired_fonte[0])

# Define the list of "CLASSIFICAÇÃO" values and add "Todos" as an option
desired_classificacao = df["CLASSIFICAÇÃO"].unique().tolist()
desired_classificacao.insert(0, "Todos")

# Create a filter for selecting "CLASSIFICAÇÃO"
classificacao = st.sidebar.multiselect("CLASSIFICAÇÃO", desired_classificacao, default=desired_classificacao[0])

# Define the list of "JUSTIFICATIVA" values and add "Todos" as an option
desired_justificativa = df["JUSTIFICATIVA"].unique().tolist()
desired_justificativa.insert(0, "Todos")

# Create a filter for selecting "JUSTIFICATIVA"
justificativa = st.sidebar.multiselect("JUSTIFICATIVA", desired_justificativa, default=desired_justificativa[0])

# Define the list of "NÍVEL DE PRIORIDADE" values and add "Todos" as an option
desired_prioridade = df["NÍVEL DE PRIORIDADE"].unique().tolist()
desired_prioridade.insert(0, "Todos")

prioridade = st.sidebar.multiselect("NÍVEL DE PRIORIDADE", desired_prioridade, default=desired_prioridade[0])

# Define the list of "FAIXA DE VALOR" values and add "Todos" as an option
desired_faixa = df["FAIXA DE VALOR"].unique().tolist()
desired_faixa.insert(0, "Todos")

faixa = st.sidebar.multiselect("FAIXA DE VALOR", desired_faixa, default=desired_faixa[0])

# Define the list of "SOLICITAÇÃO REALIZADA" values and add "Todos" as an option
desired_solicitacao = df["SOLICITAÇÃO REALIZADA"].unique().tolist()
desired_solicitacao.insert(0, "Todos")

solicitacao = st.sidebar.multiselect("SOLICITAÇÃO REALIZADA", desired_solicitacao, default=desired_solicitacao[0])

# Filter the DataFrame based on user selections
filtered_df = df.copy()

if unidade and unidade != ["Todos"]:
    filtered_df = filtered_df[filtered_df["UNIDADE"].isin(unidade)]

if fonte and fonte != ["Todos"]:
    filtered_df = filtered_df[filtered_df["FONTE DE RECURSO"].isin(fonte)]

if classificacao and classificacao != ["Todos"]:
    filtered_df = filtered_df[filtered_df["CLASSIFICAÇÃO"].isin(classificacao)]

if justificativa and justificativa != ["Todos"]:
    filtered_df = filtered_df[filtered_df["JUSTIFICATIVA"].isin(justificativa)]

if prioridade and prioridade != ["Todos"]:
    filtered_df = filtered_df[filtered_df["NÍVEL DE PRIORIDADE"].isin(prioridade)]

if faixa and faixa != ["Todos"]:
    filtered_df = filtered_df[filtered_df["FAIXA DE VALOR"].isin(faixa)]

if solicitacao and solicitacao != ["Todos"]:
    filtered_df = filtered_df[filtered_df["SOLICITAÇÃO REALIZADA"].isin(solicitacao)]

st.markdown("<br>", unsafe_allow_html=True)
authenticator.logout("Logout", "sidebar")

col1, col2, col3 = st.columns(3)
col4, col5, col6 = st.columns(3)
col7, col8, col9 = st.columns(3)
col10, col11 = st.columns(2)
col12, col13 = st.columns(2)
col14, col15 = st.columns(2)

# Convert 'VALOR TOTAL DO INVESTIMENTO' column to numeric values, ignoring non-numeric values
filtered_df['VALOR TOTAL DO INVESTIMENTO'] = pd.to_numeric(filtered_df['VALOR TOTAL DO INVESTIMENTO'], errors='coerce')

# Filter out NaN (non-numeric) values
numeric_values = filtered_df['VALOR TOTAL DO INVESTIMENTO'].dropna()

# Calculate the sum of the numeric values in the "VALOR TOTAL DO INVESTIMENTO" column
sum_valor_total = numeric_values.sum()

# Format the sum to display as Brazilian Real currency
formatted_sum = "R${:,.2f}".format(sum_valor_total)

# Display the sum of "VALOR TOTAL DO INVESTIMENTO" with a border
    <div style='border: 2px solid #1f77b4; border-radius: 5px; padding: 10px;'>
        <h3 style='text-align: center;'>Valor Prospectado 💰</h3>
        <div style='display: flex; justify-content: center;'>
            <h2 style='color: #1f77b4;'>{}</h2>
""".format(formatted_sum), unsafe_allow_html=True)

st.markdown("<br>", unsafe_allow_html=True)
st.markdown("<br>", unsafe_allow_html=True)

# Count the number of rows in the "UNIDADE" column
num_pedidos = filtered_df["UNIDADE"].count()

# Display the number of rows in a centered metric display in col2 with a centered subheader
col6.markdown("<h3 style='text-align: center;'>Número de Pedidos 📊</h3>", unsafe_allow_html=True)
col6.markdown("<div style='display: flex; justify-content: center;'>"
              "<h2 style='color: #1f77b4;'>{}</h2></div>".format(num_pedidos), unsafe_allow_html=True)

# Sum the values in the "QUANTITATIVO SOLICITADO" column
total_quantidade_solicitada = int(filtered_df["QUANTITATIVO SOLICITADO"].sum())

# Display the total quantity in a centered metric display in col3 with a subheader
col4.markdown("<h3 style='text-align: center;'>Qtd de Equipamentos 🔬</h3>", unsafe_allow_html=True)
col4.markdown("<div style='display: flex; justify-content: center;'>"
              "<h2 style='color: #1f77b4;'>{}</h2></div>".format(total_quantidade_solicitada), unsafe_allow_html=True)

# Calculate the average value
avg_value = sum_valor_total / num_pedidos

# Format the average value to display as Brazilian Real currency
formatted_avg = "R${:,.2f}".format(avg_value)

# Display the average value in a metric display in col4 with a subheader and emoji
col9.subheader('Média por Pedido ➗')
col9.metric(label='', value=formatted_avg, delta=None)

# Define a color map for the "NÍVEL DE PRIORIDADE" categories
color_map = {
    "NÃO URGENTE": "blue",
    "POUCO URGENTE": "lightblue",
    "URGENTE": "lightcoral",
    "EMERGÊNCIA": "red"

#def styled_title(title):
    #return f"<div style='text-align: center; font-size: 36px; font-weight: bold;'>{title}</div>"

# Create a bar chart using Plotly Express with the specified color order
fig = px.bar(filtered_df, x="UNIDADE", color="NÍVEL DE PRIORIDADE", title="Quantidade por UNIDADE e NÍVEL DE PRIORIDADE",

# Specify the order of bars in descending order based on the total quantity within each "UNIDADE"
fig.update_layout(barmode='stack', xaxis_categoryorder='total descending')

# Display the bar chart in col8