基于streamlit搭简易前端页面
前端小白第一次用streamlit搭简易页面,记录一下。
一些tips
每次与页面进行交互,如点击按钮、上传文件等,streamlit就会重新运行整个页面的所有代码。如果在页面渲染前需要对上传文件做很复杂的操作,重新运行所有代码就会重复这个过程,会导致页面加载内容较慢。因此可以把不会变化的内容存起来,避免重新对文件进行处理。
streamlit的渲染顺序和代码定义的分布一致,例如先在代码里写了一级标题就先渲染一级标题,先写占位符就先渲染占位符。所以排序在前的部分渲染时未渲染部分会是灰色。
一些命令
将页面分成两列
left_col, right_col = st.columns([1, 1])
在占位符里动态渲染变化内容
left_content_placeholder = st.empty()with left_content_placeholder.container():st.components.v1.html(st.session_state['current_content'], height=600, scrolling=True)
在页面加载时滚动到当前匹配的结果
scroll_script = """<script>document.addEventListener('DOMContentLoaded', function() {var element = document.getElementById('current_match');if(element) {element.scrollIntoView({ behavior: 'smooth', block: 'center' });}});</script>"""
代码示例
import streamlit as st
from docx import Document
import mammoth
import os
from bs4 import BeautifulSoup
import re
from PIL import Image
import base64
import markdown# 解析Word文档的函数,保留原格式
def parse_word_document_for_table(file):try:# 使用Mammoth将文档转换为HTML以保留格式result = mammoth.convert_to_html(file)html_content = result.value# 使用python-docx处理表格部分document = Document(file)tables_html = ""# 存储所有表格的数据all_tables_data = []# 遍历文档中的所有段落和表格prev_paragraph = ""for element in document.element.body:if element.tag.endswith('tbl'): # 检查是否为表格元素# 获取当前表格table = document.tables[len(all_tables_data)] # 获取当前表格# 使用前一个段落作为表格标题table_title = prev_paragraph.strip() if prev_paragraph else "无标题"# 在表格前添加标题tables_html += f"<h3>{table_title}</h3>" # 这里使用<h3>标签来显示标题,或者根据需要使用其他标签tables_html += "<table border='1' style='border: 1px solid black; border-collapse: collapse; width: 100%;'>"for row in table.rows:tables_html += "<tr>"for cell in row.cells:cell_content = cell.text.replace('\n', '<br>') # 处理单元格内换行tables_html += f"<td style='padding: 5px; border: 1px solid black; text-align: left; vertical-align: top;'>{cell_content}</td>"tables_html += "</tr>"tables_html += "</table><br>"all_tables_data.append(1)elif element.tag.endswith('p'): # 检查是否为段落prev_paragraph = element.text # 获取当前段落的文本作为表格的标题# 将表格的HTML拼接到文档内容中# html_content += tables_htmlhtml_content = tables_html return html_contentexcept Exception as e:return f"Error: {str(e)}"def parse_word_document(file):try:# 使用Mammoth将文档转换为HTML以保留格式result = mammoth.convert_to_html(file)html_content = result.valuereturn html_contentexcept Exception as e:return f"Error: {str(e)}"def extract_markdown(file):content_bytes = file.read()content_text = content_bytes.decode('utf-8')html = markdown.markdown(content_text)return htmldef main():DOCUMENT_PATHS = {"标题验证": "./headings.md","表格信息提取": "./tables_information.md","表格验证结果": "./output.md"}# 设置页面为宽布局st.set_page_config(page_title="项目报告核验", layout="wide")logo_path = './logo.jpg'st.image(logo_path, width=200)st.markdown("<h1 style='text-align: center;'>项目报告核验</h1>", unsafe_allow_html=True)# 使用自定义 CSS 来居中标题st.markdown("""<style>.left-header {display: flex;justify-content: center;align-items: center;height: 100%;text-align: center;}.right-header {display: flex;justify-content: center;align-items: center;height: 100%;text-align: center;}</style>""", unsafe_allow_html=True)# 创建两列布局,左边显示上传文档,右边显示a、b、c文档left_col, right_col = st.columns([1, 1]) # 调整列的宽度比例,左边更宽# 初始化状态if 'first_doc_uploaded' not in st.session_state:st.session_state['first_doc_uploaded'] = Trueif 'left_doc_uploaded' not in st.session_state:st.session_state['left_doc_uploaded'] = Falseif 'right_doc_content' not in st.session_state:st.session_state['right_doc_content'] = ""if 'right_doc_error' not in st.session_state:st.session_state['right_doc_error'] = ""if 'matches' not in st.session_state:st.session_state['matches'] = []if 'current_index' not in st.session_state:st.session_state['current_index'] = -1if 'first_render' not in st.session_state:st.session_state['first_render'] = Truewith right_col: st.markdown('<div class="right-header"><h2>内容展示区域</h2></div>', unsafe_allow_html=True) tab_selection2 = st.selectbox("选择展示内容:",("标题验证", "表格信息提取", "表格验证结果")) # 上传文档部分(左列)with left_col:st.markdown('<div class="left-header"><h2>项目文件上传及原文展示</h2></div>', unsafe_allow_html=True)uploaded_file = st.file_uploader("上传Word文档", type=["docx"])search_keyword = st.text_input("输入要搜索的关键词")search_button = st.button("查找/查找下一个")left_content_placeholder = st.empty()# 处理文件上传逻辑if not uploaded_file:st.session_state['current_content'] = Nonest.session_state['left_doc_uploaded'] = Falseif not st.session_state['current_content'] and uploaded_file:try:# 解析上传的Word文档content = parse_word_document_for_table(uploaded_file)st.session_state['left_doc_uploaded'] = Truest.session_state['current_content'] = contentexcept Exception as e:st.error(str(e))# if st.session_state['left_doc_uploaded']:with left_content_placeholder.container():st.components.v1.html(st.session_state['current_content'], height=600, scrolling=True)# 处理搜索按钮点击事件,只有在文档已上传后才生效if st.session_state['left_doc_uploaded'] and search_button:if search_keyword!= st.session_state.get('last_search_keyword', ''):# 关键词变化了,重新执行搜索st.session_state['last_search_keyword'] = search_keywordst.session_state['matches'] = [] # 清空上次的匹配项st.session_state['current_index'] = -1 # 重置为-1,表示没有匹配项content = parse_word_document_for_table(uploaded_file)st.session_state['current_content'] = content # 重置为原始文档内容# 执行新的搜索if search_keyword:soup_new_search = BeautifulSoup(content, 'html.parser')paragraphs = soup_new_search.find_all(['p', 'span', 'div', 'td', 'h3'])pattern = re.compile(re.escape(search_keyword), re.IGNORECASE)matches = []for idx, paragraph in enumerate(paragraphs):if pattern.search(paragraph.get_text()):highlighted_text = pattern.sub(lambda match: f"<mark style='background-color: yellow;'>{match.group(0)}</mark>", paragraph.decode_contents())paragraph.clear()paragraph.append(BeautifulSoup(highlighted_text, 'html.parser'))matches.append(paragraph)st.session_state['matches'] = matchesst.session_state['current_index'] = 0 # 重新开始从第一个匹配项st.session_state['current_content'] = str(soup_new_search) # 更新文档内容为高亮后的内容if matches:for idx, paragraph in enumerate(matches):if idx == st.session_state['current_index']:paragraph['id'] = 'current_match'paragraph['style'] = 'background-color: orange;' # 当前匹配项用橙色高亮else:paragraph['id'] = ''paragraph['style'] = 'background-color: yellow;' # 其他匹配项用黄色高亮# 保存高亮后的内容st.session_state['current_content'] = str(soup_new_search)elif search_keyword == st.session_state.get('last_search_keyword', ''):# 关键词没有变化,查找下一个匹配项if st.session_state['matches']:# 只有在有匹配项时才进行查找st.session_state['current_index'] = (st.session_state['current_index'] + 1) % len(st.session_state['matches'])soup_repeat = BeautifulSoup(st.session_state['current_content'], 'html.parser')paragraphs = soup_repeat.find_all(['p', 'span', 'div', 'td', 'h3'])pattern = re.compile(re.escape(search_keyword), re.IGNORECASE)# 更新高亮颜色idx =-1for paragraph in paragraphs:# 对当前匹配项和上一项进行样式更新if pattern.search(paragraph.get_text()):idx +=1if idx == st.session_state['current_index']:# 当前匹配项用橙色高亮paragraph['id'] = 'current_match'paragraph['style'] = 'background-color: orange;'else:# 其他匹配项用黄色高亮paragraph['id'] = ''paragraph['style'] = 'background-color: yellow;'# 保存高亮后的内容st.session_state['current_content'] = str(soup_repeat)#在页面加载时滚动到当前匹配的结果scroll_script = """<script>document.addEventListener('DOMContentLoaded', function() {var element = document.getElementById('current_match');if(element) {element.scrollIntoView({ behavior: 'smooth', block: 'center' });}});</script>"""with left_content_placeholder.container():st.components.v1.html(st.session_state['current_content']+ scroll_script , height=600, scrolling=True)with right_col: right_content_placeholder = st.empty() if st.session_state['left_doc_uploaded']:selected_document = DOCUMENT_PATHS.get(tab_selection2)else:selected_document = Nonest.session_state['right_doc_content'] = ""right_content_placeholder.empty()if 'right_selected_doc' not in st.session_state:st.session_state['right_selected_doc'] = Noneif selected_document:try:with open(selected_document, "rb") as file:content = extract_markdown(file)if content.startswith("Error"):st.session_state['right_doc_error'] = contentelse:st.session_state['right_doc_content'] = contentst.session_state['right_selected_doc'] = selected_documentexcept Exception as e:st.error(str(e))if st.session_state["right_doc_content"]:with right_content_placeholder.container():st.markdown(f"""<div style="height: 750px; overflow-y: scroll; padding: 10px; border: 1px solid #ccc; border-radius: 5px;">{st.session_state["right_doc_content"]}</div>""", unsafe_allow_html=True)if __name__ == "__main__":main()
相关文章:
基于streamlit搭简易前端页面
前端小白第一次用streamlit搭简易页面,记录一下。 一些tips 每次与页面进行交互,如点击按钮、上传文件等,streamlit就会重新运行整个页面的所有代码。如果在页面渲染前需要对上传文件做很复杂的操作,重新运行所有代码就会重复这…...

