# encoding:utf-8import sysimport osfrom datetime import datetimefrom typing import List, Dict, Optionalfrom PyQt6.QtWidgets import *from PyQt6.QtCore import *from PyQt6.QtGui import *class ModernMessageBox(QDialog): """现代化的消息对话框""" def __init__(self, title, message, icon_type="info", parent=None): super().__init__(parent) self.setWindowTitle(title) self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Dialog) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self.setModal(True) # 设置对话框大小 self.setFixedSize(380, 180) # 创建主布局 layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # 创建圆角卡片 card = QFrame() card.setObjectName("card") card.setStyleSheet("""#card { background-color: white; border-radius: 12px; border: 1px solid #e0e0e0; } """) card_layout = QVBoxLayout(card) card_layout.setSpacing(15) # 图标和标题 icon_label = QLabel() icon_label.setFixedSize(40, 40) icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter) if icon_type == "info": icon_label.setText("ℹ️") icon_label.setStyleSheet("font-size: 24px; color: #2196F3;") elif icon_type == "success": icon_label.setText("✅") icon_label.setStyleSheet("font-size: 24px; color: #4CAF50;") elif icon_type == "warning": icon_label.setText("⚠️") icon_label.setStyleSheet("font-size: 24px; color: #FF9800;") elif icon_type == "error": icon_label.setText("❌") icon_label.setStyleSheet("font-size: 24px; color: #f44336;") # 消息文本 message_label = QLabel(message) message_label.setWordWrap(True) message_label.setAlignment(Qt.AlignmentFlag.AlignCenter) message_label.setStyleSheet("font-size: 13px; color: #333; padding: 0 15px;") # 按钮 button = QPushButton("确定") button.setFixedSize(100, 35) button.setCursor(Qt.CursorShape.PointingHandCursor) button.setStyleSheet(""" QPushButton { background-color: #2196F3; color: white; border: none; border-radius: 6px; font-size: 13px; font-weight: bold; } QPushButton:hover { background-color: #1976D2; } QPushButton:pressed { background-color: #0D47A1; } """) button.clicked.connect(self.accept) # 添加到布局 card_layout.addWidget(icon_label, 0, Qt.AlignmentFlag.AlignCenter) card_layout.addWidget(message_label) button_layout = QHBoxLayout() button_layout.addStretch() button_layout.addWidget(button) button_layout.addStretch() card_layout.addLayout(button_layout) layout.addWidget(card) # 添加阴影效果 shadow = QGraphicsDropShadowEffect() shadow.setBlurRadius(15) shadow.setColor(QColor(0, 0, 0, 40)) shadow.setOffset(0, 3) card.setGraphicsEffect(shadow)class ModernSwitch(QCheckBox): """现代化的开关按钮""" def __init__(self, parent=None): super().__init__(parent) self.setFixedSize(50, 24) self.setCursor(Qt.CursorShape.PointingHandCursor) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.RenderHint.Antialiasing) # 绘制背景 rect = QRect(0, 0, 50, 24) if self.isChecked(): painter.setBrush(QColor(76, 175, 80)) painter.setPen(Qt.PenStyle.NoPen) painter.drawRoundedRect(rect, 12, 12) # 绘制圆点 painter.setBrush(QColor(255, 255, 255)) painter.drawEllipse(29, 4, 16, 16) else: painter.setBrush(QColor(200, 200, 200)) painter.setPen(Qt.PenStyle.NoPen) painter.drawRoundedRect(rect, 12, 12) # 绘制圆点 painter.setBrush(QColor(255, 255, 255)) painter.drawEllipse(5, 4, 16, 16)class ModernLineEdit(QLineEdit): """现代化的输入框""" def __init__(self, placeholder="", parent=None): super().__init__(parent) self.setPlaceholderText(placeholder) self.setFixedHeight(35) self.setStyleSheet(""" QLineEdit { border: 1px solid #e0e0e0; border-radius: 6px; padding: 0 12px; font-size: 13px; background-color: white; } QLineEdit:focus { border: 2px solid #2196F3; } """)class ModernComboBox(QComboBox): """现代化的下拉框""" def __init__(self, parent=None): super().__init__(parent) self.setFixedHeight(35) self.setStyleSheet(""" QComboBox { border: 1px solid #e0e0e0; border-radius: 6px; padding: 0 12px; font-size: 13px; background-color: white; } QComboBox::drop-down { border: none; width: 25px; } QComboBox::down-arrow { width: 10px; height: 10px; } QComboBox:hover { border: 2px solid #2196F3; } """)class ModernPushButton(QPushButton): """现代化的按钮""" def __init__(self, text, icon=None, primary=True, parent=None): super().__init__(text, parent) self.setCursor(Qt.CursorShape.PointingHandCursor) self.setFixedHeight(38) if primary: self.setStyleSheet(""" QPushButton { background-color: #2196F3; color: white; border: none; border-radius: 6px; font-size: 13px; font-weight: bold; padding: 0 15px; } QPushButton:hover { background-color: #1976D2; } QPushButton:pressed { background-color: #0D47A1; } QPushButton:disabled { background-color: #e0e0e0; color: #999; } """) else: self.setStyleSheet(""" QPushButton { background-color: white; color: #333; border: 1px solid #e0e0e0; border-radius: 6px; font-size: 13px; font-weight: bold; padding: 0 15px; } QPushButton:hover { background-color: #f5f5f5; border: 2px solid #2196F3; } QPushButton:pressed { background-color: #e0e0e0; } QPushButton:disabled { background-color: #f5f5f5; color: #999; border: 1px solid #e0e0e0; } """) if icon: self.setIcon(QIcon(icon)) self.setIconSize(QSize(18, 18))class ModernProgressBar(QProgressBar): """现代化的进度条""" def __init__(self, parent=None): super().__init__(parent) self.setFixedHeight(16) self.setTextVisible(False) self.setStyleSheet(""" QProgressBar { border: none; background-color: #f0f0f0; border-radius: 8px; } QProgressBar::chunk { background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #2196F3, stop:1 #64B5F6); border-radius: 8px; } """)class MainWindow(QMainWindow): """主窗口""" def __init__(self): super().__init__() self.setWindowTitle("百度OCR大模型快递面单批量识别工具(欢迎关注微信公众号:码海听潮)") self.setMinimumSize(900, 650) self.resize(900, 650) # 设置窗口图标 self.setWindowIcon(self.create_window_icon()) # 设置全局样式 self.setStyleSheet(""" QMainWindow { background-color: #f5f7fa; } QLabel { color: #333; font-size: 12px; } QGroupBox { font-weight: bold; font-size: 13px; border: 1px solid #e0e0e0; border-radius: 8px; margin-top: 8px; padding-top: 8px; } QGroupBox::title { subcontrol-origin: margin; left: 8px; padding: 0 5px 0 5px; } QTableWidget { border: 1px solid #e0e0e0; border-radius: 8px; background-color: white; gridline-color: #f0f0f0; } QTableWidget::item { padding: 4px; } QTableWidget::item:selected { background-color: #E3F2FD; color: #333; } QHeaderView::section { background-color: #f5f5f5; padding: 6px; border: none; border-right: 1px solid #e0e0e0; border-bottom: 1px solid #e0e0e0; font-weight: bold; font-size: 12px; } QScrollBar:vertical { border: none; background-color: #f5f5f5; width: 8px; border-radius: 4px; } QScrollBar::handle:vertical { background-color: #c0c0c0; border-radius: 4px; } QScrollBar::handle:vertical:hover { background-color: #a0a0a0; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { border: none; background: none; } """) # 初始化变量 self.results = [] # 创建UI self.setup_ui() def create_window_icon(self): """创建窗口图标""" icon = QIcon() pixmap = QPixmap(48, 48) pixmap.fill(Qt.GlobalColor.transparent) painter = QPainter(pixmap) painter.setRenderHint(QPainter.RenderHint.Antialiasing) # 绘制一个现代化的图标 painter.setBrush(QColor(33, 150, 243)) painter.setPen(Qt.PenStyle.NoPen) painter.drawRoundedRect(0, 0, 48, 48, 10, 10) painter.setPen(QPen(QColor(255, 255, 255), 2)) painter.setFont(QFont("Arial", 20, QFont.Weight.Bold)) painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, "OCR") painter.end() icon.addPixmap(pixmap) return icon def setup_ui(self): """设置UI""" # 创建中央部件 central_widget = QWidget() self.setCentralWidget(central_widget) # 创建主布局 main_layout = QHBoxLayout(central_widget) main_layout.setContentsMargins(15, 15, 15, 15) main_layout.setSpacing(15) # 左侧面板(设置区域) left_panel = self.create_left_panel() main_layout.addWidget(left_panel, 35) # 右侧面板(表格区域) right_panel = self.create_right_panel() main_layout.addWidget(right_panel, 65) def create_left_panel(self): """创建左侧面板""" panel = QFrame() panel.setObjectName("leftPanel") panel.setStyleSheet("""#leftPanel { background-color: white; border-radius: 12px; border: 1px solid #e0e0e0; } """) # 使用QVBoxLayout作为主布局 layout = QVBoxLayout(panel) layout.setSpacing(12) layout.setContentsMargins(15, 15, 15, 15) # API设置组 api_group = QGroupBox("API 设置") api_group.setStyleSheet(""" QGroupBox { font-size: 13px; font-weight: bold; border: 1px solid #e0e0e0; border-radius: 8px; margin-top: 8px; padding-top: 12px; } QGroupBox::title { subcontrol-origin: margin; left: 8px; padding: 0 8px 0 8px; color: #2196F3; } """) api_layout = QVBoxLayout(api_group) api_layout.setSpacing(10) api_layout.setContentsMargins(12, 12, 12, 12) # API Key api_key_label = QLabel("API Key:") api_key_label.setStyleSheet("font-weight: bold; font-size: 12px;") self.api_key_edit = ModernLineEdit("请输入您的API Key") api_layout.addWidget(api_key_label) api_layout.addWidget(self.api_key_edit) # Secret Key secret_key_label = QLabel("Secret Key:") secret_key_label.setStyleSheet("font-weight: bold; font-size: 12px;") self.secret_key_edit = ModernLineEdit("请输入您的Secret Key") self.secret_key_edit.setEchoMode(QLineEdit.EchoMode.Password) api_layout.addWidget(secret_key_label) api_layout.addWidget(self.secret_key_edit) # 保存API密钥按钮 save_api_btn = ModernPushButton("保存API密钥", primary=False) save_api_btn.clicked.connect(self.save_api_keys) api_layout.addWidget(save_api_btn) layout.addWidget(api_group) # ===== Token状态组 ===== token_group = QGroupBox("Token 状态") token_group.setStyleSheet(api_group.styleSheet()) token_layout = QVBoxLayout(token_group) token_layout.setSpacing(8) token_layout.setContentsMargins(12, 12, 12, 12) self.token_status_label = QLabel("Token状态: 未获取") self.token_status_label.setStyleSheet("color: #f44336; font-weight: bold; font-size: 12px;") token_layout.addWidget(self.token_status_label) self.token_expire_label = QLabel("过期时间: --") self.token_expire_label.setStyleSheet("font-size: 12px;") token_layout.addWidget(self.token_expire_label) self.token_remaining_label = QLabel("剩余天数: --") self.token_remaining_label.setStyleSheet("font-size: 12px;") token_layout.addWidget(self.token_remaining_label) # 刷新Token按钮 refresh_token_btn = ModernPushButton("刷新Token", primary=False) refresh_token_btn.clicked.connect(self.refresh_token) token_layout.addWidget(refresh_token_btn) layout.addWidget(token_group) # ===== 文件夹选择组 ===== folder_group = QGroupBox("文件夹设置") folder_group.setStyleSheet(api_group.styleSheet()) folder_layout = QVBoxLayout(folder_group) folder_layout.setSpacing(10) folder_layout.setContentsMargins(12, 12, 12, 12) # 文件夹路径 folder_path_label = QLabel("图片文件夹路径:") folder_path_label.setStyleSheet("font-weight: bold; font-size: 12px;") self.folder_path_edit = ModernLineEdit("请选择包含图片的文件夹") folder_layout.addWidget(folder_path_label) folder_layout.addWidget(self.folder_path_edit) # 选择文件夹按钮 select_folder_btn = ModernPushButton("选择文件夹", primary=False) select_folder_btn.clicked.connect(self.select_folder) folder_layout.addWidget(select_folder_btn) layout.addWidget(folder_group) # 操作按钮 button_layout = QHBoxLayout() button_layout.setSpacing(8) self.start_btn = ModernPushButton("▶ 开始处理") self.start_btn.clicked.connect(self.start_processing) button_layout.addWidget(self.start_btn) self.stop_btn = ModernPushButton("■ 停止处理", primary=False) self.stop_btn.setEnabled(False) self.stop_btn.clicked.connect(self.stop_processing) button_layout.addWidget(self.stop_btn) layout.addLayout(button_layout) # 添加弹性空间 layout.addStretch() return panel def create_right_panel(self): """创建右侧面板""" panel = QFrame() panel.setObjectName("rightPanel") panel.setStyleSheet("""#rightPanel { background-color: white; border-radius: 12px; border: 1px solid #e0e0e0; } """) layout = QVBoxLayout(panel) layout.setSpacing(12) layout.setContentsMargins(15, 15, 15, 15) # 统计标签 self.stats_label = QLabel("总计: 0 | 成功: 0 | 失败: 0") self.stats_label.setStyleSheet(""" background-color: #f5f5f5; padding: 6px 12px; border-radius: 12px; font-weight: bold; color: #666; font-size: 13px; """) self.stats_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.stats_label, 0, Qt.AlignmentFlag.AlignCenter) # 结果表格 self.table = QTableWidget() self.table.setAlternatingRowColors(True) self.table.setSortingEnabled(True) self.table.horizontalHeader().setStretchLastSection(True) layout.addWidget(self.table, 1) # 导出按钮 export_btn = ModernPushButton("📥 导出Excel", primary=False) export_btn.clicked.connect(self.export_excel) layout.addWidget(export_btn, 0, Qt.AlignmentFlag.AlignRight) return panel def save_api_keys(self): """保存API密钥""" api_key = self.api_key_edit.text().strip() secret_key = self.secret_key_edit.text().strip() if not api_key or not secret_key: ModernMessageBox("错误", "请输入完整的API Key和Secret Key", "error", self).exec() return # 模拟保存成功 self.token_status_label.setText("Token状态: ✅ 有效") self.token_status_label.setStyleSheet("color: #4CAF50; font-weight: bold; font-size: 12px;") self.token_expire_label.setText(f"过期时间: {(datetime.now().replace(hour=0, minute=0, second=0) + timedelta(days=30)).strftime('%Y-%m-%d %H:%M:%S')}") self.token_remaining_label.setText("剩余天数: 30 天") ModernMessageBox("成功", "API密钥保存成功!", "success", self).exec() def refresh_token(self): """刷新Token""" if not self.api_key_edit.text() or not self.secret_key_edit.text(): ModernMessageBox("错误", "请先输入API Key和Secret Key", "error", self).exec() return # 模拟刷新成功 ModernMessageBox("成功", "Token刷新成功!", "success", self).exec() def select_folder(self): """选择文件夹""" folder_path = QFileDialog.getExistingDirectory( self, "选择图片文件夹", os.path.expanduser("~"), QFileDialog.Option.ShowDirsOnly ) if folder_path: self.folder_path_edit.setText(folder_path) def start_processing(self): """开始处理""" # 验证输入 folder_path = self.folder_path_edit.text().strip() if not folder_path or not os.path.exists(folder_path): ModernMessageBox("错误", "请选择有效的文件夹", "error", self).exec() return api_key = self.api_key_edit.text().strip() secret_key = self.secret_key_edit.text().strip() if not api_key or not secret_key: ModernMessageBox("错误", "请输入API Key和Secret Key", "error", self).exec() return # 清空表格 self.table.setRowCount(0) self.table.setColumnCount(0) self.results = [] # 更新UI状态 self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) # 模拟处理过程 self.simulate_processing() def simulate_processing(self): """模拟处理过程(仅用于界面演示)""" # 这里只是界面演示,不实际调用OCR ModernMessageBox("提示", "这是一个界面演示版本,不包含实际的OCR功能。\n\n如需完整功能,请集成百度OCR API。", "info", self).exec() # 恢复按钮状态 self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) def stop_processing(self): """停止处理""" self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) def export_excel(self): """导出Excel""" if not self.results: ModernMessageBox("提示", "没有数据可导出", "info", self).exec() return # 选择保存路径 file_path, _ = QFileDialog.getSaveFileName( self, "保存Excel文件", os.path.join(os.path.expanduser("~"), "Desktop", f"快递面单识别结果_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"), "Excel文件 (*.xlsx)" ) if file_path: ModernMessageBox("成功", f"Excel文件已保存到:\n{file_path}", "success", self).exec()def main(): """主函数""" app = QApplication(sys.argv) # 设置应用程序样式 app.setStyle('Fusion') # 设置字体 font = QFont("Microsoft YaHei", 9) app.setFont(font) # 创建并显示主窗口 window = MainWindow() window.show() sys.exit(app.exec())if __name__ == "__main__": main()