使用 pyinstaller 打包 streamlit 应用
参考链接
https://blog.csdn.net/Helloorld_1/article/details/131767478
前言
Streamlit
是一个开源的Python库,它用于快速创建和分享数据应用。它专为数据科学家和工程师设计,使他们能够轻松地将数据脚本转换为交互式 web 应用。使用Streamlit
,你可以用少量的 Python 代码就能构建美观、功能强大的数据仪表盘,它支持热重载,这意味着你可以写代码和查看变化实时发生在浏览器中。
这意味着,很多时候我们在使用 Python 编程时,没有必要使用pyqt
或者专门写一个 web 前端页面来实现某个小功能,但又可以依托 Streamlit
快速实现界面化的交互功能。
但是因为Streamlit
应用,使用pyinstaller
进行打包时,会报错:
'streamlit' 不是内部或外部命令,也不是可运行的程序
让打包后的 exe 无法执行,但解决办法却十分简单,仅需将Scripts
环境中的steamlit.exe
文件和app.py
复制到入口文件同目录下即可。
具体实现流程如下:
1. 功能实现
示例项目为一个可视化批量生成二维码的应用qrcode_maker.py
:
# qrcode_maker.py
import io
import zipfile
import streamlit as st
import pandas as pd
import qrcode
from PIL import Image, ImageDraw, ImageFont
def generate_qr_code(content):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=1,
)
qr.add_data(content)
qr.make(fit=True)
img = qr.make_image(fill='black', back_color='white').convert('RGB')
return img
def add_text(draw, text, position, font_size, color):
font = ImageFont.truetype("simhei.ttf", font_size) # 确保您有这个字体文件或更换为您系统中的有效字体
draw.text(position, text, fill=color, font=font)
def main():
st.title('批量生成二维码工具')
uploaded_file = st.file_uploader("上传背景图片", type=['png', 'jpg', 'jpeg'])
background_buffer = io.BytesIO()
if uploaded_file is not None:
background_buffer.write(uploaded_file.getvalue())
background = Image.open(background_buffer)
# st.image(background, caption='上传的背景图片')
st.write(f"图片像素大小: {background.size[0]} x {background.size[1]}")
qr_content = st.text_input("二维码内容(预览功能)")
qr_size = st.number_input("二维码大小/像素", min_value=1, value=100)
qr_position = st.text_input("二维码位置(格式:x,y)", "100,100")
label_content = st.text_input("文字标签内容(预览功能)")
label_font_size = st.number_input("文字标签字体大小", min_value=1, value=20)
label_color = st.color_picker("文字标签颜色")
label_position = st.text_input("文字标签位置(格式:x,y)", "100,150")
if st.button("生成预览"):
if uploaded_file and qr_content:
background_buffer.seek(0)
background = Image.open(background_buffer)
qr_image = generate_qr_code(qr_content)
qr_image = qr_image.resize((qr_size, qr_size))
qr_position_tuple = tuple(map(int, qr_position.split(',')))
background.paste(qr_image, qr_position_tuple)
draw = ImageDraw.Draw(background)
label_position_tuple = tuple(map(int, label_position.split(',')))
add_text(draw, label_content, label_position_tuple, label_font_size, label_color)
st.image(background, caption='预览图')
else:
st.error("请上传背景图片和输入二维码内容")
file = st.file_uploader("上传xlsx或csv文件", type=['xlsx', 'csv'])
df = None
if file is not None:
if file.type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
df = pd.read_excel(file)
else:
df = pd.read_csv(file)
st.write(df)
if st.button("批量生成"):
if uploaded_file and df is not None:
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'a', zipfile.ZIP_DEFLATED) as zip_file:
background_buffer.seek(0)
background = Image.open(background_buffer)
for index, row in df.iterrows():
temp_image = background.copy()
qr_image = generate_qr_code(str(row['content']))
qr_image = qr_image.resize((qr_size, qr_size))
qr_position_tuple = tuple(map(int, qr_position.split(',')))
temp_image.paste(qr_image, qr_position_tuple)
draw = ImageDraw.Draw(temp_image)
label_position_tuple = tuple(map(int, label_position.split(',')))
add_text(draw, str(row['title']), label_position_tuple, label_font_size, label_color)
temp_buffer = io.BytesIO()
temp_image.save(temp_buffer, format='PNG')
temp_buffer.seek(0)
zip_file.writestr(f"{row['title']}.png", temp_buffer.read())
zip_buffer.seek(0)
st.success("批量生成成功!")
st.download_button(
label="下载二维码ZIP",
data=zip_buffer,
file_name="qrcodes.zip",
mime="application/zip"
)
else:
st.error("请上传背景图片和xlsx或csv文件")
if __name__ == "__main__":
main()
2. 创建纯净的打包环境
# 创建虚拟环境
python -m venv make_qrcode
# 激活环境
.\Scripts\activate
# 安装环境
pip install -r requirements.txt
3. 创建pyinstaller hooks
在主代码目录下新建hooks
文件夹,并创建一个hooks-streamlit.py
文件,代码如下:
from PyInstaller.utils.hooks import copy_metadata
datas = copy_metadata("streamlit")
4. 创建运行文件run_app.py
import streamlit.web.cli as stcli
import os, sys
def resolve_path(path):
resolved_path = os.path.abspath(os.path.join(os.getcwd(), path))
return resolved_path
if __name__ == "__main__":
# 这里的 streamlit app 是什么名字,需要修改下
sys.argv = [
"streamlit",
"run",
resolve_path("qrcode_maker.py"), # 这里
"--global.developmentMode=false",
]
sys.exit(stcli.main())
此时的文件结构
5. 打包和配置
5.1. 先打包一次,生成.spec
文件
pyinstaller --onefile --additional-hooks-dir=./hooks run_app.py --clean
5.2. 修改.spec
文件
# -*- mode: python ; coding: utf-8 -*-
# 添加以下部分代码
from PyInstaller.utils.hooks import collect_data_files
from PyInstaller.utils.hooks import copy_metadata
# 设置 streamlit 运行时目录,一般在 Lib/site-packages 里,如果你和我一样是用上述命令行创建的环境用这个 datas 配置就行,如果是用 pycharm 创建的,需要改下目录,使用第2个 .venv
# 本质就是要指定 运行时环境,一般报错都有提示;
datas = [("Lib/site-packages/streamlit/runtime","./streamlit/runtime")]
# datas = [(".venv/Lib/site-packages/streamlit/runtime","./streamlit/runtime")]
datas += collect_data_files("streamlit")
datas += copy_metadata("streamlit")
block_cipher = None
# 修改下面 a 里面的 datas 为 =datas
# 如果打包后缺失 module, 需要配置 hiddenimports,这里要看你打包后允许报的是缺失哪个模块,比如我这里就缺失 "qrcode"
a = Analysis(
['run_app.py'],
pathex=[],
binaries=[],
datas=datas,
hiddenimports=['qrcode'],
hookspath=['./hooks'],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='run_app',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
5.3. 再次打包,使用.spec
文件进行打包
pyinstaller run_app.spec --clean
5.4. 复制streamlit app
文件到 dist 目录
现在点击run_app.py
即可运行程序了
版权声明:
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
有限进步!
喜欢就支持一下吧
打赏
微信
支付宝