Harmony Next开发通过bindSheet绑定半模态窗口
示例概述 Harmony Next开发通过bindSheet绑定半模态窗口 知识点 半模态窗口父子组件传值 组件 LoginComponent Component struct LoginComponent {// Prop 父子单项绑定值Prop message:string // Link 父子双向绑定值Link userName:stringLink password:stringLink isSh…...

YOLOv11改进,YOLOv11添加DLKA-Attention可变形大核注意力,WACV2024 ,二次创新C3k2结构
摘要 作者引入了一种称为可变形大核注意力 (D-LKA Attention) 的新方法来增强医学图像分割。这种方法使用大型卷积内核有效地捕获体积上下文,避免了过多的计算需求。D-LKA Attention 还受益于可变形卷积,以适应不同的数据模式。 理论介绍 大核卷积(Large Kernel Convolu…...

【51单片机】矩阵按键快速上手
51单片机矩阵按键是一种在单片机应用系统中广泛使用的按键排列方式,特别适用于需要多个按键但I/O口资源有限的情况。以下是对51单片机矩阵按键的详细介绍: 一、矩阵按键的基本概念 定义:矩阵按键,又称行列键盘,是…...

一文说清:git reset HEAD原理
1 使用add命令,将文件添加到暂存区 命令如下: 对比结果如下: 2 使用reset HEAD命令 如下: 结果对比如下: 忽略logs目录下的内容。 发现只是修改了index暂存区的内容。删掉了原来添加到暂存区的对象ID&#x…...
【前端面试题】书、定位问题、困难
看过什么书 《JavaScript 高级程序设计(第 4 版)》(作者:Matt Frisbie) 这是一本深入学习 JavaScript 语言的经典书籍。它详细地涵盖了 JavaScript 的高级特性,包括原型链、闭包、异步编程等复杂概念。以闭…...

WADesk 升级 Webpack5 一些技术细节认识5和4的区别在哪里
背景 升级过程中发现有很多新的知识点,虽然未来可能永远都不会再遇到,但是仍然是一次学习的好机会,可以让自己知道,打包软件的进化之路,和原来 Webpack 4 版本的差异在哪里。 移除的依赖记录 babel/register: 在 Nod…...
学习 Dockerfile 常用指令
学习 Dockerfile 常用指令 在构建 Docker 镜像时,Dockerfile 文件是一份至关重要的配置文件,它定义了构建镜像的所有步骤。通过在 Dockerfile 中使用不同的指令(命令),我们可以控制镜像的构建过程、设置环境、指定执行…...

day11 性能测试(3)——Jmeter 断言+关联
【没有所谓的运气🍬,只有绝对的努力✊】 目录 1、复习 2、查看结果树 多个http请求原因分析 3、作业 4、Jmeter断言 4.1 响应断言 4.1.1 案例 4.1.2 小结 4.2 json断言 4.2.1 案例 4.2.2 小结 4.3 断言持续时间 4.3.1 案例 4.3.2 小结 4.…...
ES6中的map和set
Map JS的数据对象(Obejct),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键(一定程度上对其的使用有限制) 比如下面代码 const data {} const element document.…...

UE5中实现Billboard公告板渲染
公告板(Billboard)通常指永远面向摄像机的面片,游戏中许多技术都基于公告板,例如提示拾取图标、敌人血槽信息等,本文将使用UE5和材质节点制作一个公告板。 Gif效果: 网格效果: 1.思路 通过…...

泊松编辑 possion editing图像合成笔记
开源地址: GitHub - kono-dada/Reproduction-of-possion-image-editing 掩码必须是矩形框...

#渗透测试#漏洞挖掘#红蓝攻防#SRC漏洞挖掘
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…...

系列2:基于Centos-8.6Kubernetes 集成GPU资源信息
每日禅语 自省,就是自我反省、自我检查,自知己短,从而弥补短处、纠正过失。佛陀强调自觉觉他,强调以达到觉行圆满为修行的最高境界。要改正错误,除了虚心接受他人意见之外,还要不忘时时观照己身。自省自悟之…...
Coturn 实战指南:WebRTC 中的 NAT 穿透利器
1. 什么是 Coturn? Coturn 是一种开源的 TURN(Traversal Using Relays around NAT)服务器,用于解决 NAT 穿透问题。它帮助客户端在受限网络环境(例如防火墙或 NAT 后面)中实现双向通信,常用于 WebRTC 应用、VoIP、在线游戏等场景。 2. Cotur…...

基于卷积神经网络的Caser算法
将一段交互序列嵌入到一个以时间为纵轴的平面空间中形成“一张图”后,基于卷积序列嵌入的推荐(Caser)算法利用多个不同大小的卷积滤波器,来捕捉序列中物品间的点级(point-level)、联合的(union-…...
自闭症在学校:了解自闭症的特点,优化学校教育方式
在教育的广阔天地里,每一片叶子都承载着生命的独特韵律,每一朵花都在以自己的方式绽放。然而,在特殊教育的花园里,有一群孩子,他们或许不那么容易被看见,不那么容易与世界沟通,但他们同样拥有学…...

多线程的知识总结(8):用 thread 类 或全局 async (...) 函数,创建新线程时,谁才是在新线程里第一个被执行的函数
(40)用 thread 类 或全局 async (…) 函数,创建新线程时,谁才是在新线程里第一个被执行的函数? 弄清楚这个问题,有利于推测和理解线程中代码的执行流程。根据 thread 类 和 async (…࿰…...
ArcGIS地理空间平台manager存在任意文件读取漏洞
免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...

HarmonyOS Next 元服务新建到上架全流程
HarmonyOS Next 元服务新建到上架全流程 接上篇 这篇文章的主要目的是介绍元服务从新建到上家的完整流程 在AGC平台上新建一个项目 链接 一个项目可以多个应用 AGC新建一个元服务应用 新建一个本地元服务项目 如果成功在AGC平台上新建过元服务,那么这里会自动显…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...