基于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平台上新建过元服务,那么这里会自动显…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...
