|
|
@@ -5,12 +5,17 @@ import os
|
|
|
import re
|
|
|
import uuid
|
|
|
import hashlib
|
|
|
+import time
|
|
|
+import threading
|
|
|
+from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
from Crypto.Cipher import AES
|
|
|
from Crypto.Util.Padding import unpad
|
|
|
from typing import Optional, List, Dict, Any
|
|
|
from PyQt5.QtWidgets import *
|
|
|
from PyQt5.QtCore import *
|
|
|
from PyQt5.QtGui import *
|
|
|
+from requests.adapters import HTTPAdapter
|
|
|
+from urllib3.util.retry import Retry
|
|
|
|
|
|
# --- 核心功能类 ---
|
|
|
class OrderManager:
|
|
|
@@ -37,6 +42,26 @@ class OrderManager:
|
|
|
}
|
|
|
self.key = b"S9u978T13NLCGc5W"
|
|
|
self.iv = b"X83yWMD9iKhLxfwX"
|
|
|
+
|
|
|
+ # 缓存
|
|
|
+ self.order_detail_cache = {}
|
|
|
+ self.ocr_cache = {}
|
|
|
+ self.cache_lock = threading.Lock()
|
|
|
+
|
|
|
+ # 创建会话并配置连接池
|
|
|
+ self.session = requests.Session()
|
|
|
+ retry_strategy = Retry(
|
|
|
+ total=2,
|
|
|
+ backoff_factor=0.5,
|
|
|
+ status_forcelist=[429, 500, 502, 503, 504],
|
|
|
+ )
|
|
|
+ adapter = HTTPAdapter(
|
|
|
+ pool_connections=20,
|
|
|
+ pool_maxsize=20,
|
|
|
+ max_retries=retry_strategy
|
|
|
+ )
|
|
|
+ self.session.mount('http://', adapter)
|
|
|
+ self.session.mount('https://', adapter)
|
|
|
|
|
|
def decrypt_order_data(self, hex_string):
|
|
|
"""解密订单数据"""
|
|
|
@@ -62,7 +87,7 @@ class OrderManager:
|
|
|
}
|
|
|
|
|
|
try:
|
|
|
- response = requests.post(url, headers=self.headers, json=payload, timeout=30)
|
|
|
+ response = self.session.post(url, headers=self.headers, json=payload, timeout=30)
|
|
|
response.raise_for_status()
|
|
|
result = response.json()
|
|
|
|
|
|
@@ -74,13 +99,18 @@ class OrderManager:
|
|
|
return None, str(e)
|
|
|
|
|
|
def get_order_detail(self, order_no):
|
|
|
- """获取订单详情"""
|
|
|
+ """获取订单详情(带缓存)"""
|
|
|
+ # 检查缓存
|
|
|
+ with self.cache_lock:
|
|
|
+ if order_no in self.order_detail_cache:
|
|
|
+ return self.order_detail_cache[order_no], None
|
|
|
+
|
|
|
# 获取BiddingNo
|
|
|
snatch_url = "https://wirelessgate.aihuishou.com/jdx-rc-service/front/bid/snatch"
|
|
|
snatch_payload = {"orderNo": order_no}
|
|
|
|
|
|
try:
|
|
|
- snatch_response = requests.post(snatch_url, headers=self.headers, json=snatch_payload, timeout=30)
|
|
|
+ snatch_response = self.session.post(snatch_url, headers=self.headers, json=snatch_payload, timeout=15)
|
|
|
snatch_response.raise_for_status()
|
|
|
snatch_data = snatch_response.json()
|
|
|
|
|
|
@@ -98,7 +128,7 @@ class OrderManager:
|
|
|
detail_url = f"https://wirelessgate.aihuishou.com/jdx-qa-service/app/ka/v3/order/detail?orderNo={order_no}&biddingNo={bidding_no}"
|
|
|
|
|
|
try:
|
|
|
- detail_response = requests.get(detail_url, headers=self.headers, timeout=30)
|
|
|
+ detail_response = self.session.get(detail_url, headers=self.headers, timeout=15)
|
|
|
detail_response.raise_for_status()
|
|
|
detail_data = detail_response.json()
|
|
|
|
|
|
@@ -112,70 +142,82 @@ class OrderManager:
|
|
|
decrypted_json_string = self.decrypt_order_data(encrypted_hex)
|
|
|
order_detail = json.loads(decrypted_json_string)
|
|
|
|
|
|
+ # 存入缓存
|
|
|
+ with self.cache_lock:
|
|
|
+ self.order_detail_cache[order_no] = order_detail
|
|
|
+
|
|
|
return order_detail, None
|
|
|
|
|
|
except Exception as e:
|
|
|
return None, str(e)
|
|
|
|
|
|
def find_original_image_by_notice(self, json_data, notice_keyword):
|
|
|
- """查找图片链接"""
|
|
|
+ """优化的图片查找(使用栈迭代)"""
|
|
|
if isinstance(json_data, str):
|
|
|
try:
|
|
|
json_data = json.loads(json_data)
|
|
|
except json.JSONDecodeError:
|
|
|
return None
|
|
|
-
|
|
|
- def recursive_search(obj):
|
|
|
+
|
|
|
+ # 使用栈迭代而非递归
|
|
|
+ stack = [json_data]
|
|
|
+ keyword_lower = notice_keyword.lower()
|
|
|
+
|
|
|
+ while stack:
|
|
|
+ obj = stack.pop()
|
|
|
+
|
|
|
if isinstance(obj, dict):
|
|
|
+ # 检查当前字典
|
|
|
if 'notice' in obj and 'originalImage' in obj:
|
|
|
- if notice_keyword.lower() in obj['notice'].lower():
|
|
|
+ if keyword_lower in obj['notice'].lower():
|
|
|
return obj['originalImage']
|
|
|
|
|
|
+ # 将所有值加入栈
|
|
|
for value in obj.values():
|
|
|
- result = recursive_search(value)
|
|
|
- if result:
|
|
|
- return result
|
|
|
-
|
|
|
+ stack.append(value)
|
|
|
+
|
|
|
elif isinstance(obj, list):
|
|
|
- for item in obj:
|
|
|
- result = recursive_search(item)
|
|
|
- if result:
|
|
|
- return result
|
|
|
-
|
|
|
- return None
|
|
|
-
|
|
|
- return recursive_search(json_data)
|
|
|
+ # 将列表元素加入栈
|
|
|
+ stack.extend(obj)
|
|
|
+
|
|
|
+ return None
|
|
|
|
|
|
def get_all_image_notices(self, json_data):
|
|
|
"""获取所有图片的notice信息"""
|
|
|
notices = []
|
|
|
+ stack = [json_data]
|
|
|
|
|
|
- def recursive_search(obj):
|
|
|
+ while stack:
|
|
|
+ obj = stack.pop()
|
|
|
+
|
|
|
if isinstance(obj, dict):
|
|
|
if 'notice' in obj and 'image' in obj:
|
|
|
notices.append(obj['notice'])
|
|
|
for value in obj.values():
|
|
|
- recursive_search(value)
|
|
|
+ stack.append(value)
|
|
|
elif isinstance(obj, list):
|
|
|
- for item in obj:
|
|
|
- recursive_search(item)
|
|
|
+ stack.extend(obj)
|
|
|
|
|
|
- recursive_search(json_data)
|
|
|
- return list(set(notices)) # 去重
|
|
|
+ return list(set(notices))
|
|
|
|
|
|
def ocr_by_url(self, image_url):
|
|
|
- """通过URL进行OCR识别"""
|
|
|
+ """通过URL进行OCR识别(带缓存)"""
|
|
|
+ # 检查缓存
|
|
|
+ cache_key = hashlib.md5(image_url.encode()).hexdigest()
|
|
|
+ with self.cache_lock:
|
|
|
+ if cache_key in self.ocr_cache:
|
|
|
+ return self.ocr_cache[cache_key]
|
|
|
+
|
|
|
try:
|
|
|
- # 构建请求数据
|
|
|
payload = {"url": image_url}
|
|
|
-
|
|
|
- # 发送POST请求
|
|
|
- response = requests.post(self.ocr_url, json=payload, timeout=60)
|
|
|
+ response = self.session.post(self.ocr_url, json=payload, timeout=30)
|
|
|
|
|
|
if response.status_code == 200:
|
|
|
ocr_result = response.json()
|
|
|
- print(f"OCR完整返回: {json.dumps(ocr_result)}")
|
|
|
if ocr_result.get('success'):
|
|
|
+ # 存入缓存
|
|
|
+ with self.cache_lock:
|
|
|
+ self.ocr_cache[cache_key] = ocr_result
|
|
|
return ocr_result
|
|
|
return None
|
|
|
|
|
|
@@ -183,7 +225,6 @@ class OrderManager:
|
|
|
return None
|
|
|
|
|
|
def extract_text_from_ocr_result(self, ocr_result):
|
|
|
- print(ocr_result)
|
|
|
"""提取OCR文本"""
|
|
|
if not ocr_result or not ocr_result.get('texts'):
|
|
|
return ''
|
|
|
@@ -217,22 +258,109 @@ class OrderManager:
|
|
|
return int(match.group(1))
|
|
|
|
|
|
return None
|
|
|
+
|
|
|
+ def find_best_image(self, order_detail):
|
|
|
+ """查找最佳验机图片(按优先级)"""
|
|
|
+ keywords = [
|
|
|
+ "苹果沙漏验机报告",
|
|
|
+ "沙漏图",
|
|
|
+ "苹果验机软件",
|
|
|
+ "苹果沙漏验机基本信息",
|
|
|
+ "验机照片",
|
|
|
+ "验机报告"
|
|
|
+ ]
|
|
|
+
|
|
|
+ for keyword in keywords:
|
|
|
+ img = self.find_original_image_by_notice(order_detail, keyword)
|
|
|
+ if img:
|
|
|
+ return img
|
|
|
+ return None
|
|
|
+
|
|
|
+# --- 后台工作线程 ---
|
|
|
+class OrderListWorker(QThread):
|
|
|
+ finished = pyqtSignal(list)
|
|
|
+ error = pyqtSignal(str)
|
|
|
+
|
|
|
+ def __init__(self, order_manager):
|
|
|
+ super().__init__()
|
|
|
+ self.order_manager = order_manager
|
|
|
|
|
|
-# --- GUI主窗口 ---
|
|
|
+ def run(self):
|
|
|
+ try:
|
|
|
+ order_list, error = self.order_manager.get_pending_order_list(100)
|
|
|
+ if error:
|
|
|
+ self.error.emit(error)
|
|
|
+ else:
|
|
|
+ self.finished.emit(order_list)
|
|
|
+ except Exception as e:
|
|
|
+ self.error.emit(str(e))
|
|
|
+
|
|
|
+class ChargeQueryWorker(QThread):
|
|
|
+ finished = pyqtSignal(int, object, object, list)
|
|
|
+ error = pyqtSignal(int, str)
|
|
|
+
|
|
|
+ def __init__(self, order_manager, order_no, index):
|
|
|
+ super().__init__()
|
|
|
+ self.order_manager = order_manager
|
|
|
+ self.order_no = order_no
|
|
|
+ self.index = index
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ try:
|
|
|
+ # 获取订单详情
|
|
|
+ order_detail, error = self.order_manager.get_order_detail(self.order_no)
|
|
|
+ if error:
|
|
|
+ self.error.emit(self.index, error)
|
|
|
+ return
|
|
|
+
|
|
|
+ # 查找所有图片的notice
|
|
|
+ image_notices = self.order_manager.get_all_image_notices(order_detail)
|
|
|
+
|
|
|
+ # 查找验机图片
|
|
|
+ report_img = self.order_manager.find_best_image(order_detail)
|
|
|
+
|
|
|
+ if not report_img:
|
|
|
+ self.finished.emit(self.index, None, None, image_notices)
|
|
|
+ return
|
|
|
+
|
|
|
+ # OCR识别
|
|
|
+ ocr_result = self.order_manager.ocr_by_url(report_img)
|
|
|
+ if not ocr_result:
|
|
|
+ self.finished.emit(self.index, None, None, image_notices)
|
|
|
+ return
|
|
|
+
|
|
|
+ # 提取充电次数
|
|
|
+ ocr_text = self.order_manager.extract_text_from_ocr_result(ocr_result)
|
|
|
+ charge_count = self.order_manager.extract_charge_count(ocr_text)
|
|
|
+
|
|
|
+ self.finished.emit(self.index, charge_count, ocr_result, image_notices)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ self.error.emit(self.index, str(e))
|
|
|
+
|
|
|
+# --- 主窗口 ---
|
|
|
class MainWindow(QMainWindow):
|
|
|
def __init__(self):
|
|
|
super().__init__()
|
|
|
self.order_manager = None
|
|
|
self.order_list = []
|
|
|
- self.order_details_cache = {} # 缓存订单详情
|
|
|
- self.charge_counts = {} # 缓存充电次数
|
|
|
+ self.order_details_cache = {}
|
|
|
+ self.charge_counts = {}
|
|
|
self.is_batch_processing = False
|
|
|
self.batch_queue = []
|
|
|
self.current_batch_index = 0
|
|
|
+
|
|
|
+ # 并行处理相关
|
|
|
+ self.thread_pool = ThreadPoolExecutor(max_workers=5)
|
|
|
+ self.future_tasks = {}
|
|
|
+ self.processing_lock = threading.Lock()
|
|
|
+ self.completed_count = 0
|
|
|
+ self.total_tasks = 0
|
|
|
+
|
|
|
self.init_ui()
|
|
|
|
|
|
def init_ui(self):
|
|
|
- self.setWindowTitle('爱回收订单查询工具')
|
|
|
+ self.setWindowTitle('爱回收订单查询工具 (优化版)')
|
|
|
self.setGeometry(100, 100, 1200, 800)
|
|
|
|
|
|
# 设置样式
|
|
|
@@ -315,7 +443,6 @@ class MainWindow(QMainWindow):
|
|
|
}
|
|
|
""")
|
|
|
|
|
|
- # 中央部件
|
|
|
central_widget = QWidget()
|
|
|
self.setCentralWidget(central_widget)
|
|
|
main_layout = QVBoxLayout(central_widget)
|
|
|
@@ -325,26 +452,29 @@ class MainWindow(QMainWindow):
|
|
|
config_group = QGroupBox("配置")
|
|
|
config_layout = QGridLayout()
|
|
|
|
|
|
- # Token输入
|
|
|
config_layout.addWidget(QLabel("Access Token:"), 0, 0)
|
|
|
self.token_input = QLineEdit()
|
|
|
self.token_input.setText("jdx9ce52336abee4cf78db56567dc259512")
|
|
|
self.token_input.setEchoMode(QLineEdit.Password)
|
|
|
config_layout.addWidget(self.token_input, 0, 1)
|
|
|
|
|
|
- # 显示/隐藏Token按钮
|
|
|
self.show_token_btn = QPushButton("显示")
|
|
|
self.show_token_btn.setFixedWidth(60)
|
|
|
self.show_token_btn.clicked.connect(self.toggle_token_visibility)
|
|
|
config_layout.addWidget(self.show_token_btn, 0, 2)
|
|
|
|
|
|
- # OCR地址输入
|
|
|
config_layout.addWidget(QLabel("OCR地址:"), 1, 0)
|
|
|
self.ocr_input = QLineEdit()
|
|
|
self.ocr_input.setText("http://111.229.45.38:19100/ocr/url")
|
|
|
config_layout.addWidget(self.ocr_input, 1, 1, 1, 2)
|
|
|
|
|
|
- # 操作按钮
|
|
|
+ # 并发数设置
|
|
|
+ config_layout.addWidget(QLabel("并发数:"), 2, 0)
|
|
|
+ self.thread_count_combo = QComboBox()
|
|
|
+ self.thread_count_combo.addItems(["3", "5", "8", "10", "15"])
|
|
|
+ self.thread_count_combo.setCurrentText("5")
|
|
|
+ config_layout.addWidget(self.thread_count_combo, 2, 1)
|
|
|
+
|
|
|
btn_layout = QHBoxLayout()
|
|
|
self.search_btn = QPushButton("🔍 搜索订单")
|
|
|
self.search_btn.clicked.connect(self.search_orders)
|
|
|
@@ -356,16 +486,15 @@ class MainWindow(QMainWindow):
|
|
|
self.refresh_btn.setFixedHeight(35)
|
|
|
btn_layout.addWidget(self.refresh_btn)
|
|
|
|
|
|
- # 一键获取所有充电次数按钮
|
|
|
- self.batch_btn = QPushButton("📊 一键获取所有充电次数")
|
|
|
+ self.batch_btn = QPushButton("⚡ 并行查询充电次数")
|
|
|
self.batch_btn.setObjectName("batch_btn")
|
|
|
- self.batch_btn.clicked.connect(self.batch_check_charge_counts)
|
|
|
+ self.batch_btn.clicked.connect(self.batch_check_charge_counts_parallel)
|
|
|
self.batch_btn.setFixedHeight(35)
|
|
|
self.batch_btn.setEnabled(False)
|
|
|
btn_layout.addWidget(self.batch_btn)
|
|
|
|
|
|
btn_layout.addStretch()
|
|
|
- config_layout.addLayout(btn_layout, 2, 0, 1, 3)
|
|
|
+ config_layout.addLayout(btn_layout, 3, 0, 1, 3)
|
|
|
|
|
|
config_group.setLayout(config_layout)
|
|
|
main_layout.addWidget(config_group)
|
|
|
@@ -374,12 +503,10 @@ class MainWindow(QMainWindow):
|
|
|
list_group = QGroupBox("订单列表")
|
|
|
list_layout = QVBoxLayout()
|
|
|
|
|
|
- # 进度条
|
|
|
self.progress_bar = QProgressBar()
|
|
|
self.progress_bar.setVisible(False)
|
|
|
list_layout.addWidget(self.progress_bar)
|
|
|
|
|
|
- # 表格
|
|
|
self.table = QTableWidget()
|
|
|
self.table.setColumnCount(7)
|
|
|
self.table.setHorizontalHeaderLabels(["序号", "订单号", "商品信息", "IMEI", "预估价格", "门店", "充电次数"])
|
|
|
@@ -401,14 +528,12 @@ class MainWindow(QMainWindow):
|
|
|
log_group = QGroupBox("日志")
|
|
|
log_layout = QVBoxLayout()
|
|
|
|
|
|
- # 日志文本框
|
|
|
self.log_text = QTextEdit()
|
|
|
self.log_text.setObjectName("log_text")
|
|
|
self.log_text.setReadOnly(True)
|
|
|
self.log_text.setMaximumHeight(200)
|
|
|
log_layout.addWidget(self.log_text)
|
|
|
|
|
|
- # 日志控制按钮
|
|
|
log_btn_layout = QHBoxLayout()
|
|
|
self.clear_log_btn = QPushButton("清空日志")
|
|
|
self.clear_log_btn.clicked.connect(self.clear_log)
|
|
|
@@ -420,13 +545,11 @@ class MainWindow(QMainWindow):
|
|
|
log_group.setLayout(log_layout)
|
|
|
main_layout.addWidget(log_group)
|
|
|
|
|
|
- # --- 底部状态栏 ---
|
|
|
self.status_bar = QStatusBar()
|
|
|
self.setStatusBar(self.status_bar)
|
|
|
self.status_bar.showMessage("就绪")
|
|
|
|
|
|
def toggle_token_visibility(self):
|
|
|
- """切换Token显示/隐藏"""
|
|
|
if self.token_input.echoMode() == QLineEdit.Password:
|
|
|
self.token_input.setEchoMode(QLineEdit.Normal)
|
|
|
self.show_token_btn.setText("隐藏")
|
|
|
@@ -435,7 +558,6 @@ class MainWindow(QMainWindow):
|
|
|
self.show_token_btn.setText("显示")
|
|
|
|
|
|
def log_message(self, message, level="INFO"):
|
|
|
- """添加日志消息"""
|
|
|
timestamp = QDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
|
|
|
color_map = {
|
|
|
"INFO": "#d4d4d4",
|
|
|
@@ -447,16 +569,13 @@ class MainWindow(QMainWindow):
|
|
|
color = color_map.get(level, "#d4d4d4")
|
|
|
html = f'<span style="color: #888888;">[{timestamp}]</span> <span style="color: {color};">[{level}]</span> {message}<br>'
|
|
|
self.log_text.append(html)
|
|
|
- # 滚动到底部
|
|
|
scrollbar = self.log_text.verticalScrollBar()
|
|
|
scrollbar.setValue(scrollbar.maximum())
|
|
|
|
|
|
def clear_log(self):
|
|
|
- """清空日志"""
|
|
|
self.log_text.clear()
|
|
|
|
|
|
def search_orders(self):
|
|
|
- """搜索订单"""
|
|
|
access_token = self.token_input.text().strip()
|
|
|
ocr_url = self.ocr_input.text().strip()
|
|
|
|
|
|
@@ -470,10 +589,7 @@ class MainWindow(QMainWindow):
|
|
|
|
|
|
self.log_message("开始搜索订单...", "INFO")
|
|
|
|
|
|
- # 初始化订单管理器
|
|
|
self.order_manager = OrderManager(access_token, ocr_url)
|
|
|
-
|
|
|
- # 清空缓存
|
|
|
self.order_details_cache = {}
|
|
|
self.charge_counts = {}
|
|
|
self.batch_btn.setEnabled(False)
|
|
|
@@ -481,21 +597,18 @@ class MainWindow(QMainWindow):
|
|
|
self.status_bar.showMessage("正在获取订单列表...")
|
|
|
self.search_btn.setEnabled(False)
|
|
|
|
|
|
- # 在后台线程执行
|
|
|
self.worker = OrderListWorker(self.order_manager)
|
|
|
self.worker.finished.connect(self.on_orders_loaded)
|
|
|
self.worker.error.connect(self.on_error)
|
|
|
self.worker.start()
|
|
|
|
|
|
def refresh_orders(self):
|
|
|
- """刷新订单列表"""
|
|
|
if self.order_manager:
|
|
|
self.search_orders()
|
|
|
else:
|
|
|
QMessageBox.warning(self, "提示", "请先搜索订单")
|
|
|
|
|
|
def on_orders_loaded(self, order_list):
|
|
|
- """订单列表加载完成"""
|
|
|
self.order_list = order_list
|
|
|
self.display_orders()
|
|
|
self.search_btn.setEnabled(True)
|
|
|
@@ -504,7 +617,6 @@ class MainWindow(QMainWindow):
|
|
|
self.log_message(f"成功加载 {len(order_list)} 条订单", "SUCCESS")
|
|
|
|
|
|
def on_error(self, error_msg):
|
|
|
- """错误处理"""
|
|
|
self.search_btn.setEnabled(True)
|
|
|
self.batch_btn.setEnabled(False)
|
|
|
self.status_bar.showMessage("加载失败")
|
|
|
@@ -512,35 +624,27 @@ class MainWindow(QMainWindow):
|
|
|
QMessageBox.critical(self, "错误", f"获取订单列表失败:\n{error_msg}")
|
|
|
|
|
|
def display_orders(self):
|
|
|
- """显示订单列表"""
|
|
|
self.table.setRowCount(len(self.order_list))
|
|
|
|
|
|
for i, order in enumerate(self.order_list):
|
|
|
- # 序号
|
|
|
self.table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
|
|
|
|
|
|
- # 订单号
|
|
|
order_no = order.get('orderNo', '')
|
|
|
self.table.setItem(i, 1, QTableWidgetItem(order_no))
|
|
|
|
|
|
- # 商品信息
|
|
|
brand_info = order.get('brandInfo', '')
|
|
|
self.table.setItem(i, 2, QTableWidgetItem(brand_info))
|
|
|
|
|
|
- # IMEI
|
|
|
imei = order.get('imei', '')
|
|
|
self.table.setItem(i, 3, QTableWidgetItem(imei))
|
|
|
|
|
|
- # 预估价格
|
|
|
price = order.get('price', '')
|
|
|
price_str = str(price) if price else '待定'
|
|
|
self.table.setItem(i, 4, QTableWidgetItem(price_str))
|
|
|
|
|
|
- # 门店
|
|
|
title = order.get('title', '')
|
|
|
self.table.setItem(i, 5, QTableWidgetItem(title))
|
|
|
|
|
|
- # 充电次数(初始为空,显示"查询"按钮)
|
|
|
charge_widget = QWidget()
|
|
|
charge_layout = QHBoxLayout(charge_widget)
|
|
|
charge_layout.setContentsMargins(5, 2, 5, 2)
|
|
|
@@ -560,7 +664,6 @@ class MainWindow(QMainWindow):
|
|
|
self.table.setCellWidget(i, 6, charge_widget)
|
|
|
|
|
|
def check_charge_count(self, index):
|
|
|
- """查询充电次数"""
|
|
|
if not self.order_manager:
|
|
|
return
|
|
|
|
|
|
@@ -572,23 +675,21 @@ class MainWindow(QMainWindow):
|
|
|
return
|
|
|
|
|
|
self.log_message(f"开始查询订单 {order_no} 的充电次数", "INFO")
|
|
|
-
|
|
|
self.status_bar.showMessage(f"正在查询订单 {order_no} 的充电次数...")
|
|
|
|
|
|
- # 禁用按钮
|
|
|
btn = self.findChild(QPushButton, f"check_btn_{index}")
|
|
|
if btn:
|
|
|
btn.setEnabled(False)
|
|
|
btn.setText("查询中")
|
|
|
|
|
|
- # 在后台线程执行
|
|
|
self.charge_worker = ChargeQueryWorker(self.order_manager, order_no, index)
|
|
|
self.charge_worker.finished.connect(self.on_charge_queried)
|
|
|
self.charge_worker.error.connect(self.on_charge_error)
|
|
|
self.charge_worker.start()
|
|
|
|
|
|
- def batch_check_charge_counts(self):
|
|
|
- """一键获取所有订单的充电次数"""
|
|
|
+ # ===== 并行批量查询 =====
|
|
|
+ def batch_check_charge_counts_parallel(self):
|
|
|
+ """并行批量查询充电次数"""
|
|
|
if not self.order_list:
|
|
|
QMessageBox.warning(self, "提示", "请先搜索订单")
|
|
|
return
|
|
|
@@ -608,12 +709,20 @@ class MainWindow(QMainWindow):
|
|
|
QMessageBox.information(self, "提示", "所有订单已查询完成")
|
|
|
return
|
|
|
|
|
|
+ # 关闭旧的线程池
|
|
|
+ if hasattr(self, 'thread_pool'):
|
|
|
+ self.thread_pool.shutdown(wait=False)
|
|
|
+
|
|
|
+ # 获取并发数
|
|
|
+ max_workers = int(self.thread_count_combo.currentText())
|
|
|
+ self.thread_pool = ThreadPoolExecutor(max_workers=max_workers)
|
|
|
+
|
|
|
self.is_batch_processing = True
|
|
|
- self.batch_queue = pending_indices
|
|
|
- self.current_batch_index = 0
|
|
|
+ self.completed_count = 0
|
|
|
+ self.total_tasks = len(pending_indices)
|
|
|
+ self.future_tasks = {}
|
|
|
|
|
|
- # ===== 添加以下代码 =====
|
|
|
- # 禁用相关按钮
|
|
|
+ # 禁用按钮
|
|
|
self.batch_btn.setEnabled(False)
|
|
|
self.search_btn.setEnabled(False)
|
|
|
self.refresh_btn.setEnabled(False)
|
|
|
@@ -623,110 +732,266 @@ class MainWindow(QMainWindow):
|
|
|
self.progress_bar.setMaximum(len(pending_indices))
|
|
|
self.progress_bar.setValue(0)
|
|
|
|
|
|
- self.log_message(f"开始批量查询 {len(pending_indices)} 个订单的充电次数", "PROGRESS")
|
|
|
+ self.log_message(f"开始并行查询 {len(pending_indices)} 个订单 (并发数: {max_workers})", "PROGRESS")
|
|
|
+
|
|
|
+ # 提交所有任务
|
|
|
+ token = self.token_input.text().strip()
|
|
|
+ ocr_url = self.ocr_input.text().strip()
|
|
|
+
|
|
|
+ for idx in pending_indices:
|
|
|
+ order_no = self.order_list[idx].get('orderNo')
|
|
|
+
|
|
|
+ # 提交任务到线程池
|
|
|
+ future = self.thread_pool.submit(
|
|
|
+ self.process_single_order_parallel,
|
|
|
+ token,
|
|
|
+ ocr_url,
|
|
|
+ order_no,
|
|
|
+ idx
|
|
|
+ )
|
|
|
+ self.future_tasks[future] = idx
|
|
|
+
|
|
|
+ # 更新UI按钮状态
|
|
|
+ btn = self.findChild(QPushButton, f"check_btn_{idx}")
|
|
|
+ if btn:
|
|
|
+ btn.setEnabled(False)
|
|
|
+ btn.setText("查询中")
|
|
|
+
|
|
|
+ # 启动结果收集线程
|
|
|
+ collector = threading.Thread(target=self.collect_parallel_results)
|
|
|
+ collector.daemon = True
|
|
|
+ collector.start()
|
|
|
+
|
|
|
+ def process_single_order_parallel(self, token, ocr_url, order_no, index):
|
|
|
+ """在独立线程中处理单个订单"""
|
|
|
+ try:
|
|
|
+ # 每个线程创建独立的OrderManager实例
|
|
|
+ order_manager = OrderManager(token, ocr_url)
|
|
|
+
|
|
|
+ # 获取订单详情
|
|
|
+ order_detail, error = order_manager.get_order_detail(order_no)
|
|
|
+ if error:
|
|
|
+ return index, None, error, None
|
|
|
+
|
|
|
+ # 查找验机图片
|
|
|
+ report_img = order_manager.find_best_image(order_detail)
|
|
|
+ if not report_img:
|
|
|
+ return index, None, "未找到验机图片", None
|
|
|
+
|
|
|
+ # OCR识别
|
|
|
+ ocr_result = order_manager.ocr_by_url(report_img)
|
|
|
+ if not ocr_result:
|
|
|
+ return index, None, "OCR识别失败", None
|
|
|
+
|
|
|
+ # 提取充电次数
|
|
|
+ ocr_text = order_manager.extract_text_from_ocr_result(ocr_result)
|
|
|
+ charge_count = order_manager.extract_charge_count(ocr_text)
|
|
|
+
|
|
|
+ return index, charge_count, None, ocr_result
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ return index, None, str(e), None
|
|
|
+
|
|
|
+ def collect_parallel_results(self):
|
|
|
+ """收集并行处理的结果"""
|
|
|
+ for future in as_completed(self.future_tasks):
|
|
|
+ index = self.future_tasks[future]
|
|
|
+ try:
|
|
|
+ idx, charge_count, error, ocr_result = future.result(timeout=60)
|
|
|
+
|
|
|
+ # 更新UI(在主线程)
|
|
|
+ QMetaObject.invokeMethod(self, "update_parallel_result",
|
|
|
+ Qt.QueuedConnection,
|
|
|
+ Q_ARG(int, idx),
|
|
|
+ Q_ARG(object, charge_count),
|
|
|
+ Q_ARG(str, error or ""),
|
|
|
+ Q_ARG(object, ocr_result)
|
|
|
+ )
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ QMetaObject.invokeMethod(self, "update_parallel_result",
|
|
|
+ Qt.QueuedConnection,
|
|
|
+ Q_ARG(int, index),
|
|
|
+ Q_ARG(object, None),
|
|
|
+ Q_ARG(str, str(e)),
|
|
|
+ Q_ARG(object, None)
|
|
|
+ )
|
|
|
+
|
|
|
+ self.completed_count += 1
|
|
|
+
|
|
|
+ # 更新进度(在主线程)
|
|
|
+ QMetaObject.invokeMethod(self, "update_batch_progress",
|
|
|
+ Qt.QueuedConnection,
|
|
|
+ Q_ARG(int, self.completed_count)
|
|
|
+ )
|
|
|
+
|
|
|
+ # 全部完成
|
|
|
+ QMetaObject.invokeMethod(self, "on_batch_complete_parallel",
|
|
|
+ Qt.QueuedConnection
|
|
|
+ )
|
|
|
+
|
|
|
+ @pyqtSlot(int, object, str, object)
|
|
|
+ def update_parallel_result(self, index, charge_count, error, ocr_result):
|
|
|
+ """更新单个结果(在主线程执行)"""
|
|
|
+ order_no = self.order_list[index].get('orderNo')
|
|
|
|
|
|
- # 开始处理第一个
|
|
|
+ # 缓存结果
|
|
|
+ if charge_count is not None:
|
|
|
+ self.charge_counts[order_no] = charge_count
|
|
|
+ self.update_charge_display(index, charge_count)
|
|
|
+ self.log_message(f"订单 {order_no} 充电次数: {charge_count}", "SUCCESS")
|
|
|
+ else:
|
|
|
+ self.charge_counts[order_no] = None
|
|
|
+ self.update_charge_display(index, None)
|
|
|
+ if error:
|
|
|
+ self.log_message(f"订单 {order_no} 查询失败: {error}", "ERROR")
|
|
|
+
|
|
|
+ # 恢复按钮
|
|
|
+ btn = self.findChild(QPushButton, f"check_btn_{index}")
|
|
|
+ if btn:
|
|
|
+ btn.setEnabled(True)
|
|
|
+ btn.setText("查询")
|
|
|
+
|
|
|
+ @pyqtSlot(int)
|
|
|
+ def update_batch_progress(self, completed):
|
|
|
+ """更新进度(在主线程执行)"""
|
|
|
+ self.progress_bar.setValue(completed)
|
|
|
+ self.status_bar.showMessage(f"并行处理中: {completed}/{self.total_tasks}")
|
|
|
+
|
|
|
+ @pyqtSlot()
|
|
|
+ def on_batch_complete_parallel(self):
|
|
|
+ """并行批量完成(在主线程执行)"""
|
|
|
+ self.is_batch_processing = False
|
|
|
+ self.future_tasks.clear()
|
|
|
+
|
|
|
+ # 恢复按钮
|
|
|
+ self.batch_btn.setEnabled(True)
|
|
|
+ self.search_btn.setEnabled(True)
|
|
|
+ self.refresh_btn.setEnabled(True)
|
|
|
+
|
|
|
+ # 隐藏进度条
|
|
|
+ self.progress_bar.setVisible(False)
|
|
|
+
|
|
|
+ # 统计结果
|
|
|
+ success_count = sum(1 for v in self.charge_counts.values() if v is not None)
|
|
|
+ total_count = len(self.charge_counts)
|
|
|
+
|
|
|
+ self.status_bar.showMessage(f"批量查询完成,成功 {success_count}/{total_count}")
|
|
|
+ self.log_message(f"批量查询完成,成功 {success_count}/{total_count}", "SUCCESS")
|
|
|
+
|
|
|
+ QMessageBox.information(self, "完成",
|
|
|
+ f"并行批量查询完成!\n\n成功找到充电次数: {success_count}/{total_count}\n总订单: {total_count}")
|
|
|
+
|
|
|
+ # ===== 原有的串行查询(保留作为备选) =====
|
|
|
+ def batch_check_charge_counts(self):
|
|
|
+ """串行批量查询(保留)"""
|
|
|
+ if not self.order_list:
|
|
|
+ QMessageBox.warning(self, "提示", "请先搜索订单")
|
|
|
+ return
|
|
|
+
|
|
|
+ if self.is_batch_processing:
|
|
|
+ return
|
|
|
+
|
|
|
+ pending_indices = []
|
|
|
+ for i, order in enumerate(self.order_list):
|
|
|
+ order_no = order.get('orderNo')
|
|
|
+ if order_no not in self.charge_counts:
|
|
|
+ pending_indices.append(i)
|
|
|
+
|
|
|
+ if not pending_indices:
|
|
|
+ self.log_message("所有订单已查询完成", "SUCCESS")
|
|
|
+ QMessageBox.information(self, "提示", "所有订单已查询完成")
|
|
|
+ return
|
|
|
+
|
|
|
+ self.is_batch_processing = True
|
|
|
+ self.batch_queue = pending_indices
|
|
|
+ self.current_batch_index = 0
|
|
|
+
|
|
|
+ self.batch_btn.setEnabled(False)
|
|
|
+ self.search_btn.setEnabled(False)
|
|
|
+ self.refresh_btn.setEnabled(False)
|
|
|
+
|
|
|
+ self.progress_bar.setVisible(True)
|
|
|
+ self.progress_bar.setMaximum(len(pending_indices))
|
|
|
+ self.progress_bar.setValue(0)
|
|
|
+
|
|
|
+ self.log_message(f"开始串行查询 {len(pending_indices)} 个订单", "PROGRESS")
|
|
|
self.process_next_batch()
|
|
|
|
|
|
def process_next_batch(self):
|
|
|
- """处理下一个批量查询"""
|
|
|
if self.current_batch_index >= len(self.batch_queue):
|
|
|
- # 全部完成
|
|
|
self.on_batch_complete()
|
|
|
return
|
|
|
|
|
|
index = self.batch_queue[self.current_batch_index]
|
|
|
order_no = self.order_list[index].get('orderNo')
|
|
|
|
|
|
- # 更新进度
|
|
|
self.progress_bar.setValue(self.current_batch_index + 1)
|
|
|
- self.status_bar.showMessage(f"批量处理中: {self.current_batch_index + 1}/{len(self.batch_queue)} - 订单 {order_no}")
|
|
|
+ self.status_bar.showMessage(f"串行处理中: {self.current_batch_index + 1}/{len(self.batch_queue)}")
|
|
|
|
|
|
- # 禁用该行的查询按钮
|
|
|
btn = self.findChild(QPushButton, f"check_btn_{index}")
|
|
|
if btn:
|
|
|
btn.setEnabled(False)
|
|
|
btn.setText("查询中")
|
|
|
|
|
|
- # 创建worker处理
|
|
|
self.batch_worker = ChargeQueryWorker(self.order_manager, order_no, index)
|
|
|
self.batch_worker.finished.connect(self.on_batch_item_complete)
|
|
|
self.batch_worker.error.connect(self.on_batch_item_error)
|
|
|
self.batch_worker.start()
|
|
|
|
|
|
def on_batch_item_complete(self, index, charge_count, ocr_result, image_notices):
|
|
|
- """批量查询单个完成"""
|
|
|
order_no = self.order_list[index].get('orderNo')
|
|
|
|
|
|
- # 更新显示
|
|
|
self.charge_counts[order_no] = charge_count
|
|
|
self.update_charge_display(index, charge_count)
|
|
|
|
|
|
- # 恢复按钮
|
|
|
btn = self.findChild(QPushButton, f"check_btn_{index}")
|
|
|
if btn:
|
|
|
btn.setEnabled(True)
|
|
|
btn.setText("查询")
|
|
|
|
|
|
- # 日志
|
|
|
if charge_count is not None:
|
|
|
self.log_message(f"[{self.current_batch_index + 1}/{len(self.batch_queue)}] 订单 {order_no} 充电次数: {charge_count}", "SUCCESS")
|
|
|
else:
|
|
|
self.log_message(f"[{self.current_batch_index + 1}/{len(self.batch_queue)}] 订单 {order_no} 未找到充电次数", "WARNING")
|
|
|
- if ocr_result:
|
|
|
- ocr_text = self.order_manager.extract_text_from_ocr_result(ocr_result)
|
|
|
- if ocr_text:
|
|
|
- self.log_message(f" OCR文本: {ocr_text}", "INFO")
|
|
|
- if image_notices:
|
|
|
- self.log_message(f" 图片notice: {', '.join(image_notices[:5])}", "INFO")
|
|
|
|
|
|
- # 处理下一个
|
|
|
self.current_batch_index += 1
|
|
|
- QTimer.singleShot(500, self.process_next_batch) # 延迟500ms防止请求过快
|
|
|
+ QTimer.singleShot(300, self.process_next_batch)
|
|
|
|
|
|
def on_batch_item_error(self, index, error_msg):
|
|
|
- """批量查询单个错误"""
|
|
|
order_no = self.order_list[index].get('orderNo')
|
|
|
self.log_message(f"[{self.current_batch_index + 1}/{len(self.batch_queue)}] 订单 {order_no} 查询失败: {error_msg}", "ERROR")
|
|
|
|
|
|
- # 恢复按钮
|
|
|
btn = self.findChild(QPushButton, f"check_btn_{index}")
|
|
|
if btn:
|
|
|
btn.setEnabled(True)
|
|
|
btn.setText("查询")
|
|
|
|
|
|
- # 处理下一个
|
|
|
self.current_batch_index += 1
|
|
|
- QTimer.singleShot(500, self.process_next_batch)
|
|
|
+ QTimer.singleShot(300, self.process_next_batch)
|
|
|
|
|
|
def on_batch_complete(self):
|
|
|
- """批量查询完成"""
|
|
|
self.is_batch_processing = False
|
|
|
self.batch_queue = []
|
|
|
|
|
|
- # 恢复按钮
|
|
|
self.batch_btn.setEnabled(True)
|
|
|
self.search_btn.setEnabled(True)
|
|
|
self.refresh_btn.setEnabled(True)
|
|
|
|
|
|
- # 隐藏进度条
|
|
|
self.progress_bar.setVisible(False)
|
|
|
|
|
|
- self.status_bar.showMessage(f"批量查询完成,共处理 {len(self.charge_counts)} 个订单")
|
|
|
- self.log_message("批量查询完成", "SUCCESS")
|
|
|
-
|
|
|
- # 统计结果
|
|
|
success_count = sum(1 for v in self.charge_counts.values() if v is not None)
|
|
|
total_count = len(self.charge_counts)
|
|
|
- self.log_message(f"统计: 成功 {success_count}/{total_count} 个订单找到充电次数", "INFO")
|
|
|
|
|
|
- QMessageBox.information(self, "完成", f"批量查询完成!\n\n成功找到充电次数: {success_count}/{total_count}")
|
|
|
+ self.status_bar.showMessage(f"串行查询完成,成功 {success_count}/{total_count}")
|
|
|
+ self.log_message(f"串行查询完成,成功 {success_count}/{total_count}", "SUCCESS")
|
|
|
+
|
|
|
+ QMessageBox.information(self, "完成", f"串行查询完成!\n\n成功找到充电次数: {success_count}/{total_count}")
|
|
|
|
|
|
+ # ===== 通用方法 =====
|
|
|
def on_charge_queried(self, index, charge_count, ocr_result, image_notices):
|
|
|
- """充电次数查询完成"""
|
|
|
order_no = self.order_list[index].get('orderNo')
|
|
|
|
|
|
- # 如果没有找到充电次数,输出详细信息
|
|
|
if charge_count is None:
|
|
|
if ocr_result:
|
|
|
self.log_message(f"订单 {order_no} 未找到充电次数,OCR识别结果:", "WARNING")
|
|
|
@@ -735,22 +1000,6 @@ class MainWindow(QMainWindow):
|
|
|
self.log_message(f"OCR文本: {ocr_text}", "INFO")
|
|
|
else:
|
|
|
self.log_message(f"OCR文本为空", "WARNING")
|
|
|
- else:
|
|
|
- # 重新获取订单详情来输出图片信息
|
|
|
- order_detail, _ = self.order_manager.get_order_detail(order_no)
|
|
|
- if order_detail:
|
|
|
- self.log_message(f"订单 {order_no} 所有图片信息:", "INFO")
|
|
|
- # 递归获取所有图片
|
|
|
- def print_images(obj):
|
|
|
- if isinstance(obj, dict):
|
|
|
- if 'notice' in obj and 'originalImage' in obj:
|
|
|
- self.log_message(f" {obj['notice']}: {obj['originalImage']}", "INFO")
|
|
|
- for value in obj.values():
|
|
|
- print_images(value)
|
|
|
- elif isinstance(obj, list):
|
|
|
- for item in obj:
|
|
|
- print_images(item)
|
|
|
- print_images(order_detail)
|
|
|
|
|
|
if not image_notices:
|
|
|
self.log_message(f"订单 {order_no} 未找到验机图片", "ERROR")
|
|
|
@@ -771,7 +1020,6 @@ class MainWindow(QMainWindow):
|
|
|
self.status_bar.showMessage(f"订单 {order_no} 未找到充电次数信息")
|
|
|
|
|
|
def on_charge_error(self, index, error_msg):
|
|
|
- """充电次数查询错误"""
|
|
|
order_no = self.order_list[index].get('orderNo')
|
|
|
self.log_message(f"订单 {order_no} 查询失败: {error_msg}", "ERROR")
|
|
|
|
|
|
@@ -784,7 +1032,6 @@ class MainWindow(QMainWindow):
|
|
|
QMessageBox.warning(self, "查询失败", f"获取订单详情失败:\n{error_msg}")
|
|
|
|
|
|
def update_charge_display(self, index, charge_count):
|
|
|
- """更新充电次数显示"""
|
|
|
widget = self.table.cellWidget(index, 6)
|
|
|
if widget:
|
|
|
label = widget.findChild(QLabel)
|
|
|
@@ -795,90 +1042,18 @@ class MainWindow(QMainWindow):
|
|
|
else:
|
|
|
label.setText("未找到")
|
|
|
label.setStyleSheet("color: #dc3545;")
|
|
|
-
|
|
|
-# --- 后台工作线程 ---
|
|
|
-class OrderListWorker(QThread):
|
|
|
- finished = pyqtSignal(list)
|
|
|
- error = pyqtSignal(str)
|
|
|
-
|
|
|
- def __init__(self, order_manager):
|
|
|
- super().__init__()
|
|
|
- self.order_manager = order_manager
|
|
|
-
|
|
|
- def run(self):
|
|
|
- try:
|
|
|
- order_list, error = self.order_manager.get_pending_order_list(100)
|
|
|
- if error:
|
|
|
- self.error.emit(error)
|
|
|
- else:
|
|
|
- self.finished.emit(order_list)
|
|
|
- except Exception as e:
|
|
|
- self.error.emit(str(e))
|
|
|
-
|
|
|
-class ChargeQueryWorker(QThread):
|
|
|
- finished = pyqtSignal(int, object, object, list) # index, charge_count, ocr_result, image_notices
|
|
|
- error = pyqtSignal(int, str)
|
|
|
-
|
|
|
- def __init__(self, order_manager, order_no, index):
|
|
|
- super().__init__()
|
|
|
- self.order_manager = order_manager
|
|
|
- self.order_no = order_no
|
|
|
- self.index = index
|
|
|
-
|
|
|
- def run(self):
|
|
|
- try:
|
|
|
- # 获取订单详情
|
|
|
- order_detail, error = self.order_manager.get_order_detail(self.order_no)
|
|
|
- if error:
|
|
|
- self.error.emit(self.index, error)
|
|
|
- return
|
|
|
-
|
|
|
- # 查找所有图片的notice
|
|
|
- image_notices = self.order_manager.get_all_image_notices(order_detail)
|
|
|
-
|
|
|
- # 查找验机图片
|
|
|
- report_img = self.order_manager.find_original_image_by_notice(order_detail, "苹果沙漏验机报告")
|
|
|
-
|
|
|
- if report_img is None:
|
|
|
- report_img = self.order_manager.find_original_image_by_notice(order_detail, "沙漏图")
|
|
|
- if report_img is None:
|
|
|
- report_img = self.order_manager.find_original_image_by_notice(order_detail, "苹果验机软件")
|
|
|
- if report_img is None:
|
|
|
- report_img = self.order_manager.find_original_image_by_notice(order_detail, "苹果沙漏验机基本信息")
|
|
|
- if report_img is None:
|
|
|
- report_img = self.order_manager.find_original_image_by_notice(order_detail, "验机照片")
|
|
|
- if report_img is None:
|
|
|
- report_img = self.order_manager.find_original_image_by_notice(order_detail, "验机报告")
|
|
|
- if not report_img:
|
|
|
- # 没有找到图片,返回notice列表
|
|
|
- self.finished.emit(self.index, None, None, image_notices)
|
|
|
- return
|
|
|
- else:
|
|
|
- print("找到图片:" + report_img)
|
|
|
- # OCR识别(使用URL方式)
|
|
|
- ocr_result = self.order_manager.ocr_by_url(report_img)
|
|
|
- if not ocr_result:
|
|
|
- # OCR失败,返回notice列表
|
|
|
- self.finished.emit(self.index, None, None, image_notices)
|
|
|
- return
|
|
|
-
|
|
|
- # 提取充电次数
|
|
|
- ocr_text = self.order_manager.extract_text_from_ocr_result(ocr_result)
|
|
|
- charge_count = self.order_manager.extract_charge_count(ocr_text)
|
|
|
-
|
|
|
- self.finished.emit(self.index, charge_count, ocr_result, image_notices)
|
|
|
-
|
|
|
- except Exception as e:
|
|
|
- self.error.emit(self.index, str(e))
|
|
|
+
|
|
|
+ def closeEvent(self, event):
|
|
|
+ """关闭窗口时清理线程池"""
|
|
|
+ if hasattr(self, 'thread_pool'):
|
|
|
+ self.thread_pool.shutdown(wait=False)
|
|
|
+ event.accept()
|
|
|
|
|
|
# --- 主程序入口 ---
|
|
|
def main():
|
|
|
app = QApplication(sys.argv)
|
|
|
app.setStyle('Fusion')
|
|
|
|
|
|
- # 设置应用图标
|
|
|
- app.setWindowIcon(QIcon())
|
|
|
-
|
|
|
window = MainWindow()
|
|
|
window.show()
|
|
|
|