feat: 实现CutThenThink P0阶段核心功能
项目初始化 - 创建完整项目结构(src/, data/, docs/, examples/, tests/) - 配置requirements.txt依赖 - 创建.gitignore P0基础框架 - 数据库模型:Record模型,6种分类类型 - 配置管理:YAML配置,支持AI/OCR/云存储/UI配置 - OCR模块:PaddleOCR本地识别,支持云端扩展 - AI模块:支持OpenAI/Claude/通义/Ollama,6种分类 - 存储模块:完整CRUD,搜索,统计,导入导出 - 主窗口框架:侧边栏导航,米白配色方案 - 图片处理:截图/剪贴板/文件选择/图片预览 - 处理流程整合:OCR→AI→存储串联,Markdown展示,剪贴板复制 - 分类浏览:卡片网格展示,分类筛选,搜索,详情查看 技术栈 - PyQt6 + SQLAlchemy + PaddleOCR + OpenAI/Claude SDK - 共47个Python文件,4000+行代码 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
359
examples/ai_example.py
Normal file
359
examples/ai_example.py
Normal file
@@ -0,0 +1,359 @@
|
||||
"""
|
||||
AI 模块使用示例
|
||||
|
||||
演示如何使用 AI 模块进行文本分类
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 添加项目根目录到 Python 路径
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from src.core.ai import (
|
||||
CategoryType,
|
||||
ClassificationResult,
|
||||
AIClassifier,
|
||||
classify_text,
|
||||
)
|
||||
|
||||
|
||||
def example_classify_with_openai():
|
||||
"""
|
||||
示例 1: 使用 OpenAI 进行分类
|
||||
|
||||
注意:需要安装 openai 库并配置 API key
|
||||
pip install openai
|
||||
"""
|
||||
print("=" * 60)
|
||||
print("示例 1: 使用 OpenAI 进行分类")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# 创建 OpenAI 客户端
|
||||
client = AIClassifier.create_client(
|
||||
provider="openai",
|
||||
api_key="your-openai-api-key", # 替换为实际的 API key
|
||||
model="gpt-4o-mini",
|
||||
temperature=0.7,
|
||||
max_tokens=2000,
|
||||
)
|
||||
|
||||
# 测试文本
|
||||
test_text = """
|
||||
今天要完成的任务:
|
||||
1. 完成项目文档
|
||||
2. 修复 Bug #123
|
||||
3. 参加团队会议
|
||||
4. 代码审查
|
||||
"""
|
||||
|
||||
print(f"输入文本:\n{test_text}\n")
|
||||
|
||||
# 进行分类
|
||||
try:
|
||||
result = client.classify(test_text)
|
||||
|
||||
print("分类结果:")
|
||||
print(f" 分类: {result.category}")
|
||||
print(f" 置信度: {result.confidence:.2f}")
|
||||
print(f" 标题: {result.title}")
|
||||
print(f" 标签: {', '.join(result.tags)}")
|
||||
print(f" 理由: {result.reasoning}")
|
||||
print(f"\n生成的 Markdown 内容:")
|
||||
print(result.content)
|
||||
|
||||
except Exception as e:
|
||||
print(f"分类失败: {e}")
|
||||
print("提示:请确保已安装 openai 库并配置正确的 API key")
|
||||
|
||||
|
||||
def example_classify_with_claude():
|
||||
"""
|
||||
示例 2: 使用 Claude 进行分类
|
||||
|
||||
注意:需要安装 anthropic 库并配置 API key
|
||||
pip install anthropic
|
||||
"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 2: 使用 Claude 进行分类")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# 创建 Claude 客户端
|
||||
client = AIClassifier.create_client(
|
||||
provider="anthropic",
|
||||
api_key="your-anthropic-api-key", # 替换为实际的 API key
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
temperature=0.7,
|
||||
max_tokens=2000,
|
||||
)
|
||||
|
||||
# 测试文本 - 笔记类型
|
||||
test_text = """
|
||||
Python 装饰器是一种强大的功能,它允许在不修改原函数代码的情况下增强函数功能。
|
||||
|
||||
基本语法:
|
||||
@decorator_name
|
||||
def function():
|
||||
pass
|
||||
|
||||
常见用途:
|
||||
- 日志记录
|
||||
- 性能测试
|
||||
- 权限验证
|
||||
- 缓存
|
||||
"""
|
||||
|
||||
print(f"输入文本:\n{test_text}\n")
|
||||
|
||||
# 进行分类
|
||||
try:
|
||||
result = client.classify(test_text)
|
||||
|
||||
print("分类结果:")
|
||||
print(f" 分类: {result.category}")
|
||||
print(f" 置信度: {result.confidence:.2f}")
|
||||
print(f" 标题: {result.title}")
|
||||
print(f" 标签: {', '.join(result.tags)}")
|
||||
print(f" 理由: {result.reasoning}")
|
||||
print(f"\n生成的 Markdown 内容:")
|
||||
print(result.content)
|
||||
|
||||
except Exception as e:
|
||||
print(f"分类失败: {e}")
|
||||
print("提示:请确保已安装 anthropic 库并配置正确的 API key")
|
||||
|
||||
|
||||
def example_classify_with_qwen():
|
||||
"""
|
||||
示例 3: 使用通义千问进行分类
|
||||
|
||||
注意:需要阿里云 API key
|
||||
"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 3: 使用通义千问进行分类")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# 创建通义千问客户端
|
||||
client = AIClassifier.create_client(
|
||||
provider="qwen",
|
||||
api_key="your-qwen-api-key", # 替换为实际的 API key
|
||||
model="qwen-turbo",
|
||||
temperature=0.7,
|
||||
max_tokens=2000,
|
||||
)
|
||||
|
||||
# 测试文本 - 灵感类型
|
||||
test_text = """
|
||||
突然想到一个产品创意:做一个智能截图管理工具!
|
||||
|
||||
核心功能:
|
||||
- 自动 OCR 识别截图文字
|
||||
- AI 智能分类整理
|
||||
- 自动生成待办事项
|
||||
- 云端同步多设备
|
||||
|
||||
技术栈:
|
||||
- Python + PyQt6 桌面应用
|
||||
- PaddleOCR 本地识别
|
||||
- OpenAI/Claude AI 分类
|
||||
- SQLite 本地存储
|
||||
"""
|
||||
|
||||
print(f"输入文本:\n{test_text}\n")
|
||||
|
||||
# 进行分类
|
||||
try:
|
||||
result = client.classify(test_text)
|
||||
|
||||
print("分类结果:")
|
||||
print(f" 分类: {result.category}")
|
||||
print(f" 置信度: {result.confidence:.2f}")
|
||||
print(f" 标题: {result.title}")
|
||||
print(f" 标签: {', '.join(result.tags)}")
|
||||
print(f" 理由: {result.reasoning}")
|
||||
print(f"\n生成的 Markdown 内容:")
|
||||
print(result.content)
|
||||
|
||||
except Exception as e:
|
||||
print(f"分类失败: {e}")
|
||||
print("提示:请确保已配置正确的通义千问 API key")
|
||||
|
||||
|
||||
def example_classify_with_ollama():
|
||||
"""
|
||||
示例 4: 使用本地 Ollama 进行分类
|
||||
|
||||
注意:需要先安装并运行 Ollama
|
||||
https://ollama.ai/
|
||||
"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 4: 使用本地 Ollama 进行分类")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# 创建 Ollama 客户端
|
||||
client = AIClassifier.create_client(
|
||||
provider="ollama",
|
||||
api_key="", # Ollama 不需要 API key
|
||||
model="llama3.2", # 或其他已下载的模型
|
||||
temperature=0.7,
|
||||
max_tokens=2000,
|
||||
timeout=120, # 本地模型可能需要更长时间
|
||||
)
|
||||
|
||||
# 测试文本 - 搞笑类型
|
||||
test_text = """
|
||||
程序员最讨厌的四件事:
|
||||
1. 写注释
|
||||
2. 写文档
|
||||
3. 别人不写注释
|
||||
4. 别人不写文档
|
||||
|
||||
为什么程序员总是分不清万圣节和圣诞节?
|
||||
因为 Oct 31 == Dec 25
|
||||
"""
|
||||
|
||||
print(f"输入文本:\n{test_text}\n")
|
||||
|
||||
# 进行分类
|
||||
try:
|
||||
result = client.classify(test_text)
|
||||
|
||||
print("分类结果:")
|
||||
print(f" 分类: {result.category}")
|
||||
print(f" 置信度: {result.confidence:.2f}")
|
||||
print(f" 标题: {result.title}")
|
||||
print(f" 标签: {', '.join(result.tags)}")
|
||||
print(f" 理由: {result.reasoning}")
|
||||
print(f"\n生成的 Markdown 内容:")
|
||||
print(result.content)
|
||||
|
||||
except Exception as e:
|
||||
print(f"分类失败: {e}")
|
||||
print("提示:请确保已安装并运行 Ollama 服务")
|
||||
|
||||
|
||||
def example_classify_with_config():
|
||||
"""
|
||||
示例 5: 使用配置文件进行分类
|
||||
|
||||
从配置文件读取 AI 配置
|
||||
"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 5: 使用配置文件进行分类")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
try:
|
||||
from src.config.settings import get_settings
|
||||
|
||||
# 加载配置
|
||||
settings = get_settings()
|
||||
|
||||
# 检查配置
|
||||
print("当前 AI 配置:")
|
||||
print(f" 提供商: {settings.ai.provider}")
|
||||
print(f" 模型: {settings.ai.model}")
|
||||
print(f" 温度: {settings.ai.temperature}")
|
||||
print(f" 最大 tokens: {settings.ai.max_tokens}")
|
||||
print()
|
||||
|
||||
# 测试文本
|
||||
test_text = """
|
||||
API 接口文档
|
||||
|
||||
GET /api/users
|
||||
获取用户列表
|
||||
|
||||
参数:
|
||||
- page: 页码(默认 1)
|
||||
- limit: 每页数量(默认 20)
|
||||
|
||||
返回:
|
||||
{
|
||||
"users": [...],
|
||||
"total": 100,
|
||||
"page": 1
|
||||
}
|
||||
"""
|
||||
|
||||
print(f"输入文本:\n{test_text}\n")
|
||||
|
||||
# 使用配置进行分类
|
||||
result = classify_text(test_text, settings.ai)
|
||||
|
||||
print("分类结果:")
|
||||
print(f" 分类: {result.category}")
|
||||
print(f" 置信度: {result.confidence:.2f}")
|
||||
print(f" 标题: {result.title}")
|
||||
print(f" 标签: {', '.join(result.tags)}")
|
||||
print(f" 理由: {result.reasoning}")
|
||||
print(f"\n生成的 Markdown 内容:")
|
||||
print(result.content)
|
||||
|
||||
except Exception as e:
|
||||
print(f"分类失败: {e}")
|
||||
print("提示:请确保已在配置文件中正确设置 AI 配置")
|
||||
|
||||
|
||||
def example_batch_classification():
|
||||
"""
|
||||
示例 6: 批量分类多个文本
|
||||
"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 6: 批量分类多个文本")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# 测试文本列表
|
||||
test_texts = [
|
||||
("今天要完成:写代码、测试、部署", "待办类型"),
|
||||
("Python 列表推导式的用法示例", "笔记类型"),
|
||||
("产品创意:做一个 AI 写作助手", "灵感类型"),
|
||||
("API 接口:POST /api/create", "参考资料"),
|
||||
("程序员的 10 个搞笑瞬间", "搞笑类型"),
|
||||
]
|
||||
|
||||
print("批量分类结果:\n")
|
||||
|
||||
for text, description in test_texts:
|
||||
print(f"文本: {description}")
|
||||
print(f"内容: {text[:40]}...")
|
||||
print(f"预期分类: {description.split('类型')[0]}")
|
||||
print(f"实际分类: 需要调用 AI 服务")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("\n")
|
||||
print("╔" + "═" * 58 + "╗")
|
||||
print("║" + " " * 15 + "AI 模块使用示例" + " " * 15 + "║")
|
||||
print("╚" + "═" * 58 + "╝")
|
||||
print()
|
||||
|
||||
# 运行示例(注释掉需要 API key 的示例)
|
||||
# example_classify_with_openai()
|
||||
# example_classify_with_claude()
|
||||
# example_classify_with_qwen()
|
||||
# example_classify_with_ollama()
|
||||
# example_classify_with_config()
|
||||
example_batch_classification()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("提示")
|
||||
print("=" * 60)
|
||||
print()
|
||||
print("1. 取消注释上面的示例函数来运行特定示例")
|
||||
print("2. 替换 'your-api-key' 为实际的 API 密钥")
|
||||
print("3. 确保已安装所需的依赖库:")
|
||||
print(" pip install openai anthropic")
|
||||
print("4. Ollama 需要单独安装和运行")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
80
examples/browse_demo.py
Normal file
80
examples/browse_demo.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
浏览视图演示脚本
|
||||
|
||||
展示分类浏览功能的使用方法
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目路径
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
from src.models.database import init_database, get_db
|
||||
from src.gui.widgets.browse_view import BrowseView
|
||||
|
||||
|
||||
class BrowseDemoWindow(QMainWindow):
|
||||
"""浏览视图演示窗口"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 初始化数据库
|
||||
db_path = "sqlite:////home/congsh/CodeSpace/ClaudeSpace/CutThenThink/data/cutnthink.db"
|
||||
init_database(db_path)
|
||||
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
"""设置UI"""
|
||||
self.setWindowTitle("CutThenThink - 分类浏览演示")
|
||||
self.setMinimumSize(1000, 700)
|
||||
self.resize(1200, 800)
|
||||
|
||||
# 创建中央组件
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
# 布局
|
||||
layout = QVBoxLayout(central_widget)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# 创建浏览视图
|
||||
self.browse_view = BrowseView()
|
||||
layout.addWidget(self.browse_view)
|
||||
|
||||
# 连接信号
|
||||
self.browse_view.record_modified.connect(self.on_record_modified)
|
||||
self.browse_view.record_deleted.connect(self.on_record_deleted)
|
||||
|
||||
def on_record_modified(self, record_id: int):
|
||||
"""记录被修改"""
|
||||
print(f"记录 {record_id} 已被修改")
|
||||
|
||||
def on_record_deleted(self, record_id: int):
|
||||
"""记录被删除"""
|
||||
print(f"记录 {record_id} 已被删除")
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 创建应用
|
||||
app = QApplication(sys.argv)
|
||||
app.setApplicationName("CutThenThink")
|
||||
app.setOrganizationName("CutThenThink")
|
||||
|
||||
# 创建并显示主窗口
|
||||
window = BrowseDemoWindow()
|
||||
window.show()
|
||||
|
||||
# 运行应用
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
278
examples/config_example.py
Normal file
278
examples/config_example.py
Normal file
@@ -0,0 +1,278 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
配置管理使用示例
|
||||
|
||||
演示如何使用配置管理模块进行各种操作
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目路径
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from src.config.settings import (
|
||||
get_config,
|
||||
get_settings,
|
||||
SettingsManager,
|
||||
AIProvider,
|
||||
OCRMode,
|
||||
CloudStorageType,
|
||||
Theme,
|
||||
ConfigError
|
||||
)
|
||||
|
||||
|
||||
def example_1_basic_usage():
|
||||
"""示例 1: 基本使用"""
|
||||
print("=" * 60)
|
||||
print("示例 1: 基本使用")
|
||||
print("=" * 60)
|
||||
|
||||
# 获取配置对象
|
||||
settings = get_settings()
|
||||
|
||||
print(f"AI 提供商: {settings.ai.provider}")
|
||||
print(f"AI 模型: {settings.ai.model}")
|
||||
print(f"OCR 模式: {settings.ocr.mode}")
|
||||
print(f"界面主题: {settings.ui.theme}")
|
||||
print(f"快捷键 - 截图: {settings.ui.hotkeys.screenshot}")
|
||||
print()
|
||||
|
||||
|
||||
def example_2_modify_config():
|
||||
"""示例 2: 修改配置"""
|
||||
print("=" * 60)
|
||||
print("示例 2: 修改配置")
|
||||
print("=" * 60)
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
# 修改 AI 配置
|
||||
settings.ai.provider = AIProvider.OPENAI
|
||||
settings.ai.model = "gpt-4"
|
||||
settings.ai.temperature = 0.8
|
||||
|
||||
# 修改界面配置
|
||||
settings.ui.theme = Theme.DARK
|
||||
settings.ui.window_width = 1400
|
||||
|
||||
print("配置已修改:")
|
||||
print(f" AI 提供商: {settings.ai.provider}")
|
||||
print(f" AI 模型: {settings.ai.model}")
|
||||
print(f" 温度: {settings.ai.temperature}")
|
||||
print(f" 主题: {settings.ui.theme}")
|
||||
|
||||
# 注意: 实际使用时需要调用 manager.save() 来保存
|
||||
# manager.save()
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def example_3_manager_operations():
|
||||
"""示例 3: 配置管理器操作"""
|
||||
print("=" * 60)
|
||||
print("示例 3: 配置管理器操作")
|
||||
print("=" * 60)
|
||||
|
||||
manager = get_config()
|
||||
|
||||
# 使用 get 获取嵌套值
|
||||
provider = manager.get('ai.provider')
|
||||
theme = manager.get('ui.theme')
|
||||
screenshot_hotkey = manager.get('ui.hotkeys.screenshot')
|
||||
|
||||
print("使用 manager.get() 获取配置:")
|
||||
print(f" AI 提供商: {provider}")
|
||||
print(f" 主题: {theme}")
|
||||
print(f" 截图快捷键: {screenshot_hotkey}")
|
||||
|
||||
# 使用 set 设置嵌套值
|
||||
manager.set('ai.temperature', 1.2)
|
||||
manager.set('ui.language', 'en_US')
|
||||
|
||||
print("\n使用 manager.set() 修改配置:")
|
||||
print(f" 温度: {manager.settings.ai.temperature}")
|
||||
print(f" 语言: {manager.settings.ui.language}")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def example_4_validation():
|
||||
"""示例 4: 配置验证"""
|
||||
print("=" * 60)
|
||||
print("示例 4: 配置验证")
|
||||
print("=" * 60)
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
# 验证当前配置
|
||||
try:
|
||||
settings.validate()
|
||||
print("✓ 当前配置有效")
|
||||
except ConfigError as e:
|
||||
print(f"✗ 配置错误: {e}")
|
||||
|
||||
# 尝试创建无效配置
|
||||
print("\n尝试创建无效配置:")
|
||||
settings.ai.provider = AIProvider.OPENAI
|
||||
settings.ai.api_key = "" # 缺少 API key
|
||||
|
||||
try:
|
||||
settings.validate()
|
||||
print("✗ 应该抛出异常")
|
||||
except ConfigError as e:
|
||||
print(f"✓ 正确捕获错误: {e}")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def example_5_custom_config_file():
|
||||
"""示例 5: 使用自定义配置文件"""
|
||||
print("=" * 60)
|
||||
print("示例 5: 使用自定义配置文件")
|
||||
print("=" * 60)
|
||||
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
# 创建临时目录
|
||||
temp_dir = Path(tempfile.mkdtemp())
|
||||
custom_config = temp_dir / 'custom_config.yaml'
|
||||
|
||||
try:
|
||||
# 创建自定义配置管理器
|
||||
manager = SettingsManager(custom_config)
|
||||
|
||||
# 加载配置(会创建默认配置)
|
||||
settings = manager.load()
|
||||
print(f"✓ 配置文件已创建: {custom_config}")
|
||||
|
||||
# 修改配置
|
||||
settings.ai.provider = AIProvider.ANTHROPIC
|
||||
settings.ai.api_key = "sk-ant-test123"
|
||||
settings.ui.theme = Theme.LIGHT
|
||||
|
||||
# 保存配置
|
||||
manager.save()
|
||||
print("✓ 配置已保存")
|
||||
|
||||
# 重新加载验证
|
||||
manager2 = SettingsManager(custom_config)
|
||||
loaded = manager2.load()
|
||||
print(f"✓ 重新加载成功: provider={loaded.ai.provider}, theme={loaded.ui.theme}")
|
||||
|
||||
finally:
|
||||
# 清理
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def example_6_cloud_storage_config():
|
||||
"""示例 6: 云存储配置"""
|
||||
print("=" * 60)
|
||||
print("示例 6: 云存储配置")
|
||||
print("=" * 60)
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
# 配置 S3 存储
|
||||
settings.cloud_storage.type = CloudStorageType.S3
|
||||
settings.cloud_storage.endpoint = "https://s3.amazonaws.com"
|
||||
settings.cloud_storage.access_key = "your-access-key"
|
||||
settings.cloud_storage.secret_key = "your-secret-key"
|
||||
settings.cloud_storage.bucket = "my-bucket"
|
||||
settings.cloud_storage.region = "us-east-1"
|
||||
|
||||
print("S3 存储配置:")
|
||||
print(f" 类型: {settings.cloud_storage.type}")
|
||||
print(f" 端点: {settings.cloud_storage.endpoint}")
|
||||
print(f" 存储桶: {settings.cloud_storage.bucket}")
|
||||
print(f" 区域: {settings.cloud_storage.region}")
|
||||
|
||||
# 验证配置
|
||||
try:
|
||||
settings.cloud_storage.validate()
|
||||
print("✓ S3 配置有效")
|
||||
except ConfigError as e:
|
||||
print(f"✗ S3 配置错误: {e}")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def example_7_ocr_config():
|
||||
"""示例 7: OCR 配置"""
|
||||
print("=" * 60)
|
||||
print("示例 7: OCR 配置")
|
||||
print("=" * 60)
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
# 本地 OCR 配置
|
||||
settings.ocr.mode = OCRMode.LOCAL
|
||||
settings.ocr.use_gpu = False
|
||||
settings.ocr.lang = "ch" # 中文
|
||||
|
||||
print("本地 OCR 配置:")
|
||||
print(f" 模式: {settings.ocr.mode}")
|
||||
print(f" 使用 GPU: {settings.ocr.use_gpu}")
|
||||
print(f" 语言: {settings.ocr.lang}")
|
||||
|
||||
# 云端 OCR 配置
|
||||
settings.ocr.mode = OCRMode.CLOUD
|
||||
settings.ocr.api_endpoint = "https://api.example.com/ocr"
|
||||
settings.ocr.api_key = "cloud-api-key"
|
||||
|
||||
print("\n云端 OCR 配置:")
|
||||
print(f" 模式: {settings.ocr.mode}")
|
||||
print(f" 端点: {settings.ocr.api_endpoint}")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def example_8_advanced_config():
|
||||
"""示例 8: 高级配置"""
|
||||
print("=" * 60)
|
||||
print("示例 8: 高级配置")
|
||||
print("=" * 60)
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
# 启用调试模式
|
||||
settings.advanced.debug_mode = True
|
||||
settings.advanced.log_level = "DEBUG"
|
||||
settings.advanced.log_file = "/var/log/cutthenthink/app.log"
|
||||
|
||||
print("高级配置:")
|
||||
print(f" 调试模式: {settings.advanced.debug_mode}")
|
||||
print(f" 日志级别: {settings.advanced.log_level}")
|
||||
print(f" 日志文件: {settings.advanced.log_file}")
|
||||
print(f" 最大日志大小: {settings.advanced.max_log_size} MB")
|
||||
print(f" 备份文件数: {settings.advanced.backup_count}")
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
"""运行所有示例"""
|
||||
print("\n" + "=" * 60)
|
||||
print("配置管理模块使用示例")
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
example_1_basic_usage()
|
||||
example_2_modify_config()
|
||||
example_3_manager_operations()
|
||||
example_4_validation()
|
||||
example_5_custom_config_file()
|
||||
example_6_cloud_storage_config()
|
||||
example_7_ocr_config()
|
||||
example_8_advanced_config()
|
||||
|
||||
print("=" * 60)
|
||||
print("所有示例运行完成!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
267
examples/gui_integration_example.py
Normal file
267
examples/gui_integration_example.py
Normal file
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GUI 集成示例
|
||||
|
||||
演示如何在 GUI 中集成处理流程和结果展示
|
||||
"""
|
||||
|
||||
import sys
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# 添加项目根目录到路径
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from src.core.processor import ImageProcessor, ProcessCallback, ProcessResult, create_markdown_result
|
||||
from src.config.settings import get_settings
|
||||
from src.utils.logger import init_logger, get_logger
|
||||
from src.gui.widgets import ResultWidget, MessageHandler, ProgressDialog
|
||||
|
||||
|
||||
# 初始化日志
|
||||
log_dir = project_root / "logs"
|
||||
init_logger(log_dir=log_dir, level="INFO", colored_console=True)
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class GUIProcessCallback(ProcessCallback):
|
||||
"""
|
||||
GUI 回调类
|
||||
"""
|
||||
|
||||
def __init__(self, progress_dialog: ProgressDialog, status_label: ttk.Label):
|
||||
super().__init__()
|
||||
self.progress_dialog = progress_dialog
|
||||
self.status_label = status_label
|
||||
|
||||
def on_start(self, message: str = "开始处理"):
|
||||
self.status_label.config(text=message)
|
||||
|
||||
def on_ocr_start(self, message: str = "开始 OCR 识别"):
|
||||
self.status_label.config(text=message)
|
||||
if self.progress_dialog and not self.progress_dialog.is_cancelled():
|
||||
self.progress_dialog.set_message(message)
|
||||
|
||||
def on_ocr_complete(self, result):
|
||||
self.status_label.config(text=f"OCR 完成: {len(result.results)} 行")
|
||||
|
||||
def on_ai_start(self, message: str = "开始 AI 分类"):
|
||||
self.status_label.config(text=message)
|
||||
if self.progress_dialog and not self.progress_dialog.is_cancelled():
|
||||
self.progress_dialog.set_message(message)
|
||||
|
||||
def on_ai_complete(self, result):
|
||||
self.status_label.config(text=f"AI 完成: {result.category.value}")
|
||||
|
||||
def on_save_start(self, message: str = "开始保存"):
|
||||
self.status_label.config(text=message)
|
||||
|
||||
def on_save_complete(self, record_id: int):
|
||||
self.status_label.config(text=f"保存成功: ID={record_id}")
|
||||
|
||||
def on_error(self, message: str, exception=None):
|
||||
self.status_label.config(text=f"错误: {message}")
|
||||
|
||||
def on_complete(self, result: ProcessResult):
|
||||
if result.success:
|
||||
self.status_label.config(text=f"完成! 耗时 {result.process_time:.2f}s")
|
||||
else:
|
||||
self.status_label.config(text="处理失败")
|
||||
|
||||
|
||||
class ProcessorDemoGUI:
|
||||
"""
|
||||
处理器演示 GUI
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("CutThenThink - 处理流程整合演示")
|
||||
self.root.geometry("900x700")
|
||||
|
||||
# 加载配置
|
||||
self.settings = get_settings()
|
||||
|
||||
# 消息处理器
|
||||
self.message_handler = MessageHandler(self.root)
|
||||
|
||||
# 处理器
|
||||
self.processor: Optional[ImageProcessor] = None
|
||||
|
||||
# 创建 UI
|
||||
self._create_ui()
|
||||
|
||||
def _create_ui(self):
|
||||
"""创建 UI"""
|
||||
# 顶部工具栏
|
||||
toolbar = ttk.Frame(self.root, padding=10)
|
||||
toolbar.pack(side=tk.TOP, fill=tk.X)
|
||||
|
||||
# 选择图片按钮
|
||||
self.select_button = ttk.Button(
|
||||
toolbar,
|
||||
text="📁 选择图片",
|
||||
command=self._on_select_image
|
||||
)
|
||||
self.select_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 处理按钮
|
||||
self.process_button = ttk.Button(
|
||||
toolbar,
|
||||
text="🚀 开始处理",
|
||||
command=self._on_process,
|
||||
state=tk.DISABLED
|
||||
)
|
||||
self.process_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 分隔符
|
||||
ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=10)
|
||||
|
||||
# 当前图片标签
|
||||
ttk.Label(toolbar, text="当前图片:").pack(side=tk.LEFT, padx=5)
|
||||
self.image_label = ttk.Label(toolbar, text="未选择", foreground="gray")
|
||||
self.image_label.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 状态栏
|
||||
self.status_label = ttk.Label(
|
||||
self.root,
|
||||
text="就绪",
|
||||
relief=tk.SUNKEN,
|
||||
anchor=tk.W,
|
||||
padding=(5, 2)
|
||||
)
|
||||
self.status_label.pack(side=tk.BOTTOM, fill=tk.X)
|
||||
|
||||
# 主内容区域(使用 PanedWindow 分割)
|
||||
paned = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
|
||||
paned.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
|
||||
# 左侧:结果展示
|
||||
left_frame = ttk.LabelFrame(paned, text="处理结果", padding=5)
|
||||
paned.add(left_frame, weight=2)
|
||||
|
||||
self.result_widget = ResultWidget(left_frame)
|
||||
self.result_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
|
||||
|
||||
# 右侧:日志
|
||||
right_frame = ttk.LabelFrame(paned, text="处理日志", padding=5)
|
||||
paned.add(right_frame, weight=1)
|
||||
|
||||
self.log_text = tk.Text(
|
||||
right_frame,
|
||||
wrap=tk.WORD,
|
||||
font=("Consolas", 9),
|
||||
state=tk.DISABLED
|
||||
)
|
||||
self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
|
||||
# 配置日志标签
|
||||
self.log_text.tag_config("INFO", foreground="#3498db")
|
||||
self.log_text.tag_config("WARNING", foreground="#f39c12")
|
||||
self.log_text.tag_config("ERROR", foreground="#e74c3c")
|
||||
self.log_text.tag_config("SUCCESS", foreground="#27ae60")
|
||||
|
||||
def _log(self, level: str, message: str):
|
||||
"""添加日志"""
|
||||
self.log_text.config(state=tk.NORMAL)
|
||||
self.log_text.insert(tk.END, f"{message}\n", level)
|
||||
self.log_text.see(tk.END)
|
||||
self.log_text.config(state=tk.DISABLED)
|
||||
self.root.update()
|
||||
|
||||
def _on_select_image(self):
|
||||
"""选择图片"""
|
||||
filename = filedialog.askopenfilename(
|
||||
title="选择图片",
|
||||
filetypes=[
|
||||
("图片文件", "*.png *.jpg *.jpeg *.bmp *.gif"),
|
||||
("所有文件", "*.*")
|
||||
]
|
||||
)
|
||||
|
||||
if filename:
|
||||
self.current_image_path = filename
|
||||
self.image_label.config(text=Path(filename).name)
|
||||
self.process_button.config(state=tk.NORMAL)
|
||||
self._log("INFO", f"已选择图片: {filename}")
|
||||
|
||||
def _on_process(self):
|
||||
"""处理图片"""
|
||||
if not hasattr(self, 'current_image_path'):
|
||||
return
|
||||
|
||||
# 创建进度对话框
|
||||
progress_dialog = ProgressDialog(
|
||||
self.root,
|
||||
title="处理中",
|
||||
message="正在处理图片...",
|
||||
cancelable=True
|
||||
)
|
||||
|
||||
# 创建回调
|
||||
callback = GUIProcessCallback(progress_dialog, self.status_label)
|
||||
|
||||
# 创建处理器
|
||||
self.processor = ImageProcessor(
|
||||
ocr_config={
|
||||
'mode': self.settings.ocr.mode.value,
|
||||
'lang': self.settings.ocr.lang,
|
||||
'use_gpu': self.settings.ocr.use_gpu
|
||||
},
|
||||
ai_config=self.settings.ai,
|
||||
db_path=str(project_root / "data" / "cutnthink.db"),
|
||||
callback=callback
|
||||
)
|
||||
|
||||
# 处理图片(在后台执行)
|
||||
self.root.after(100, lambda: self._do_process(progress_dialog))
|
||||
|
||||
def _do_process(self, progress_dialog: ProgressDialog):
|
||||
"""执行处理"""
|
||||
try:
|
||||
self._log("INFO", "开始处理...")
|
||||
|
||||
# 处理
|
||||
result = self.processor.process_image(self.current_image_path)
|
||||
|
||||
# 关闭进度对话框
|
||||
if not progress_dialog.is_cancelled():
|
||||
progress_dialog.close()
|
||||
|
||||
# 显示结果
|
||||
if result.success:
|
||||
self.result_widget.set_result(result)
|
||||
self._log("SUCCESS", f"处理成功! 耗时 {result.process_time:.2f}s")
|
||||
self.message_handler.show_info(
|
||||
"处理完成",
|
||||
f"处理成功!\n耗时: {result.process_time:.2f}秒\n记录ID: {result.record_id}"
|
||||
)
|
||||
else:
|
||||
self._log("ERROR", f"处理失败: {result.error_message}")
|
||||
self.message_handler.show_error(
|
||||
"处理失败",
|
||||
result.error_message or "未知错误"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
progress_dialog.close()
|
||||
logger.error(f"处理失败: {e}", exc_info=True)
|
||||
self._log("ERROR", f"处理失败: {e}")
|
||||
self.message_handler.show_error("处理失败", str(e), exception=e)
|
||||
|
||||
def run(self):
|
||||
"""运行 GUI"""
|
||||
self.root.mainloop()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
app = ProcessorDemoGUI()
|
||||
app.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
286
examples/ocr_example.py
Normal file
286
examples/ocr_example.py
Normal file
@@ -0,0 +1,286 @@
|
||||
"""
|
||||
OCR 模块使用示例
|
||||
|
||||
演示如何使用 OCR 模块进行文字识别
|
||||
"""
|
||||
|
||||
# 导入 OCR 模块
|
||||
from src.core.ocr import (
|
||||
recognize_text,
|
||||
preprocess_image,
|
||||
PaddleOCREngine,
|
||||
CloudOCREngine,
|
||||
OCRFactory,
|
||||
ImagePreprocessor,
|
||||
OCRResult,
|
||||
OCRLanguage
|
||||
)
|
||||
|
||||
|
||||
def example_1_quick_recognize():
|
||||
"""示例 1: 快速识别文本(最简单)"""
|
||||
print("示例 1: 快速识别文本")
|
||||
print("-" * 50)
|
||||
|
||||
result = recognize_text(
|
||||
image="path/to/your/image.png",
|
||||
mode="local", # 本地识别
|
||||
lang="ch", # 中文
|
||||
use_gpu=False, # 不使用 GPU
|
||||
preprocess=False # 不预处理
|
||||
)
|
||||
|
||||
if result.success:
|
||||
print(f"识别成功!")
|
||||
print(f"平均置信度: {result.total_confidence:.2f}")
|
||||
print(f"识别行数: {len(result.results)}")
|
||||
print(f"完整文本:\n{result.full_text}")
|
||||
else:
|
||||
print(f"识别失败: {result.error_message}")
|
||||
|
||||
|
||||
def example_2_with_preprocess():
|
||||
"""示例 2: 带预处理的识别(适合低质量图片)"""
|
||||
print("\n示例 2: 带预处理的识别")
|
||||
print("-" * 50)
|
||||
|
||||
result = recognize_text(
|
||||
image="path/to/your/image.png",
|
||||
mode="local",
|
||||
lang="ch",
|
||||
preprocess=True # 启用预处理(增强对比度、锐度等)
|
||||
)
|
||||
|
||||
if result.success:
|
||||
print(f"识别成功!")
|
||||
print(f"完整文本:\n{result.full_text}")
|
||||
|
||||
|
||||
def example_3_engine_directly():
|
||||
"""示例 3: 直接使用 OCR 引擎"""
|
||||
print("\n示例 3: 直接使用 OCR 引擎")
|
||||
print("-" * 50)
|
||||
|
||||
# 创建引擎
|
||||
config = {
|
||||
'lang': 'ch', # 语言
|
||||
'use_gpu': False, # 是否使用 GPU
|
||||
'show_log': False # 是否显示日志
|
||||
}
|
||||
|
||||
engine = PaddleOCREngine(config)
|
||||
|
||||
# 识别图片
|
||||
result = engine.recognize(
|
||||
image="path/to/your/image.png",
|
||||
preprocess=False
|
||||
)
|
||||
|
||||
if result.success:
|
||||
print(f"识别成功!")
|
||||
print(f"完整文本:\n{result.full_text}")
|
||||
|
||||
# 遍历每一行
|
||||
for line_result in result.results:
|
||||
print(f"行 {line_result.line_index}: {line_result.text} (置信度: {line_result.confidence:.2f})")
|
||||
|
||||
|
||||
def example_4_batch_images():
|
||||
"""示例 4: 批量处理多张图片"""
|
||||
print("\n示例 4: 批量处理多张图片")
|
||||
print("-" * 50)
|
||||
|
||||
image_list = [
|
||||
"path/to/image1.png",
|
||||
"path/to/image2.png",
|
||||
"path/to/image3.png"
|
||||
]
|
||||
|
||||
engine = PaddleOCREngine({'lang': 'ch'})
|
||||
|
||||
for i, image_path in enumerate(image_list, 1):
|
||||
print(f"\n处理图片 {i}: {image_path}")
|
||||
result = engine.recognize(image_path)
|
||||
|
||||
if result.success:
|
||||
print(f" 识别成功,置信度: {result.total_confidence:.2f}")
|
||||
print(f" 文本预览: {result.full_text[:100]}...")
|
||||
else:
|
||||
print(f" 识别失败: {result.error_message}")
|
||||
|
||||
|
||||
def example_5_image_preprocess():
|
||||
"""示例 5: 图像预处理(增强识别效果)"""
|
||||
print("\n示例 5: 图像预处理")
|
||||
print("-" * 50)
|
||||
|
||||
# 预处理并保存
|
||||
processed = preprocess_image(
|
||||
image_path="path/to/input.png",
|
||||
output_path="path/to/output_processed.png",
|
||||
resize=True, # 调整大小
|
||||
enhance_contrast=True, # 增强对比度
|
||||
enhance_sharpness=True, # 增强锐度
|
||||
denoise=False, # 不去噪
|
||||
binarize=False # 不二值化
|
||||
)
|
||||
|
||||
print(f"预处理完成,图像尺寸: {processed.size}")
|
||||
|
||||
# 然后对预处理后的图片进行 OCR
|
||||
result = recognize_text(
|
||||
image=processed, # 可以传入 PIL Image
|
||||
mode="local",
|
||||
lang="ch"
|
||||
)
|
||||
|
||||
if result.success:
|
||||
print(f"识别文本: {result.full_text}")
|
||||
|
||||
|
||||
def example_6_multilanguage():
|
||||
"""示例 6: 多语言识别"""
|
||||
print("\n示例 6: 多语言识别")
|
||||
print("-" * 50)
|
||||
|
||||
# 中文
|
||||
result_ch = recognize_text(
|
||||
image="path/to/chinese_image.png",
|
||||
lang="ch" # 中文
|
||||
)
|
||||
print(f"中文识别置信度: {result_ch.total_confidence:.2f}")
|
||||
|
||||
# 英文
|
||||
result_en = recognize_text(
|
||||
image="path/to/english_image.png",
|
||||
lang="en" # 英文
|
||||
)
|
||||
print(f"英文识别置信度: {result_en.total_confidence:.2f}")
|
||||
|
||||
# 中英混合
|
||||
result_mix = recognize_text(
|
||||
image="path/to/mixed_image.png",
|
||||
lang="chinese_chinese" # 中英混合
|
||||
)
|
||||
print(f"混合识别置信度: {result_mix.total_confidence:.2f}")
|
||||
|
||||
|
||||
def example_7_cloud_ocr():
|
||||
"""示例 7: 云端 OCR(需要配置)"""
|
||||
print("\n示例 7: 云端 OCR")
|
||||
print("-" * 50)
|
||||
|
||||
# 配置云端 OCR
|
||||
config = {
|
||||
'api_endpoint': 'https://api.example.com/ocr',
|
||||
'api_key': 'your_api_key_here',
|
||||
'provider': 'custom',
|
||||
'timeout': 30
|
||||
}
|
||||
|
||||
engine = CloudOCREngine(config)
|
||||
|
||||
# 注意:云端 OCR 需要根据具体 API 实现 _send_request 方法
|
||||
result = engine.recognize("path/to/image.png")
|
||||
|
||||
if result.success:
|
||||
print(f"识别成功: {result.full_text}")
|
||||
else:
|
||||
print(f"云端 OCR 尚未实现: {result.error_message}")
|
||||
|
||||
|
||||
def example_8_factory_pattern():
|
||||
"""示例 8: 使用工厂模式创建引擎"""
|
||||
print("\n示例 8: 使用工厂模式创建引擎")
|
||||
print("-" * 50)
|
||||
|
||||
# 创建本地引擎
|
||||
local_engine = OCRFactory.create_engine(
|
||||
mode="local",
|
||||
config={'lang': 'ch'}
|
||||
)
|
||||
print(f"本地引擎类型: {type(local_engine).__name__}")
|
||||
|
||||
# 创建云端引擎
|
||||
cloud_engine = OCRFactory.create_engine(
|
||||
mode="cloud",
|
||||
config={'api_endpoint': 'https://api.example.com/ocr'}
|
||||
)
|
||||
print(f"云端引擎类型: {type(cloud_engine).__name__}")
|
||||
|
||||
|
||||
def example_9_detailed_result():
|
||||
"""示例 9: 处理详细识别结果"""
|
||||
print("\n示例 9: 处理详细识别结果")
|
||||
print("-" * 50)
|
||||
|
||||
result = recognize_text(
|
||||
image="path/to/image.png",
|
||||
mode="local",
|
||||
lang="ch"
|
||||
)
|
||||
|
||||
if result.success:
|
||||
# 遍历每一行结果
|
||||
for line_result in result.results:
|
||||
print(f"\n行 {line_result.line_index}:")
|
||||
print(f" 文本: {line_result.text}")
|
||||
print(f" 置信度: {line_result.confidence:.2f}")
|
||||
|
||||
# 如果有坐标信息
|
||||
if line_result.bbox:
|
||||
print(f" 坐标: {line_result.bbox}")
|
||||
|
||||
# 统计信息
|
||||
total_chars = sum(len(r.text) for r in result.results)
|
||||
avg_confidence = sum(r.confidence for r in result.results) / len(result.results)
|
||||
|
||||
print(f"\n统计:")
|
||||
print(f" 总行数: {len(result.results)}")
|
||||
print(f" 总字符数: {total_chars}")
|
||||
print(f" 平均置信度: {avg_confidence:.2f}")
|
||||
|
||||
|
||||
def example_10_pil_image_input():
|
||||
"""示例 10: 使用 PIL Image 作为输入"""
|
||||
print("\n示例 10: 使用 PIL Image 作为输入")
|
||||
print("-" * 50)
|
||||
|
||||
from PIL import Image
|
||||
|
||||
# 加载图像
|
||||
pil_image = Image.open("path/to/image.png")
|
||||
|
||||
# 裁剪感兴趣区域
|
||||
cropped = pil_image.crop((100, 100, 500, 300))
|
||||
|
||||
# 直接识别 PIL Image
|
||||
result = recognize_text(
|
||||
image=cropped, # 直接传入 PIL Image 对象
|
||||
mode="local",
|
||||
lang="ch"
|
||||
)
|
||||
|
||||
if result.success:
|
||||
print(f"识别结果: {result.full_text}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("OCR 模块使用示例")
|
||||
print("=" * 50)
|
||||
print("\n注意:运行这些示例前,请确保:")
|
||||
print("1. 安装依赖: pip install paddleocr paddlepaddle")
|
||||
print("2. 将示例中的 'path/to/image.png' 替换为实际图片路径")
|
||||
print("=" * 50)
|
||||
|
||||
# 取消注释想要运行的示例
|
||||
# example_1_quick_recognize()
|
||||
# example_2_with_preprocess()
|
||||
# example_3_engine_directly()
|
||||
# example_4_batch_images()
|
||||
# example_5_image_preprocess()
|
||||
# example_6_multilanguage()
|
||||
# example_7_cloud_ocr()
|
||||
# example_8_factory_pattern()
|
||||
# example_9_detailed_result()
|
||||
# example_10_pil_image_input()
|
||||
170
examples/processor_example.py
Normal file
170
examples/processor_example.py
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
处理流程整合示例
|
||||
|
||||
演示如何使用 ImageProcessor 进行完整的图片处理流程
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到路径
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from src.core.processor import ImageProcessor, ProcessCallback, ProcessResult, create_markdown_result
|
||||
from src.config.settings import get_settings
|
||||
from src.utils.logger import init_logger, get_logger
|
||||
|
||||
|
||||
# 初始化日志
|
||||
log_dir = project_root / "logs"
|
||||
init_logger(log_dir=log_dir, level="INFO", colored_console=True)
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class DemoProcessCallback(ProcessCallback):
|
||||
"""
|
||||
演示用回调类
|
||||
"""
|
||||
|
||||
def on_start(self, message: str = "开始处理"):
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f"🚀 {message}")
|
||||
print(f"{'=' * 60}\n")
|
||||
|
||||
def on_ocr_start(self, message: str = "开始 OCR 识别"):
|
||||
print(f"📸 {message}...")
|
||||
|
||||
def on_ocr_complete(self, result):
|
||||
print(f" ✅ 识别完成: {len(result.results)} 行文本")
|
||||
print(f" 📊 置信度: {result.total_confidence:.2%}")
|
||||
print(f" 📝 预览: {result.full_text[:50]}...")
|
||||
|
||||
def on_ai_start(self, message: str = "开始 AI 分类"):
|
||||
print(f"\n🤖 {message}...")
|
||||
|
||||
def on_ai_complete(self, result):
|
||||
emoji_map = {
|
||||
"TODO": "✅",
|
||||
"NOTE": "📝",
|
||||
"IDEA": "💡",
|
||||
"REF": "📚",
|
||||
"FUNNY": "😄",
|
||||
"TEXT": "📄"
|
||||
}
|
||||
emoji = emoji_map.get(result.category.value, "📄")
|
||||
print(f" {emoji} 分类: {result.category.value}")
|
||||
print(f" 📊 置信度: {result.confidence:.2%}")
|
||||
print(f" 📌 标题: {result.title}")
|
||||
print(f" 🏷️ 标签: {', '.join(result.tags)}")
|
||||
|
||||
def on_save_start(self, message: str = "开始保存到数据库"):
|
||||
print(f"\n💾 {message}...")
|
||||
|
||||
def on_save_complete(self, record_id: int):
|
||||
print(f" ✅ 保存成功: 记录 ID = {record_id}")
|
||||
|
||||
def on_error(self, message: str, exception=None):
|
||||
print(f"\n❌ 错误: {message}")
|
||||
if exception:
|
||||
print(f" 异常类型: {type(exception).__name__}")
|
||||
|
||||
def on_complete(self, result: ProcessResult):
|
||||
print(f"\n{'=' * 60}")
|
||||
if result.success:
|
||||
print(f"✨ 处理成功!")
|
||||
print(f" 耗时: {result.process_time:.2f} 秒")
|
||||
print(f" 步骤: {' -> '.join(result.steps_completed)}")
|
||||
else:
|
||||
print(f"❌ 处理失败")
|
||||
if result.error_message:
|
||||
print(f" 错误: {result.error_message}")
|
||||
if result.warnings:
|
||||
print(f"\n⚠️ 警告:")
|
||||
for warning in result.warnings:
|
||||
print(f" - {warning}")
|
||||
print(f"{'=' * 60}\n")
|
||||
|
||||
|
||||
def process_single_image_demo(image_path: str):
|
||||
"""
|
||||
处理单张图片的演示
|
||||
|
||||
Args:
|
||||
image_path: 图片路径
|
||||
"""
|
||||
print(f"\n处理图片: {image_path}")
|
||||
|
||||
# 加载配置
|
||||
settings = get_settings()
|
||||
|
||||
# 创建处理器
|
||||
callback = DemoProcessCallback()
|
||||
processor = ImageProcessor(
|
||||
ocr_config={
|
||||
'mode': settings.ocr.mode.value,
|
||||
'lang': settings.ocr.lang,
|
||||
'use_gpu': settings.ocr.use_gpu
|
||||
},
|
||||
ai_config=settings.ai,
|
||||
db_path=str(project_root / "data" / "cutnthink.db"),
|
||||
callback=callback
|
||||
)
|
||||
|
||||
# 处理图片
|
||||
result = processor.process_image(image_path)
|
||||
|
||||
# 显示 Markdown 结果
|
||||
if result.ai_result:
|
||||
markdown = create_markdown_result(result.ai_result, result.ocr_result.full_text if result.ocr_result else "")
|
||||
print("\n" + "=" * 60)
|
||||
print("Markdown 格式结果:")
|
||||
print("=" * 60)
|
||||
print(markdown)
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("""
|
||||
╔══════════════════════════════════════════════════════════╗
|
||||
║ CutThenThink - 处理流程整合示例 ║
|
||||
║ OCR -> AI -> 存储 完整流程演示 ║
|
||||
╚══════════════════════════════════════════════════════════╝
|
||||
""")
|
||||
|
||||
# 检查命令行参数
|
||||
if len(sys.argv) < 2:
|
||||
print("用法: python processor_example.py <图片路径>")
|
||||
print("\n示例:")
|
||||
print(" python processor_example.py /path/to/image.png")
|
||||
print(" python processor_example.py /path/to/image.jpg")
|
||||
return
|
||||
|
||||
image_path = sys.argv[1]
|
||||
|
||||
# 检查文件是否存在
|
||||
if not Path(image_path).exists():
|
||||
print(f"❌ 错误: 文件不存在: {image_path}")
|
||||
return
|
||||
|
||||
# 处理图片
|
||||
try:
|
||||
result = process_single_image_demo(image_path)
|
||||
|
||||
if result.success:
|
||||
print("\n🎉 处理完成!")
|
||||
else:
|
||||
print("\n⚠️ 处理未完全成功,请检查日志")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理失败: {e}", exc_info=True)
|
||||
print(f"\n❌ 处理失败: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
170
examples/storage_example.py
Normal file
170
examples/storage_example.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
存储模块使用示例
|
||||
演示常见的使用场景
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到路径
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from src.core.storage import Storage
|
||||
|
||||
|
||||
def example_basic_usage():
|
||||
"""基本使用示例"""
|
||||
print("=" * 60)
|
||||
print("示例 1: 基本使用")
|
||||
print("=" * 60)
|
||||
|
||||
storage = Storage()
|
||||
|
||||
# 创建几条笔记
|
||||
storage.create(
|
||||
title="项目会议记录",
|
||||
content="讨论了新功能的设计方案",
|
||||
category="工作",
|
||||
tags=["会议", "重要"]
|
||||
)
|
||||
|
||||
storage.create(
|
||||
title="Python 学习笔记",
|
||||
content="今天学习了列表推导式和装饰器",
|
||||
category="学习",
|
||||
tags=["Python", "编程"]
|
||||
)
|
||||
|
||||
storage.create(
|
||||
title="周末计划",
|
||||
content="周六去图书馆,周日看电影",
|
||||
category="生活"
|
||||
)
|
||||
|
||||
# 查看所有笔记
|
||||
all_notes = storage.get_all()
|
||||
print(f"\n总共有 {len(all_notes)} 条笔记:")
|
||||
for note in all_notes:
|
||||
print(f" • {note['title']} [{note['category']}]")
|
||||
|
||||
|
||||
def example_search():
|
||||
"""搜索示例"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 2: 搜索功能")
|
||||
print("=" * 60)
|
||||
|
||||
storage = Storage()
|
||||
|
||||
# 搜索包含"Python"的笔记
|
||||
results = storage.search("Python")
|
||||
print(f"\n搜索 'Python' 找到 {len(results)} 条结果:")
|
||||
for note in results:
|
||||
print(f" • {note['title']}")
|
||||
|
||||
# 搜索包含"会议"的笔记
|
||||
results = storage.search("会议")
|
||||
print(f"\n搜索 '会议' 找到 {len(results)} 条结果:")
|
||||
for note in results:
|
||||
print(f" • {note['title']}")
|
||||
|
||||
|
||||
def example_category_management():
|
||||
"""分类管理示例"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 3: 分类管理")
|
||||
print("=" * 60)
|
||||
|
||||
storage = Storage()
|
||||
|
||||
# 查看所有分类
|
||||
categories = storage.get_categories()
|
||||
print(f"\n所有分类 ({len(categories)} 个):")
|
||||
for category in categories:
|
||||
count = len(storage.get_by_category(category))
|
||||
print(f" • {category} ({count} 条笔记)")
|
||||
|
||||
# 按分类查看笔记
|
||||
print("\n工作分类下的笔记:")
|
||||
work_notes = storage.get_by_category("工作")
|
||||
for note in work_notes:
|
||||
print(f" • {note['title']}")
|
||||
|
||||
|
||||
def example_update_and_delete():
|
||||
"""更新和删除示例"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 4: 更新和删除")
|
||||
print("=" * 60)
|
||||
|
||||
storage = Storage()
|
||||
|
||||
# 获取第一条笔记
|
||||
all_notes = storage.get_all()
|
||||
if all_notes:
|
||||
first_note = all_notes[0]
|
||||
print(f"\n原始标题: {first_note['title']}")
|
||||
|
||||
# 更新标题
|
||||
updated = storage.update(
|
||||
first_note['id'],
|
||||
title=f"{first_note['title']}(已编辑)"
|
||||
)
|
||||
print(f"更新后标题: {updated['title']}")
|
||||
|
||||
# 删除最后一条笔记
|
||||
if len(all_notes) > 1:
|
||||
last_note = all_notes[-1]
|
||||
storage.delete(last_note['id'])
|
||||
print(f"\n已删除: {last_note['title']}")
|
||||
|
||||
|
||||
def example_statistics():
|
||||
"""统计信息示例"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 5: 统计信息")
|
||||
print("=" * 60)
|
||||
|
||||
storage = Storage()
|
||||
|
||||
stats = storage.get_stats()
|
||||
print(f"\n📊 笔记统计:")
|
||||
print(f" • 总记录数: {stats['total_records']}")
|
||||
print(f" • 总分类数: {stats['total_categories']}")
|
||||
print(f"\n各分类记录数:")
|
||||
for category, count in stats['categories'].items():
|
||||
print(f" • {category}: {count}")
|
||||
|
||||
|
||||
def example_backup():
|
||||
"""备份示例"""
|
||||
print("\n" + "=" * 60)
|
||||
print("示例 6: 数据备份")
|
||||
print("=" * 60)
|
||||
|
||||
storage = Storage()
|
||||
|
||||
# 导出所有数据
|
||||
data = storage.export_data()
|
||||
print(f"\n导出 {len(data)} 条记录")
|
||||
|
||||
# 可以保存到备份文件
|
||||
backup_file = Path("/tmp/notes_backup.json")
|
||||
import json
|
||||
with open(backup_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
print(f"备份已保存到: {backup_file}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 运行所有示例
|
||||
example_basic_usage()
|
||||
example_search()
|
||||
example_category_management()
|
||||
example_update_and_delete()
|
||||
example_statistics()
|
||||
example_backup()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("所有示例运行完成!")
|
||||
print("=" * 60)
|
||||
75
examples/test_image_features.py
Normal file
75
examples/test_image_features.py
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
图片处理功能测试脚本
|
||||
|
||||
测试 P0-7 实现的图片处理功能:
|
||||
1. 全局快捷键截图
|
||||
2. 剪贴板监听
|
||||
3. 图片文件选择
|
||||
4. 图片预览组件
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到路径
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
try:
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from PyQt6.QtCore import Qt
|
||||
from src.gui.main_window import MainWindow
|
||||
from src.gui.widgets import (
|
||||
ScreenshotWidget,
|
||||
ClipboardMonitor,
|
||||
ImagePicker,
|
||||
ImagePreviewWidget
|
||||
)
|
||||
print("✓ 所有组件导入成功")
|
||||
|
||||
# 创建应用程序
|
||||
app = QApplication(sys.argv)
|
||||
app.setApplicationName("CutThenThink")
|
||||
|
||||
# 创建并显示主窗口
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
|
||||
print("✓ 主窗口已启动")
|
||||
print("\n功能测试说明:")
|
||||
print("=" * 50)
|
||||
print("1. 全局快捷键截图:")
|
||||
print(" - 点击「新建截图」按钮")
|
||||
print(" - 或使用快捷键 Ctrl+Shift+A")
|
||||
print(" - 拖动鼠标选择截图区域")
|
||||
print("")
|
||||
print("2. 剪贴板监听:")
|
||||
print(" - 复制任意图片到剪贴板")
|
||||
print(" - 点击「粘贴剪贴板图片」按钮")
|
||||
print(" - 或使用快捷键 Ctrl+Shift+V")
|
||||
print("")
|
||||
print("3. 图片文件选择:")
|
||||
print(" - 点击「导入图片」按钮")
|
||||
print(" - 或使用快捷键 Ctrl+Shift+O")
|
||||
print(" - 选择本地图片文件")
|
||||
print("")
|
||||
print("4. 图片预览:")
|
||||
print(" - 加载图片后可进行缩放操作")
|
||||
print(" - 支持鼠标滚轮缩放")
|
||||
print(" - 支持拖动平移")
|
||||
print(" - 支持旋转和全屏")
|
||||
print("=" * 50)
|
||||
|
||||
sys.exit(app.exec())
|
||||
|
||||
except ImportError as e:
|
||||
print(f"✗ 导入失败: {e}")
|
||||
print("\n请确保已安装所有依赖:")
|
||||
print(" pip install -r requirements.txt")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"✗ 启动失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user