1.说明
考完试查看正确答案(错题解析)截图, 进行ocr提取文字用于再次考试快速检索题目
WEB版说明 : 运行后会打开启动web服务, 2秒后打开浏览器跳转页面(局域网内可分享地址打开), 在界面搜索关键字即可
需要先提取图片中的文字, 参考: python(59) : 多线程调用大模型ocr提取图片文本-优快云博客
功能特性
- 🔍 智能搜索:根据图片关联的文本内容进行搜索
- 🎯 高级搜索:支持使用
&(与)和|(或)操作符进行复杂查询 - 🖼️ 图片浏览:支持在搜索结果中前后翻页浏览图片
- 🌐 Web 界面:现代化的响应式 Web 界面,支持移动端访问
- 📊 统计信息:显示数据库中的图片总数
- 🚀 自动启动:启动后自动打开浏览器访问
项目结构
考试助手/
├── answer_search_web.py # Flask Web 应用主文件
├── templates/
│ └── index.html # 前端页面
├── resource/ # 资源目录
│ └── [目录名]/
│ ├── imgs/ # 图片目录
│ │ └── *.png
│ └── texts/ # 文本目录
│ └── *.txt
├── requirements.txt # Python 依赖
└── README.md # 项目说明文档
安装说明
1. 环境要求
- Python 3.7+
- pip
2. 安装依赖
pip install -r requirements.txt
或者手动安装:
pip install Flask==3.0.0 flask_cors==5.0.0 Pillow==10.1.0
使用方法
1. 准备资源文件
确保项目根目录下有 resource 文件夹,结构如下:
resource/
├── 目录1/
│ ├── imgs/ # 存放图片文件(.png格式)
│ └── texts/ # 存放对应的文本文件(.txt格式,文件名与图片对应)
├── 目录2/
│ ├── imgs/
│ └── texts/
└── ...
注意:
- 文本文件名和图片文件名需要对应(例如:
1.txt对应1.png) - 文本文件内容将用于搜索匹配
2. 运行应用
python answer_search_web.py
启动后,应用会:
- 自动加载
resource目录下的所有图片和文本数据 - 在 2 秒后自动打开浏览器
- 默认访问地址:
http://[本机IP]:5000/
3. 使用搜索功能
基本搜索
在搜索框中输入关键词,系统会自动搜索包含该关键词的图片。
高级搜索
-
与操作(&):
关键词1&关键词2- 表示同时包含两个关键词的图片
- 示例:
Python&Flask查找同时包含 "Python" 和 "Flask" 的图片
-
或操作(|):
关键词1|关键词2- 表示包含任意一个关键词的图片
- 示例:
Python|Java查找包含 "Python" 或 "Java" 的图片
浏览结果
- 使用 上一张 / 下一张 按钮浏览搜索结果
- 搜索结果会显示当前图片位置(如:共 10 张,当前第 3 张)
API 文档
1. 搜索接口
端点:POST /api/search
请求体:
{
"keyword": "搜索关键词"
}
响应:
{
"success": true,
"count": 5,
"results": ["图片路径1", "图片路径2", ...],
"message": "找到 5 张包含 \"关键词\" 的图片"
}
2. 根据路径获取图片
端点:GET /api/image_by_path?path=图片路径
响应:返回图片文件(PNG 格式)
3. 获取统计信息
端点:GET /api/stats
响应:
{
"total_images": 100
}
打包成可执行文件
使用 PyInstaller 将应用打包成独立的可执行文件:
# 安装 PyInstaller
pip install pyinstaller
# 打包(使用图标)
pyinstaller -F -i maomi.ico answer_search_web.py --clean --noconfirm
打包后的可执行文件位于 dist/ 目录下。
注意:打包时需要确保 templates 目录和 resource 目录与可执行文件在同一目录下。
技术栈
- 后端:Flask 3.0.0
- 跨域支持:Flask-CORS 5.0.0
- 图片处理:Pillow 10.1.0
- 前端:原生 HTML/CSS/JavaScript
注意事项
- 资源目录:确保
resource目录存在且包含有效的图片和文本文件 - 端口占用:默认使用 5000 端口,如果被占用需要修改代码中的端口号
- 文件编码:文本文件需要使用 UTF-8 编码
- 图片格式:目前支持 PNG 格式图片
- 网络访问:应用默认监听
0.0.0.0,局域网内其他设备也可以访问
常见问题
Q: 启动后没有自动打开浏览器?
A: 可以手动访问控制台显示的地址,通常是 http://[本机IP]:5000/
Q: 搜索不到结果?
A: 检查以下几点:
resource目录是否存在- 文本文件内容是否正确
- 搜索关键词是否与文本内容匹配
Q: 图片无法显示?
A: 检查图片路径是否正确,确保图片文件存在且可读
许可证
本项目仅供学习和个人使用。
2.python代码(answer_search_web.py)
import os
import tkinter as tk
# from tkinter import messagebox
from PIL import Image, ImageTk
# 全局变量存储图片文本字典
img_text_dict = {}
# pip install pyinstaller Pillow
# pyinstaller -F -i maomi.ico answer_search_gui.py --clean --noconfirm
def load_image_text_data():
"""加载图片和文本数据"""
global img_text_dict
# 目录配置
current_dir = os.getcwd()
print(current_dir)
#current_dir = r'F:\my\python\mypy\考试\瓴羊高级数据研发工程师认证2'
# 遍历资源目录
ks_dirs = []
resource_dir = os.path.join(current_dir, 'resource')
if not os.path.exists(resource_dir):
print(f"警告: 资源目录不存在 {resource_dir}")
return
for item in os.listdir(resource_dir):
item_path = os.path.join(resource_dir, item)
if os.path.isdir(item_path):
ks_dirs.append(item_path)
# 检索文件
for path in ks_dirs:
texts_path = os.path.join(path, 'texts')
imgs_path = os.path.join(path, 'imgs')
if not os.path.exists(texts_path):
continue
for fil in os.listdir(texts_path):
text_file = os.path.join(texts_path, fil)
try:
with open(text_file, 'r', encoding='utf-8') as f:
content = f.read()
img_name = fil.split('.')[0] + '.png'
img_path = os.path.join(imgs_path, img_name)
img_text_dict[img_path] = content.strip().replace(' ', '')
except Exception as e:
print(f"读取文件错误 {text_file}: {e}")
print(f"加载了 {len(img_text_dict)} 张图片数据")
def find_keys_by_value(dictionary, target_value):
"""
根据目标值在字典中查找所有匹配的键。
支持 &(与)和 |(或)操作符:
- 如果 target_value 包含 '&',则要求所有分隔的值都包含在字典值中(与)。
- 如果 target_value 包含 '|',则只要有一个分隔的值包含在字典值中即可(或)。
- 如果都不包含,则进行普通包含匹配。
Args:
dictionary (dict): 要搜索的字典,值应为字符串或可迭代的容器(如列表、集合等)
target_value (str): 要查找的目标值,支持 & 和 | 分隔
Returns:
list: 包含所有匹配键的列表
"""
# 辅助函数:检查 value 是否包含 sub
def contains(value, sub):
if isinstance(value, str):
return sub in value
elif isinstance(value, (list, tuple, set)):
return sub in value
else:
return str(sub) in str(value)
result = []
for key, value in dictionary.items():
if '&' in target_value:
# 使用 & 分割,所有部分都必须包含(与)
parts = target_value.split('&')
if all(contains(value, part.strip()) for part in parts):
result.append(key)
elif '|' in target_value:
# 使用 | 分割,任意一个部分包含即可(或)
parts = target_value.split('|')
if any(contains(value, part.strip()) for part in parts):
result.append(key)
else:
# 普通包含匹配
if contains(value, target_value):
result.append(key)
return result
class ImageSearchApp:
def __init__(self, root, img_text_dict):
# 设置主窗口
self.root = root
self.img_text_dict = img_text_dict
self.root.title("图片搜索器")
self.root.geometry("1200x700")
self.root.configure(bg="#f0f0f0")
# 存储搜索结果
self.search_results = []
self.current_index = 0
# 创建UI组件
self.create_widgets()
def create_widgets(self):
# 创建搜索框和导航按钮框架(合并为一个框架)
search_nav_frame = tk.Frame(self.root, bg="#f0f0f0", pady=20)
search_nav_frame.pack(fill=tk.X, padx=20)
# 搜索标签
tk.Label(search_nav_frame, text="搜索关键字:", bg="#f0f0f0", font=("Arial", 12)).pack(side=tk.LEFT, padx=5)
# 搜索输入框
self.search_entry = tk.Entry(search_nav_frame, font=("Arial", 12), width=40)
self.search_entry.pack(side=tk.LEFT, padx=5)
self.search_entry.bind('<Return>', lambda event: self.perform_search())
# 绑定输入事件,自动搜索
self.search_entry.bind('<KeyRelease>', lambda event: self.perform_search())
# 重置按钮
reset_btn = tk.Button(
search_nav_frame,
text="重置",
command=self.reset_search,
bg="#FF9800",
fg="white",
font=("Arial", 10, "bold"),
padx=10
)
reset_btn.pack(side=tk.LEFT, padx=5)
# 搜索按钮
search_btn = tk.Button(
search_nav_frame,
text="搜索",
command=self.perform_search,
bg="#4CAF50",
fg="white",
font=("Arial", 10, "bold"),
padx=10
)
search_btn.pack(side=tk.LEFT, padx=5)
# 搜索结果信息标签 - 新增部分
self.result_info_label = tk.Label(
search_nav_frame,
text="",
bg="#f0f0f0",
font=("Arial", 10),
fg="#666666"
)
self.result_info_label.pack(side=tk.LEFT, padx=15)
# 导航按钮
self.prev_btn = tk.Button(
search_nav_frame,
text="上一张",
command=self.prev_image,
state=tk.DISABLED,
bg="#2196F3",
fg="white",
font=("Arial", 10),
padx=15
)
self.prev_btn.pack(side=tk.LEFT, padx=5)
self.next_btn = tk.Button(
search_nav_frame,
text="下一张",
command=self.next_image,
state=tk.DISABLED,
bg="#2196F3",
fg="white",
font=("Arial", 10),
padx=15
)
self.next_btn.pack(side=tk.LEFT, padx=5)
# 搜索提示信息(放在搜索框和按钮的下一行)
hint_label = tk.Label(
self.root,
text="搜索提示: 支持使用 &(与)和 |(或)操作符。 例如:关键词1&关键词2 表示同时包含两个关键词, 关键词1|关键词2 表示包含任意一个关键词。",
bg="#f0f0f0",
font=("Arial", 9),
fg="#666666",
wraplength=800,
justify=tk.LEFT
)
hint_label.pack(anchor=tk.W, padx=20, pady=(0, 10))
# 图片展示框架
self.image_frame = tk.Frame(self.root, bg="white", bd=2, relief=tk.SUNKEN)
self.image_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
# 图片标签
self.image_label = tk.Label(self.image_frame, bg="white")
self.image_label.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# 图片信息标签
self.info_label = tk.Label(self.root, text="", bg="#f0f0f0", font=("Arial", 10))
self.info_label.pack(pady=5, anchor=tk.W, padx=20)
# 初始提示
self.image_label.config(text="请输入关键字并搜索图片")
# 搜索初始化
self.perform_search()
def reset_search(self):
"""重置搜索:清空搜索框并执行搜索"""
self.search_entry.delete(0, tk.END)
self.perform_search()
def perform_search(self):
"""执行搜索操作"""
keyword = self.search_entry.get().strip()
# if not keyword:
# messagebox.showwarning("提示", "请输入搜索关键字")
# return
# 在图片列表中搜索包含关键字的项
filelist = find_keys_by_value(self.img_text_dict, keyword)
self.search_results = [(index, filename) for index, filename in enumerate(filelist)]
# 更新UI
if self.search_results:
self.current_index = 0
self.update_image_display()
self.update_nav_buttons()
self.info_label.config(text=f"找到 {len(self.search_results)} 张包含 '{keyword}' 的图片")
else:
self.image_label.config(text=f"没有找到包含 '{keyword}' 的图片", image='') # 新增 image='' 清空当前图片
self.prev_btn.config(state=tk.DISABLED)
self.next_btn.config(state=tk.DISABLED)
self.result_info_label.config(text="")
self.info_label.config(text=f"未找到包含 '{keyword}' 的图片")
def update_image_display(self):
"""更新当前显示的图片"""
if not self.search_results:
return
index, image_name = self.search_results[self.current_index]
# 实际应用中,这里应该是您的图片路径
# 这里使用示例图片,实际使用时请替换为您的图片路径
# 为了演示,我们使用Pillow创建一个简单的图像
try:
# 尝试打开真实图片(如果存在)
print(os.path.basename(image_name))
if os.path.exists(image_name):
img = Image.open(image_name)
else:
# 如果图片不存在,创建一个带有文字的示例图片
img = Image.new('RGB', (400, 300), color='lightgray')
# 注意:实际使用时需要安装Pillow库,并取消下面的注释
# from PIL import ImageDraw, ImageFont
# draw = ImageDraw.Draw(img)
# font = ImageFont.truetype("arial.ttf", 24) # 可能需要调整字体路径
# draw.text((50, 130), image_name, font=font, fill=(0, 0, 0))
# 调整图片大小以适应窗口
img.thumbnail((900, 500))
photo = ImageTk.PhotoImage(img)
# 更新图片标签
self.image_label.config(image=photo)
self.image_label.image = photo # 保持引用
self.result_info_label.config(
text=f"共 {len(self.search_results)} 张,当前第 {self.current_index + 1} 张")
except Exception as e:
self.image_label.config(text=f"无法加载图片: {str(e)}")
def update_nav_buttons(self):
"""更新导航按钮状态"""
if len(self.search_results) <= 1:
self.prev_btn.config(state=tk.DISABLED)
self.next_btn.config(state=tk.DISABLED)
else:
self.prev_btn.config(state=tk.NORMAL if self.current_index > 0 else tk.DISABLED)
self.next_btn.config(state=tk.NORMAL if self.current_index < len(self.search_results) - 1 else tk.DISABLED)
self.result_info_label.config(text=f"共 {len(self.search_results)} 张,当前第 {self.current_index + 1} 张")
def prev_image(self):
"""显示上一张图片"""
if self.current_index > 0:
self.current_index -= 1
self.update_image_display()
self.update_nav_buttons()
def next_image(self):
"""显示下一张图片"""
if self.current_index < len(self.search_results) - 1:
self.current_index += 1
self.update_image_display()
self.update_nav_buttons()
if __name__ == "__main__":
print("正在加载图片数据...")
load_image_text_data()
root = tk.Tk()
app = ImageSearchApp(root, img_text_dict)
root.mainloop()

1176

被折叠的 条评论
为什么被折叠?



