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:
congsh
2026-02-11 18:21:31 +08:00
commit c4a77f8aa4
79 changed files with 19412 additions and 0 deletions

359
examples/ai_example.py Normal file
View 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
View 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
View 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()

View 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
View 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()

View 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
View 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)

View 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)