1
0

5 Commits f91a88eb03 ... 0a3a7021e7

Autor SHA1 Nachricht Datum
  zhch158_admin 0a3a7021e7 feat(launch): 添加Streamlit服务器热重载参数以支持实时更新 vor 2 Wochen
  zhch158_admin 090ba64eef fix(pyrightconfig): 修复配置文件,确保Python环境和类型检查设置正确 vor 2 Wochen
  zhch158_admin 633bf31226 docs(README): 添加Streamlit热更新说明及使用方式 vor 2 Wochen
  zhch158_admin fd2189a027 feat(ocr_layout): 添加安全高亮文本功能,避免替换base64编码内容 vor 2 Wochen
  zhch158_admin 7391165659 feat(config): 添加Streamlit配置文件,设置服务器、主题、浏览器和日志选项 vor 2 Wochen

+ 2 - 1
.vscode/launch.json

@@ -31,7 +31,8 @@
 			"args": [
 				"run",
 				"streamlit_ocr_validator.py",
-				"--theme.base=light"
+				"--theme.base=light",
+				"--server.runOnSave=true"
 			],
 			"justMyCode": false
 		},

+ 21 - 0
ocr_validator/.streamlit/config.toml

@@ -0,0 +1,21 @@
+[server]
+# 文件改动时自动重新运行
+runOnSave = true
+
+# 文件监控器类型 (auto, poll, watchdog, none)
+fileWatcherType = "auto"
+
+# 端口号
+port = 8501
+
+# 主题设置
+[theme]
+base = "light"
+
+[browser]
+# 自动打开浏览器
+gatherUsageStats = false
+
+[logger]
+# 日志级别
+level = "info"

+ 46 - 4
ocr_validator/README.md

@@ -217,11 +217,53 @@ streamlit run streamlit_ocr_validator.py
 2. 在 `ocr_validator_utils.py` 中添加数据解析函数
 3. 更新 `config/global.yaml` 添加数据源
 
-## 📄 许可证
+---
 
-[根据项目许可证填写]
+# Streamlit 热更新:
 
-## 🤝 贡献
+## 方式 1: 启动参数(最简单)
 
-欢迎提交 Issue 和 Pull Request!
+在启动命令中添加参数:
 
