Streamlit基本API

Title Streamlit基本API
Framework Python
User wy8817399@vip.qq.com
Id 73
Created 4/1/26, 4:58 PM
Modified 4/1/26, 5:03 PM
Published Yes
Content

1、组件使用方法

1.1 运行代码
# 创建第一个应用(app.py)
import streamlit as st
st.title("我的第一个Streamlit应用")
st.write("你好,世界!")

# 在终端运行
streamlit run app.py
streamlit hello

 

1.2 文本组件
st.title("主标题")           # 主标题
st.header("章节标题")        # 大标题
st.subheader("子标题")       # 子标题
st.text("普通文本")          # 普通文本
st.write("万能文本/变量")     # 显示任何对象
st.markdown("**Markdown**支持") # Markdown语法

 

1.3 数据展示组件
st.dataframe(pd.DataFrame()) # 交互式表格
st.table([1,2,3])            # 静态表格
st.json({"key": "value"})     # 显示JSON格式

 

1.4 输入控件
text = st.text_input("输入文本")      # 文本输入
number = st.number_input("输入数字")   # 数字输入
date = st.date_input("选择日期")       # 日期选择
time = st.time_input("选择时间")       # 时间选择
is_checked = st.checkbox("复选框")    # 复选框
selected = st.radio("单选按钮", ['1', '2', '3', '4']) # 单选按钮
multi = st.multiselect("多选", ['1', '2', '3', '4'])  # 多选下拉
slider = st.slider("滑块", 0, 100)     # 滑块
st.button("确认")              # 按钮
st.file_uploader("上传文件")   # 文件上传

 

2、布局使用

2.1 侧边栏
# 所有输入组件添加sidebar前缀即可放入侧边栏
st.sidebar.selectbox("选项", ['1', '2', '3', '4'])

 

2.2 分列布局
col1, col2 = st.columns(2)  # 创建两列

with col1:
    st.write("第一列内容")

with col2:
    st.write("第二列内容")

 

2.3 标签页
tab1, tab2 = st.tabs(["主页", "分析"])

with tab1:
    st.write("主页内容")

with tab2:
    st.write("分析内容")

 

2.4 容器
container = st.container()
container.write("容器内的内容")

 

3、 项目页面

import streamlit as st

# 页面配置
st.set_page_config(
    page_title="智能文档检索助手",
    page_icon="🤖",
    layout="wide",
    initial_sidebar_state="collapsed"
)

# 自定义CSS样式
st.markdown("""
<style>
    /* 聊天容器 */
    .chat-container {
        background: white;
        border-radius: 12px;
        box-shadow: 0 8px 32px rgba(0,0,0,0.1);
        margin: 20px auto;
        max-width: 800px;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    /* 头部样式 */
    .chat-header {
        background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
        color: white;
        padding: 20px;
        text-align: center;
        border-radius: 12px 12px 0 0;
    }

    .chat-title {
        font-size: 24px;
        font-weight: 600;
        margin: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;
    }

    .chat-subtitle {
        font-size: 14px;
        opacity: 0.9;
        margin-top: 5px;
    }

    /* 聊天消息区域 */
    .chat-messages {
        flex: 1;
        overflow-y: auto;
        padding: 20px;
        background: #f8f9fa;
    }

    /* 消息样式 */
    .message {
        margin-bottom: 16px;
        display: flex;
        align-items: flex-start;
        gap: 12px;
    }

    .message.user {
        flex-direction: row-reverse;
    }

    .message-avatar {
        width: 36px;
        height: 36px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 18px;
        flex-shrink: 0;
    }

    .user-avatar {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
    }

    .assistant-avatar {
        background: linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%);
        color: white;
    }

    .message-content {
        max-width: 70%;
        padding: 12px 16px;
        border-radius: 18px;
        font-size: 14px;
        line-height: 1.4;
    }

    .user-message {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        border-bottom-right-radius: 4px;
    }

    .assistant-message {
        background: white;
        color: #333;
        border: 1px solid #e1e5e9;
        border-bottom-left-radius: 4px;
        box-shadow: 0 1px 2px rgba(0,0,0,0.1);
    }

    /* 流式输出动画 */
    .streaming-cursor::after {
        content: '▊';
        animation: blink 1s infinite;
        color: #667eea;
    }

    @keyframes blink {
        0%, 50% { opacity: 1; }
        51%, 100% { opacity: 0; }
    }

    /* 文档卡片样式 */
    .doc-card {
        background: 000000;
        border: 1px solid #e1e5e9;
        border-radius: 8px;
        padding: 12px;
        margin: 8px 0;
    }

    .doc-card:hover {
        border-color: #667eea;
        box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
    }

    /* 状态指示器 */
    .status-indicator {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        padding: 4px 12px;
        border-radius: 20px;
        font-size: 12px;
        font-weight: 500;
    }

    .status-rag {
        background: #e3f2fd;
        color: #1976d2;
    }

    .status-normal {
        background: #f3e5f5;
        color: #7b1fa2;
    }

    /* 隐藏Streamlit默认元素 */
    #MainMenu {visibility: hidden;}
    footer {visibility: hidden;}
    header {visibility: hidden;}

    /* 自定义按钮样式 */
    .stButton > button {
        background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
        color: white;
        border: none;
        border-radius: 8px;
        padding: 8px 16px;
        font-weight: 500;
        transition: all 0.2s;
    }

    .stButton > button:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
    }

    /* 响应式设计 */
    @media (max-width: 768px) {
        .chat-container {
            margin: 10px;
            height: 85vh;
        }

        .message-content {
            max-width: 85%;
        }
    }
</style>
""", unsafe_allow_html=True)



