优化这段代码的操作方式,人性化: import sys import json import tim...

Creado el: 10 de junio de 2025

Respondido usando O3 por Chat01

Pregunta

优化这段代码的操作方式,人性化:
import sys
import json
import time
import pyautogui
import easyocr
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QTextEdit, QVBoxLayout, QWidget,
QLabel, QGridLayout, QMessageBox)
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from fuzzywuzzy import fuzz # 引入模糊匹配库

--- 配置管理 ---

CONFIG_FILE = 'config.json'

def load_config():
"""加载配置文件,如果不存在则返回默认值"""
try:
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
return {
"question_region": None,
"option_coords": [None, None, None, None]
}

def save_config(config):
"""保存配置到文件"""
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=4)

--- 后端工作线程 (与之前类似,但匹配逻辑增强) ---

class Worker(QObject):
log_message = pyqtSignal(str)
finished = pyqtSignal()
is_stopped = False

text
def __init__(self, config): super().__init__() self.config = config self.answer_db = None self.ocr_reader = None def initialize(self): """初始化耗时操作,如加载模型""" try: # 加载答案库 self.log_message.emit("正在加载答案库...") with open('C:\\answers\\answers.json', 'r', encoding='utf-8') as f: data = json.load(f) self.answer_db = {q.get('title', '').strip(): str(q.get('answer')) for q in data.get('datalist', [])} self.log_message.emit(f"答案库加载成功,共 {len(self.answer_db)} 道题。") # 初始化OCR self.log_message.emit("正在初始化OCR识别器 (首次运行较慢)...") self.ocr_reader = easyocr.Reader(['ch_sim', 'en']) self.log_message.emit("OCR初始化完成。") return True except Exception as e: self.log_message.emit(f"初始化失败: {e}") return False @pyqtSlot() def run(self): """主答题循环""" if not self.answer_db or not self.ocr_reader: self.log_message.emit("工作线程未正确初始化!") self.finished.emit() return for i in range(3): # 假设答3题 if self.is_stopped: self.log_message.emit("任务已手动停止。") break self.log_message.emit(f"\n--- 准备回答第 {i + 1} 题 ---") time.sleep(2) # 识别题目 question_text = self.recognize_text(self.config['question_region']) if not question_text: self.log_message.emit("无法识别题目,跳过。") continue # 模糊匹配答案 best_match, score = self.find_best_match(question_text) if score >= 90: # 相似度阈值设为90 self.log_message.emit(f"找到最相似题目 (相似度: {score}%): {best_match}") answer = self.answer_db[best_match] self.log_message.emit(f"正确答案是: 选项 {answer}") # 点击操作 self.perform_click(answer) else: self.log_message.emit(f"未找到足够相似的题目 (最高相似度: {score}%)。") self.log_message.emit("\n答题流程结束!") self.finished.emit() def stop(self): self.is_stopped = True self.log_message.emit("正在尝试停止...") def recognize_text(self, region): try: screenshot = pyautogui.screenshot(region=region) result = self.ocr_reader.readtext(screenshot, detail=0, paragraph=True) text = "".join(result).strip().replace(" ", "") self.log_message.emit(f"OCR识别结果: {text}") return text except Exception as e: self.log_message.emit(f"OCR识别出错: {e}") return "" def find_best_match(self, ocr_text): """使用模糊匹配查找最相似的题目""" best_score = 0 best_match_title = "" for title in self.answer_db.keys(): # 使用 token_sort_ratio 忽略词序,更鲁棒 score = fuzz.token_sort_ratio(ocr_text, title) if score > best_score: best_score = score best_match_title = title return best_match_title, best_score def perform_click(self, answer): try: answer_index = int(answer) - 1 if 0 <= answer_index < len(self.config['option_coords']): coords = self.config['option_coords'][answer_index] self.log_message.emit(f"准备点击坐标: {coords}") pyautogui.click(coords[0], coords[1]) else: self.log_message.emit(f"答案索引'{answer}'无效!") except Exception as e: self.log_message.emit(f"点击操作失败: {e}")

--- 主界面 ---

class AnsweringApp(QMainWindow):
def init(self):
super().init()
self.config = load_config()
self.worker = None
self.thread = None