+```bash
+streamlit run streamlit_ocr_validator.py --server.runOnSave true
+```
+
+## 方式 2: 配置文件(推荐)
+
+创建 Streamlit 配置文件: 
+
+已创建 [](file:///Users/zhch158/workspace/repository.git/ocr_platform/ocr_validator/.streamlit/config.toml)
+
+## 方式 3: 更新 VS Code 调试配置 
+
+读取 [](file:///Users/zhch158/workspace/repository.git/ocr_platform/.vscode/launch.json#18-18),行 18 到 40
+
+Using "替换文件中的字符串"
+
+
+```
+
+```
+## 使用说明
+
+### 热更新工作原理
+
+1. **自动检测**:Streamlit 监控 `.py` 文件的变化
+2. **提示重新运行**:检测到变化后会显示 "Source file changed" 提示
+3. **手动/自动重新运行**:
+   - 手动:点击 "Rerun" 按钮
+   - 自动:配置 `runOnSave=true` 后自动重新运行
+
+### 快速测试
+
+修改 streamlit_ocr_validator.py 中的任何内容并保存,Streamlit 会:
+- ✅ 自动检测到文件变化
+- ✅ 立即重新加载应用
+- ✅ 保持当前页面状态(如果使用了 `st.session_state`)
+
+### 注意事项
+
+1. **监控范围**:只监控 Python 文件(`.py`),不监控数据文件
+2. **状态保持**:使用 `st.session_state` 保存状态可以在热更新后保留数据
+3. **性能影响**:如果项目很大,热更新可能需要几秒钟

+ 75 - 5
ocr_validator/ocr_validator_layout.py

@@ -10,6 +10,8 @@ from PIL import Image
 from typing import Dict, List, Optional
 import plotly.graph_objects as go
 from typing import Tuple
+import re
+import html
 
 from ocr_validator_utils import (
     rotate_image_and_coordinates,
@@ -64,6 +66,69 @@ class OCRLayoutManager:
         st.session_state[search_key] = ""
         st.session_state[quick_select_key] = 0
     
+    def _highlight_text_safely(self, content: str, text_to_highlight: str, 
+                               highlight_class: str, title: Optional[str] = None) -> str:
+        """
+        安全地高亮文本,避免替换base64编码中的内容
+        
+        Args:
+            content: 要处理的HTML内容
+            text_to_highlight: 要高亮的文本
+            highlight_class: 高亮样式类名
+            title: 鼠标悬停提示文本,默认为text_to_highlight
+        
+        Returns:
+            处理后的HTML内容
+        """
+        if not text_to_highlight or text_to_highlight not in content:
+            return content
+        
+        if title is None:
+            title = text_to_highlight
+        
+        # 转义特殊字符用于正则表达式
+        escaped_text = re.escape(text_to_highlight)
+        
+        # 找出所有base64编码区域的位置
+        # 匹配两种格式:
+        # 1. HTML: src="data:image/...;base64,..." 或 src='data:image/...;base64,...'
+        # 2. Markdown: ![...](data:image/...;base64,...)
+        # 使用更通用的模式来匹配base64数据
+        base64_pattern = r'data:image/[^;]+;base64,[A-Za-z0-9+/=]+'
+        base64_regions = []
+        
+        for match in re.finditer(base64_pattern, content):
+            base64_regions.append((match.start(), match.end()))
+        
+        # 找出所有要高亮文本的位置
+        text_pattern = re.compile(escaped_text)
+        matches = []
+        
+        for match in text_pattern.finditer(content):
+            start, end = match.start(), match.end()
+            
+            # 检查该位置是否在base64区域内
+            in_base64 = False
+            for base64_start, base64_end in base64_regions:
+                if base64_start <= start < base64_end:
+                    in_base64 = True
+                    break
+            
+            # 只保留不在base64区域内的匹配
+            if not in_base64:
+                matches.append((start, end))
+        
+        # 从后向前替换,避免位置偏移
+        for start, end in reversed(matches):
+            original_text = content[start:end]
+            # 转义HTML特殊字符
+            escaped_original = html.escape(original_text)
+            escaped_title = html.escape(title)
+            highlighted = f'<span class="{highlight_class}" title="{escaped_title}">{escaped_original}</span>'
+            content = content[:start] + highlighted + content[end:]
+        
+        return content
+    
     def clear_image_cache(self):
         """清理所有图像缓存"""
         self._rotated_image_cache.clear()
@@ -529,7 +594,7 @@ class OCRLayoutManager:
                             match_type = "no_bbox"
                     
                     # 🎯 应用高亮
-                    if len(selected_text) > 2:
+                    if len(selected_text) >= self.config.get('ocr', {}).get('min_text_length', 2):
                         # 1. 高亮原始文本
                         if selected_text in highlighted_content:
                             if match_type == "exact":
@@ -539,16 +604,21 @@ class OCRLayoutManager:
                             else:
                                 highlight_class = "highlight-text default"
                             
-                            highlighted_content = highlighted_content.replace(
+                            # 使用正则表达式避免替换base64编码中的内容
+                            highlighted_content = self._highlight_text_safely(
+                                highlighted_content,
                                 selected_text,
-                                f'<span class="{highlight_class}" title="{selected_text}">{selected_text}</span>'
+                                highlight_class
                             )
                         
                         # 2. 如果有 matched_text 且不同,也高亮
                         if matched_text and matched_text != selected_text and matched_text in highlighted_content:
-                            highlighted_content = highlighted_content.replace(
+                            # 使用正则表达式避免替换base64编码中的内容
+                            highlighted_content = self._highlight_text_safely(
+                                highlighted_content,
                                 matched_text,
-                                f'<span class="highlight-text ocr-match" title="OCR: {matched_text}">{matched_text}</span>'
+                                "highlight-text ocr-match",
+                                f"OCR: {matched_text}"
                             )
                 
                 # 🎯 调用渲染方法(样式已内置)

+ 7 - 1
pyrightconfig.json

@@ -5,6 +5,12 @@
     "../PaddleX"
   ],
   "pythonVersion": "3.12",
-  "typeCheckingMode": "basic"
+  "pythonPlatform": "Darwin",
+  "typeCheckingMode": "basic",
+  "venvPath": "/opt/miniconda3/envs",
+  "venv": "mineru2",
+  "reportMissingImports": true,
+  "reportMissingTypeStubs": false,
+  "useLibraryCodeForTypes": true
 }