如何将Streamlit应用页面导出为标准尺寸PDF文件?
解决Streamlit页面导出适配11x8.5纸张的PDF问题
问题背景
我在@MathCatsAnd帮助下创建了一个Streamlit应用,每个页面包含多个DataFrame(示例简化为每页一个)。需求是将每个页面导出为标准11x8.5英寸的PDF文件,需让26列的表格适配纸张宽度,纵向内容自动分页。但原生Ctrl+P无法正常实现,且网上找不到相关解决方案。
应用示例代码如下:
import streamlit as st import pandas as pd import numpy as np # Initialize session state with dataframes # Include initialization of "edited" slots by copying originals if 'df1' not in st.session_state: st.title("**:blue[Title123]**") Nvals = 200 rows = ['a{}'.format(i) for i in range(1, Nvals+1)] # a1:a19 values = np.arange(Nvals) st.session_state.df1 = pd.DataFrame({ "rows/cols": rows, 'A': list(range(Nvals)), 'B': list(range(Nvals)), 'C': list(range(Nvals)), 'D': list(range(Nvals)), 'E': list(range(Nvals)), 'F': list(range(Nvals)), 'G': list(range(Nvals)), 'H': list(range(Nvals)), 'I': list(range(Nvals)), 'J': list(range(Nvals)), 'K': list(range(Nvals)), 'L': list(range(Nvals)), 'M': list(range(Nvals)), 'N': list(range(Nvals)), 'Q': list(range(Nvals)), 'O': list(range(Nvals)), 'P': list(range(Nvals)), 'R': list(range(Nvals)), 'S': list(range(Nvals)), 'T': list(range(Nvals)), 'U': list(range(Nvals)), 'V': list(range(Nvals)), 'W': list(range(Nvals)), 'X': list(range(Nvals)), 'Y': list(range(Nvals)), 'Z': list(range(Nvals)), }) st.session_state.edited_df1 = st.session_state.df1.copy() # st.session_state.df2 = pd.DataFrame({ "col1": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "col2": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "col3": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }) st.session_state.edited_df2 = st.session_state.df2.copy() # Save edits by copying edited dataframes to "original" slots in session state def on_change_callback_fcn(): st.session_state.df1 = st.session_state.edited_df1 st.session_state.df2 = st.session_state.edited_df2 # Sidebar to select page and commit changes upon selection page = st.sidebar.selectbox("Select: ", ("A","B"), on_change=on_change_callback_fcn, key='SelectedSelectboxVal') # Convenient shorthand notation used below in funct1 and funct2 def funct1(): st.session_state.edited_df1 = st.data_editor(st.session_state.df1, num_rows="dynamic",height=10*len(st.session_state.edited_df1)) def funct2(): st.session_state.edited_df2 = st.data_editor(st.session_state.df2, num_rows="dynamic") if page == "A": st.header("Page A") funct1() elif page == "B": st.header("Page B") funct2() print('\n ...................... \n Entire Code Ran \n ...................... \n') print(len(st.session_state.edited_df1))
解决方案
方案1:自定义CSS优化原生打印效果
通过添加打印专用CSS,强制页面适配11x8.5英寸纸张,调整表格布局并隐藏无关元素。
修改后的代码如下:
import streamlit as st import pandas as pd import numpy as np # 添加打印专用CSS st.markdown(""" <style> @media print { @page { size: 11in 8.5in; /* 设置纸张尺寸为横向11x8.5 */ margin: 0.5in; } /* 隐藏Streamlit侧边栏、顶部导航等无关元素 */ section[data-testid="stSidebar"], header[data-testid="stHeader"], footer[data-testid="stFooter"] { display: none !important; } /* 让主内容区域占满纸张宽度 */ div[data-testid="stVerticalBlock"] { width: 100% !important; max-width: 100% !important; } /* 调整表格样式,让列适配宽度 */ table { width: 100% !important; font-size: 8pt; /* 缩小字体适配多列 */ border-collapse: collapse !important; } th, td { border: 1px solid #ddd !important; padding: 2pt !important; word-wrap: break-word; } /* 禁止表格横向溢出 */ div[data-testid="stDataFrame"] { overflow-x: visible !important; } } </style> """, unsafe_allow_html=True) # Initialize session state with dataframes # Include initialization of "edited" slots by copying originals if 'df1' not in st.session_state: st.title("**:blue[Title123]**") Nvals = 200 rows = ['a{}'.format(i) for i in range(1, Nvals+1)] # a1:a19 values = np.arange(Nvals) st.session_state.df1 = pd.DataFrame({ "rows/cols": rows, 'A': list(range(Nvals)), 'B': list(range(Nvals)), 'C': list(range(Nvals)), 'D': list(range(Nvals)), 'E': list(range(Nvals)), 'F': list(range(Nvals)), 'G': list(range(Nvals)), 'H': list(range(Nvals)), 'I': list(range(Nvals)), 'J': list(range(Nvals)), 'K': list(range(Nvals)), 'L': list(range(Nvals)), 'M': list(range(Nvals)), 'N': list(range(Nvals)), 'Q': list(range(Nvals)), 'O': list(range(Nvals)), 'P': list(range(Nvals)), 'R': list(range(Nvals)), 'S': list(range(Nvals)), 'T': list(range(Nvals)), 'U': list(range(Nvals)), 'V': list(range(Nvals)), 'W': list(range(Nvals)), 'X': list(range(Nvals)), 'Y': list(range(Nvals)), 'Z': list(range(Nvals)), }) st.session_state.edited_df1 = st.session_state.df1.copy() # st.session_state.df2 = pd.DataFrame({ "col1": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "col2": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "col3": ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10"], "Values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }) st.session_state.edited_df2 = st.session_state.df2.copy() # Save edits by copying edited dataframes to "original" slots in session state def on_change_callback_fcn(): st.session_state.df1 = st.session_state.edited_df1 st.session_state.df2 = st.session_state.edited_df2 # Sidebar to select page and commit changes upon selection page = st.sidebar.selectbox("Select: ", ("A","B"), on_change=on_change_callback_fcn, key='SelectedSelectboxVal') # 添加打印按钮 if st.button(f"打印当前页面 {page}"): st.markdown('<script>window.print();</script>', unsafe_allow_html=True) # Convenient shorthand notation used below in funct1 and funct2 def funct1(): st.session_state.edited_df1 = st.data_editor(st.session_state.df1, num_rows="dynamic",height=10*len(st.session_state.edited_df1)) def funct2(): st.session_state.edited_df2 = st.data_editor(st.session_state.df2, num_rows="dynamic") if page == "A": st.header("Page A") funct1() elif page == "B": st.header("Page B") funct2()
使用说明:
- 运行应用后,切换到目标页面,点击"打印当前页面"按钮触发打印
- 在打印预览中确认纸张尺寸为11x8.5英寸(横向),调整缩放比例确保列完全适配
方案2:用Pandas直接生成适配的PDF
如果原生打印仍有问题,可将编辑后的DataFrame直接导出为格式适配的PDF,需安装pdfkit和依赖的wkhtmltopdf工具。
步骤:
- 安装依赖:
pip install pdfkit
- 下载安装wkhtmltopdf(根据系统选择对应版本)
- 添加导出PDF的代码:
在原代码中添加如下函数,并在页面中添加导出按钮:
import pdfkit def export_to_pdf(df, page_name): # 将DataFrame转为HTML,添加样式 html = df.to_html(index=False, border=1) css = """ <style> table { width: 100%; font-size: 8pt; border-collapse: collapse; } th, td { border: 1px solid #000; padding: 3px; } </style> """ full_html = f"<html><head>{css}</head><body><h1>{page_name}</h1>{html}</body></html>" # 设置PDF选项,指定纸张尺寸 options = { 'page-size': 'Letter', # Letter尺寸为8.5x11英寸,这里设置横向 'orientation': 'Landscape', 'margin-top': '0.5in', 'margin-right': '0.5in', 'margin-bottom': '0.5in', 'margin-left': '0.5in', 'encoding': "UTF-8", } # 生成PDF pdfkit.from_string(full_html, f"{page_name}.pdf", options=options) st.success(f"PDF已导出为 {page_name}.pdf") # 在页面中添加导出按钮 if page == "A": st.header("Page A") funct1() if st.button("导出Page A为PDF"): export_to_pdf(st.session_state.edited_df1, "Page A") elif page == "B": st.header("Page B") funct2() if st.button("导出Page B为PDF"): export_to_pdf(st.session_state.edited_df2, "Page B")
注意:需确保wkhtmltopdf已添加到系统环境变量中,或在pdfkit.from_string中指定configuration参数指向wkhtmltopdf的路径。
内容的提问来源于stack exchange,提问作者user21968603
相关产品推荐
相关产品推荐

