|
|
@@ -0,0 +1,183 @@
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
+"""PyQt5 主窗口:选择文件、保存路径、转化、进度条。"""
|
|
|
+import os
|
|
|
+from pathlib import Path
|
|
|
+
|
|
|
+from PyQt5.QtWidgets import (
|
|
|
+ QWidget,
|
|
|
+ QVBoxLayout,
|
|
|
+ QHBoxLayout,
|
|
|
+ QPushButton,
|
|
|
+ QLabel,
|
|
|
+ QFileDialog,
|
|
|
+ QProgressBar,
|
|
|
+ QMessageBox,
|
|
|
+ QApplication,
|
|
|
+ QMainWindow,
|
|
|
+ QFrame,
|
|
|
+)
|
|
|
+from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
|
|
+from PyQt5.QtGui import QFont
|
|
|
+
|
|
|
+from .config_loader import get_ai_config
|
|
|
+from .excel_processor import process_excel
|
|
|
+
|
|
|
+
|
|
|
+class ConvertWorker(QThread):
|
|
|
+ """在后台线程执行 Excel 转化,避免阻塞界面。"""
|
|
|
+ progress = pyqtSignal(int, int, str) # current, total, message
|
|
|
+ finished = pyqtSignal(str) # 保存路径
|
|
|
+ error = pyqtSignal(str)
|
|
|
+
|
|
|
+ def __init__(self, input_path: str, output_dir: str, api_base_url=None, api_key=None):
|
|
|
+ super().__init__()
|
|
|
+ self.input_path = input_path
|
|
|
+ self.output_dir = output_dir
|
|
|
+ self.api_base_url = api_base_url
|
|
|
+ self.api_key = api_key
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ try:
|
|
|
+ def report(current: int, total: int, message: str = ""):
|
|
|
+ self.progress.emit(current, max(total, 1), message or "")
|
|
|
+
|
|
|
+ out_path = process_excel(
|
|
|
+ self.input_path,
|
|
|
+ self.output_dir,
|
|
|
+ api_base_url=self.api_base_url,
|
|
|
+ api_key=self.api_key,
|
|
|
+ progress_callback=report,
|
|
|
+ )
|
|
|
+ self.finished.emit(out_path)
|
|
|
+ except Exception as e:
|
|
|
+ self.error.emit(str(e))
|
|
|
+
|
|
|
+
|
|
|
+class MainWindow(QMainWindow):
|
|
|
+ def __init__(self):
|
|
|
+ super().__init__()
|
|
|
+ self.setWindowTitle("选品部图片去文字")
|
|
|
+ self.setMinimumWidth(560)
|
|
|
+ self.setMinimumHeight(260)
|
|
|
+
|
|
|
+ self._file_path = ""
|
|
|
+ self._save_dir = ""
|
|
|
+ self._worker = None
|
|
|
+
|
|
|
+ central = QWidget()
|
|
|
+ self.setCentralWidget(central)
|
|
|
+ layout = QVBoxLayout(central)
|
|
|
+ layout.setSpacing(12)
|
|
|
+
|
|
|
+ # 选择文件
|
|
|
+ btn_file = QPushButton("选择文件")
|
|
|
+ btn_file.setMinimumHeight(36)
|
|
|
+ btn_file.clicked.connect(self._on_select_file)
|
|
|
+ layout.addWidget(btn_file)
|
|
|
+
|
|
|
+ # 文件路径
|
|
|
+ row_path = QHBoxLayout()
|
|
|
+ lbl_path = QLabel("文件路径:")
|
|
|
+ lbl_path.setMinimumWidth(80)
|
|
|
+ self._lbl_file_path = QLabel("")
|
|
|
+ self._lbl_file_path.setWordWrap(True)
|
|
|
+ self._lbl_file_path.setStyleSheet("color: #333;")
|
|
|
+ row_path.addWidget(lbl_path)
|
|
|
+ row_path.addWidget(self._lbl_file_path, 1)
|
|
|
+ layout.addLayout(row_path)
|
|
|
+
|
|
|
+ # 保存路径
|
|
|
+ row_save = QHBoxLayout()
|
|
|
+ btn_save = QPushButton("保存路径")
|
|
|
+ btn_save.setMinimumHeight(32)
|
|
|
+ btn_save.clicked.connect(self._on_select_save_dir)
|
|
|
+ self._lbl_save_path = QLabel("")
|
|
|
+ self._lbl_save_path.setWordWrap(True)
|
|
|
+ self._lbl_save_path.setStyleSheet("color: #333;")
|
|
|
+ row_save.addWidget(btn_save)
|
|
|
+ row_save.addWidget(self._lbl_save_path, 1)
|
|
|
+ layout.addLayout(row_save)
|
|
|
+
|
|
|
+ # 转化按钮
|
|
|
+ btn_convert = QPushButton("转化")
|
|
|
+ btn_convert.setMinimumHeight(40)
|
|
|
+ btn_convert.clicked.connect(self._on_convert)
|
|
|
+ layout.addWidget(btn_convert)
|
|
|
+
|
|
|
+ # 进度条(初始隐藏)
|
|
|
+ self._progress = QProgressBar()
|
|
|
+ self._progress.setMinimum(0)
|
|
|
+ self._progress.setMaximum(100)
|
|
|
+ self._progress.setValue(0)
|
|
|
+ self._progress.setTextVisible(True)
|
|
|
+ self._progress.setVisible(False)
|
|
|
+ layout.addWidget(self._progress)
|
|
|
+
|
|
|
+ self._lbl_file_path.setText("")
|
|
|
+ self._lbl_save_path.setText("")
|
|
|
+
|
|
|
+ def _on_select_file(self):
|
|
|
+ path, _ = QFileDialog.getOpenFileName(
|
|
|
+ self,
|
|
|
+ "选择 Excel 文件",
|
|
|
+ "",
|
|
|
+ "Excel 文件 (*.xlsx *.xls);;所有文件 (*.*)",
|
|
|
+ )
|
|
|
+ if path:
|
|
|
+ self._file_path = path
|
|
|
+ self._lbl_file_path.setText(path)
|
|
|
+
|
|
|
+ def _on_select_save_dir(self):
|
|
|
+ path = QFileDialog.getExistingDirectory(self, "选择保存目录", "")
|
|
|
+ if path:
|
|
|
+ self._save_dir = path
|
|
|
+ self._lbl_save_path.setText(path)
|
|
|
+
|
|
|
+ def _on_convert(self):
|
|
|
+ if not self._file_path or not os.path.isfile(self._file_path):
|
|
|
+ QMessageBox.warning(self, "提示", "请先选择有效的 Excel 文件。")
|
|
|
+ return
|
|
|
+ if not self._save_dir or not os.path.isdir(self._save_dir):
|
|
|
+ QMessageBox.warning(self, "提示", "请先选择保存路径(目录)。")
|
|
|
+ return
|
|
|
+
|
|
|
+ ext = Path(self._file_path).suffix.lower()
|
|
|
+ if ext == ".xls":
|
|
|
+ QMessageBox.information(
|
|
|
+ self,
|
|
|
+ "提示",
|
|
|
+ "当前仅对 .xlsx 格式进行图片去文字处理;.xls 将尝试按 xlsx 方式处理,若失败请先另存为 xlsx。",
|
|
|
+ )
|
|
|
+
|
|
|
+ self._progress.setVisible(True)
|
|
|
+ self._progress.setValue(0)
|
|
|
+ self._progress.setFormat("%p%")
|
|
|
+
|
|
|
+ api_base, api_key = get_ai_config()
|
|
|
+ self._worker = ConvertWorker(
|
|
|
+ self._file_path,
|
|
|
+ self._save_dir,
|
|
|
+ api_base_url=api_base,
|
|
|
+ api_key=api_key,
|
|
|
+ )
|
|
|
+ self._worker.progress.connect(self._on_progress)
|
|
|
+ self._worker.finished.connect(self._on_finished)
|
|
|
+ self._worker.error.connect(self._on_error)
|
|
|
+ self._worker.start()
|
|
|
+
|
|
|
+ def _on_progress(self, current: int, total: int, message: str):
|
|
|
+ if total > 0:
|
|
|
+ self._progress.setMaximum(total)
|
|
|
+ self._progress.setValue(current)
|
|
|
+ if message:
|
|
|
+ self._progress.setFormat(f"%p% - {message}")
|
|
|
+
|
|
|
+ def _on_finished(self, out_path: str):
|
|
|
+ self._progress.setVisible(True)
|
|
|
+ self._progress.setValue(self._progress.maximum())
|
|
|
+ self._progress.setFormat("100% - 完成")
|
|
|
+ QMessageBox.information(self, "完成", f"已保存到:\n{out_path}")
|
|
|
+
|
|
|
+ def _on_error(self, err: str):
|
|
|
+ self._progress.setVisible(False)
|
|
|
+ QMessageBox.critical(self, "错误", f"转化失败:\n{err}")
|