text
self.init_ui() self.update_config_display() def init_ui(self): self.setWindowTitle("智能答题助手 (V2.0 - 带校准功能)") self.setGeometry(100, 100, 600, 500) main_widget = QWidget() self.setCentralWidget(main_widget) main_layout = QVBoxLayout(main_widget) # 配置显示区域 config_group = QWidget() config_layout = QGridLayout(config_group) self.question_region_label = QLabel("题目区域: 未设置") self.option1_label = QLabel("选项1位置: 未设置") self.option2_label = QLabel("选项2位置: 未设置") self.option3_label = QLabel("选项3位置: 未设置") self.option4_label = QLabel("选项4位置: 未设置") self.calibrate_button = QPushButton("校准区域和位置") self.calibrate_button.clicked.connect(self.start_calibration) config_layout.addWidget(self.calibrate_button, 0, 0, 2, 1) config_layout.addWidget(self.question_region_label, 0, 1) config_layout.addWidget(self.option1_label, 1, 1) config_layout.addWidget(self.option2_label, 2, 1) config_layout.addWidget(self.option3_label, 3, 1) config_layout.addWidget(self.option4_label, 4, 1) # 日志和控制区域 self.log_box = QTextEdit() self.log_box.setReadOnly(True) self.log_box.setPlaceholderText("这里将显示运行日志...") self.start_button = QPushButton("开始答题") self.stop_button = QPushButton("紧急停止") self.stop_button.setEnabled(False) self.start_button.clicked.connect(self.start_task) self.stop_button.clicked.connect(self.stop_task) main_layout.addWidget(config_group) main_layout.addWidget(self.log_box) main_layout.addWidget(self.start_button) main_layout.addWidget(self.stop_button) def update_config_display(self): """更新界面上显示的配置信息""" q_region = self.config.get('question_region') self.question_region_label.setText(f"题目区域: {q_region if q_region else '未设置'}") o_coords = self.config.get('option_coords', [None] * 4) self.option1_label.setText(f"选项1位置: {o_coords[0] if o_coords[0] else '未设置'}") self.option2_label.setText(f"选项2位置: {o_coords[1] if o_coords[1] else '未设置'}") self.option3_label.setText(f"选项3位置: {o_coords[2] if o_coords[2] else '未设置'}") self.option4_label.setText(f"选项4位置: {o_coords[3] if o_coords[3] else '未设置'}") def start_calibration(self): """交互式校准流程""" self.log_box.clear() self.log_box.append("--- 开始校准 ---") self.showMinimized() # 最小化主窗口,方便操作 time.sleep(0.5) try: # 1. 校准题目区域 self.log_box.append("请用鼠标拖拽出题目识别区域...") QMessageBox.information(self, "校准步骤1/2", "请按住鼠标左键,拖拽出【题目】所在的矩形区域。") region = pyautogui.screenshot() # 截全屏让用户画 from PIL import Image img = Image.frombytes('RGB', region.size, region.tobytes(), 'raw', 'RGB') # 使用pyautogui的内置功能获取矩形区域 QMessageBox.information(self, "提示", "请看终端/命令行,根据提示操作。") print("请将鼠标移动到题目区域左上角,3秒后记录坐标...") time.sleep(3) p1 = pyautogui.position() print(f"左上角坐标: {p1}") print("请将鼠标移动到题目区域右下角,3秒后记录坐标...") time.sleep(3) p2 = pyautogui.position() print(f"右下角坐标: {p2}") x1, y1 = p1 x2, y2 = p2 self.config['question_region'] = (min(x1, x2), min(y1, y2), abs(x1 - x2), abs(y1 - y2)) # 2. 校准选项位置 QMessageBox.information(self, "校准步骤2/2", "接下来,请依次单击【4个选项】的中心位置。") self.config['option_coords'] = [] for i in range(4): print(f"请将鼠标移动到【选项 {i + 1}】的中心,3秒后记录坐标...") time.sleep(3) pos = pyautogui.position() self.config['option_coords'].append(pos) print(f"选项 {i + 1} 坐标: {pos}") save_config(self.config) self.log_box.append("校准完成并已保存!") except Exception as e: self.log_box.append(f"校准失败: {e}") finally: self.showNormal() # 恢复窗口 self.update_config_display() def start_task(self): if not all(self.config.values()) or not all(self.config['option_coords']): QMessageBox.warning(self, "配置不完整", "请先点击'校准'按钮完成所有区域和位置的设置!") return self.log_box.clear() self.start_button.setEnabled(False) self.stop_button.setEnabled(True) self.calibrate_button.setEnabled(False) self.thread = QThread() self.worker = Worker(self.config) # 先在主线程初始化,防止多线程问题 if self.worker.initialize(): self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.on_task_finished) self.worker.log_message.connect(self.log_box.append) self.thread.start() else: self.on_task_finished() # 初始化失败也需要恢复UI def on_task_finished(self): if self.thread: self.thread.quit() self.thread.wait() self.start_button.setEnabled(True) self.stop_button.setEnabled(False) self.calibrate_button.setEnabled(True) def stop_task(self): if self.worker: self.worker.stop() def closeEvent(self, event): self.stop_task() if self.thread and self.thread.isRunning(): self.thread.quit() self.thread.wait() event.accept()

--- 程序入口 ---

if name == 'main':
app = QApplication(sys.argv)
window = AnsweringApp()
window.show()
sys.exit(app.exec_())

Compartir este Q&A