def display_message(role, content, docs=None):
    """显示静态消息"""
    message_class = "message user" if role == "user" else "message"
    avatar_class = "user-avatar" if role == "user" else "assistant-avatar"
    content_class = "user-message" if role == "user" else "assistant-message"
    avatar_icon = "👤" if role == "user" else "🤖"

    st.markdown(f"""
    <div class="{message_class}">
        <div class="message-avatar {avatar_class}">
            {avatar_icon}
        </div>
        <div class="message-content {content_class}">
            {content}
        </div>
    </div>
    """, unsafe_allow_html=True)




def main():
    # 侧边栏
    with st.sidebar:
        st.markdown("### 📁 文档管理")

        # 文档上传
        uploaded_files = st.file_uploader(
            "上传知识库文档",
            type=['pdf', 'docx', 'txt'],
            accept_multiple_files=True,
            help="支持 PDF、Word 和txt文件"
        )

        if uploaded_files:
            for uploaded_file in uploaded_files:
                if st.button(f"📤 处理 {uploaded_file.name}", key=f"process_{uploaded_file.name}"):
                    st.rerun()

        st.markdown("---")

        # 已有文档
        st.markdown("### 📚 知识库")
        documents = [{'filename': '1.txt', 'id': 1, 'created_at': {'strftime': '1:20'}, 'chunk_count': 10},
                     {'filename': '2.txt', 'chunk_count': 10, 'id': 2, 'created_at': {'strftime': '1:20'}}]

        if documents:
            doc_options = {f"{doc['filename']}": doc['id'] for doc in documents}
            selected_docs = st.multiselect(
                "选择知识源",
                options=list(doc_options.keys()),
                help="选择后将基于文档内容回答问题"
            )
            selected_doc_ids = [doc_options[doc] for doc in selected_docs]

            # 显示文档列表
            for doc in documents:
                st.markdown(f"""
                <div class="doc-card">
                    <strong>📄 {doc['filename']}</strong><br>
                    <small>📅 {doc['created_at']['strftime']}</small><br>
                    <small>📊 {doc['chunk_count']} 个文档块</small>
                </div>
                """, unsafe_allow_html=True)
        else:
            st.info("暂无文档,请先上传")
            selected_doc_ids = []

        total_docs = len(documents) if documents else 0
        st.metric("📊 文档数", total_docs)

        if st.button("🗑️ 清空对话"):
            st.rerun()

    # 主聊天界面
    st.markdown("""
    <div class="chat-container">
        <div class="chat-header">
            <div class="chat-title">
                🤖 智能文档检索助手
            </div>
            <div class="chat-subtitle">
                基于知识库的智能问答系统
            </div>
        </div>
    </div>
    """, unsafe_allow_html=True)

    # 状态显示
    col1, col2 = st.columns([2, 1])
    with col1:
        if selected_doc_ids:
            st.markdown(f"""
            <div class="status-indicator status-rag">
                🔍 知识库模式 ({len(selected_doc_ids)} 个文档)
            </div>
            """, unsafe_allow_html=True)
        else:
            st.markdown("""
            <div class="status-indicator status-normal">
                💭 普通对话模式
            </div>
            """, unsafe_allow_html=True)

    # 聊天消息显示区域
    chat_container = st.container()

    with chat_container:
        # 欢迎消息
        st.markdown("""
            <div class="message">
                <div class="message-avatar assistant-avatar">🤖</div>
                <div class="message-content assistant-message">
                    👋 你好!我是你的AI智能助手。<br><br>
                    💡 <strong>我能做什么:</strong><br>
                    • 📚 基于你上传的文档回答问题<br>
                    • 💬 进行日常对话交流<br>
                    • 🔍 提供准确的信息检索<br><br>
                    请上传文档或直接开始对话吧!
                </div>
            </div>
            """, unsafe_allow_html=True)

    # 用户输入
    if prompt := st.chat_input("💬 输入你的问题..."):
        # 显示用户消息
        display_message("user", prompt)
        print(prompt)


if __name__ == "__main__":
    main()