| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340 |
- import sys
- import hashlib
- import requests
- import json
- import time
- import threading
- import os
- import re
- import ssl
- import subprocess
- from datetime import datetime, timedelta
- from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
- QHBoxLayout, QLabel, QLineEdit, QPushButton,
- QTableWidget, QTableWidgetItem, QHeaderView,
- QCheckBox, QMessageBox, QGroupBox, QTextEdit,
- QGridLayout, QSplitter, QComboBox, QFileDialog)
- from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
- import websocket
- import threading
- import time
- # ==================== WebSocket 客户端 ====================
- class WebSocketClient(QThread):
- connected = pyqtSignal()
- disconnected = pyqtSignal()
- message_received = pyqtSignal(dict)
- error = pyqtSignal(str)
-
- def __init__(self, get_folder_callback=None):
- super().__init__()
- self.ws = None
- self.is_running = False
- self.user_id = None
- self.type_str = "自动扫码"
- self.is_admin = True
- self.device_id = None
- self.ws_lock = threading.Lock()
- self.get_folder_callback = get_folder_callback # 获取文件夹路径的回调函数
-
- def connect(self, device_id):
- """连接WebSocket服务器"""
- self.device_id = device_id
- self.is_running = True
- self.start()
-
- def run(self):
- """在子线程中运行WebSocket连接"""
- try:
- self._connect_ws()
- except Exception as e:
- self.error.emit(f"WebSocket连接错误: {str(e)}")
-
- def _connect_ws(self):
- """建立WebSocket连接并处理消息"""
- while self.is_running:
- try:
- # 创建WebSocketApp时添加ssl_options参数禁用证书验证
- self.ws = websocket.WebSocketApp(
- 'wss://ws.lamp.run',
- on_open=self._on_open,
- on_message=self._on_message,
- on_error=self._on_error,
- on_close=self._on_close
- )
-
- # 运行WebSocket,禁用SSL证书验证
- # 添加 sslopt={"cert_reqs": ssl.CERT_NONE} 参数
- import ssl
- self.ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
-
- # 如果连接断开,等待3秒重连
- if self.is_running:
- time.sleep(3)
-
- except Exception as e:
- self.error.emit(f"连接异常: {str(e)}")
- if self.is_running:
- time.sleep(3)
-
- def _on_open(self, ws):
- """连接打开时的回调"""
- self.connected.emit()
- # 发送登录消息
- login_msg = {
- "route": "login",
- "type": self.type_str,
- "admin": self.is_admin,
- "id": self.device_id
- }
- ws.send(json.dumps(login_msg))
-
- def _on_message(self, ws, message):
- """接收消息的回调"""
- try:
- data = json.loads(message)
-
- # 处理用户信息
- if data.get('type') == 'userInfo':
- self.user_id = data.get('value', {}).get('userID')
- if self.user_id:
- self.message_received.emit(data)
- # 处理图片接收
- elif data.get('type') == 'imgIn':
- self.handle_image_received(data)
- else:
- self.message_received.emit(data)
-
- except json.JSONDecodeError:
- pass
- except Exception as e:
- print(f"处理消息异常: {e}")
- def handle_image_received(self, data):
- """处理接收到的图片"""
- try:
- import base64
-
- # 获取图片的base64编码
- img_base64 = data.get('value', '')
- if not img_base64:
- self.error.emit("图片数据为空")
- return
-
- # 获取发送者ID
- sender_id = data.get('id', 'unknown')
-
- # 去除可能的base64前缀
- if ',' in img_base64:
- img_base64 = img_base64.split(',')[1]
-
- # 解码base64
- image_data = base64.b64decode(img_base64)
-
- # 固定保存到C盘根目录
- filepath = "C:/imgIn.jpeg"
-
- # 保存图片
- with open(filepath, 'wb') as f:
- f.write(image_data)
-
- print(f"✅ 图片已保存: {filepath}")
-
- # 通知主窗口图片已保存
- success_msg = {
- 'type': 'imgSaved',
- 'filepath': filepath,
- 'filename': "imgIn.jpeg",
- 'sender_id': sender_id
- }
- self.message_received.emit(success_msg)
-
- except Exception as e:
- error_msg = f"保存图片失败: {str(e)}"
- print(f"❌ {error_msg}")
- self.error.emit(error_msg)
- def get_folder_path(self):
- """获取保存文件夹路径"""
- if self.get_folder_callback:
- return self.get_folder_callback()
- return ""
-
- def _on_error(self, ws, error):
- """错误回调"""
- self.error.emit(f"WebSocket错误: {str(error)}")
-
- def _on_close(self, ws, close_status_code, close_msg):
- """连接关闭回调"""
- self.disconnected.emit()
-
- def send(self, route, value="", send_to=None):
- """发送消息,send_to 使用 userID"""
- with self.ws_lock:
- if not self.ws:
- return False
-
- try:
- # 检查连接状态
- if self.ws.sock and self.ws.sock.connected:
- msg = {
- "route": route,
- "type": self.type_str,
- "userID": self.user_id,
- "id": send_to, # send_to 是 userID
- "value": value
- }
- self.ws.send(json.dumps(msg))
- return True
- else:
- return False
- except Exception as e:
- print(f"发送消息错误: {e}")
- return False
-
- def get_list(self):
- """获取在线客户端列表"""
- return self.send("getList")
-
- def stop(self):
- """停止连接"""
- self.is_running = False
- if self.ws:
- try:
- self.ws.close()
- except:
- pass
- # ==================== 登录线程 ====================
- class LoginThread(QThread):
- finished = pyqtSignal(dict)
- error = pyqtSignal(str)
-
- def __init__(self, username, password):
- super().__init__()
- self.username = username
- self.password = password
-
- def run(self):
- try:
- md5_password = hashlib.md5(self.password.encode('utf-8')).hexdigest()
- url = f"http://119.91.58.175:9999/gps-web/api/login.jsp?userId={self.username}&password={md5_password}&loginType=user&onlyValidate=false&plateColor=&loginWay=interface&loginLang=zh_CN"
- response = requests.get(url, timeout=10)
- response.encoding = 'utf-8'
- if response.status_code == 200:
- data = response.json()
- if data.get('rspCode') == '1':
- self.finished.emit(data)
- else:
- self.error.emit(f"登录失败: {data.get('msg', '未知错误')}")
- else:
- self.error.emit(f"请求失败: HTTP {response.status_code}")
- except Exception as e:
- self.error.emit(f"登录异常: {str(e)}")
- # ==================== 查询车辆线程 ====================
- class QueryThread(QThread):
- finished = pyqtSignal(dict)
- error = pyqtSignal(str)
-
- def __init__(self, session_id):
- super().__init__()
- self.session_id = session_id
-
- def run(self):
- try:
- url = f"http://119.91.58.175:9999/gps-web/api/get_gps_r.jsp?sessionId={self.session_id}&teamId=&carIds=&simple=false"
- response = requests.get(url, timeout=15)
- response.encoding = 'utf-8'
- if response.status_code == 200:
- data = response.json()
- if data.get('rspCode') == '1':
- self.finished.emit(data)
- else:
- self.error.emit(f"查询失败: {data.get('msg', '未知错误')}")
- else:
- self.error.emit(f"请求失败: HTTP {response.status_code}")
- except Exception as e:
- self.error.emit(f"查询异常: {str(e)}")
- # ==================== 查询区域线程 ====================
- class RegionQueryThread(QThread):
- finished = pyqtSignal(dict)
- error = pyqtSignal(str)
-
- def __init__(self, session_id):
- super().__init__()
- self.session_id = session_id
-
- def run(self):
- try:
- url = f"http://119.91.58.175:9999/gps-web/api/get_region_car.jsp?sessionId={self.session_id}®ionIds="
- response = requests.get(url, timeout=10)
- response.encoding = 'utf-8'
- if response.status_code == 200:
- data = response.json()
- if data.get('rspCode') == '1':
- self.finished.emit(data)
- else:
- self.error.emit(f"查询区域失败: {data.get('msg', '未知错误')}")
- else:
- self.error.emit(f"请求失败: HTTP {response.status_code}")
- except Exception as e:
- self.error.emit(f"查询异常: {str(e)}")
- # ==================== 主窗口 ====================
- class MainWindow(QMainWindow):
- def __init__(self):
- super().__init__()
- self.session_id = None
- self.username = None
- self.car_data = []
- self.car_checked_state = {} # 启用状态
- self.car_scan_state = {} # 扫码状态(新增)
- self.triggered_records = {}
- self.is_monitoring = False
- # 扫码任务相关
- self.scan_task_queue = [] # 待处理的车辆队列
- self.is_processing_scan = False # 是否正在处理扫码任务
- self.scan_timer = QTimer() # 定时器用于间隔执行
- self.scan_timer.timeout.connect(self.process_next_scan)
-
- # WebSocket相关
- self.ws_client = None
- self.online_clients = {} # userID -> 客户端信息
-
- # 文件夹路径
- self.folder_path = ""
-
- # 定时器
- self.refresh_timer = QTimer() # 10秒刷新车辆列表
- self.refresh_timer.timeout.connect(self.handle_query)
- self.region_timer = QTimer() # 10秒检测区域
- self.region_timer.timeout.connect(self.check_regions)
-
- # 区域相关
- self.regions = []
- self.region_cars = {}
-
- self.initUI()
- self.load_config()
-
- def initUI(self):
- self.setWindowTitle('车辆定位监控系统')
- self.setGeometry(100, 100, 1500, 850)
-
- central_widget = QWidget()
- self.setCentralWidget(central_widget)
- main_layout = QVBoxLayout(central_widget)
-
- # ===== 登录区域 =====
- login_group = QGroupBox("登录")
- login_layout = QHBoxLayout()
- login_layout.addWidget(QLabel("账号:"))
- self.username_input = QLineEdit()
- self.username_input.setText("深金川")
- login_layout.addWidget(self.username_input)
- login_layout.addWidget(QLabel("密码:"))
- self.password_input = QLineEdit()
- self.password_input.setEchoMode(QLineEdit.Password)
- self.password_input.setText("123456")
- login_layout.addWidget(self.password_input)
- self.login_btn = QPushButton("登录")
- self.login_btn.clicked.connect(self.handle_login)
- login_layout.addWidget(self.login_btn)
- self.status_label = QLabel("未登录")
- login_layout.addWidget(self.status_label)
- login_layout.addStretch()
- login_group.setLayout(login_layout)
- main_layout.addWidget(login_group)
-
- # ===== WebSocket控制 =====
- ws_group = QGroupBox("WebSocket控制")
- ws_layout = QHBoxLayout()
-
- self.ws_status_label = QLabel("未连接")
- ws_layout.addWidget(self.ws_status_label)
-
- self.refresh_clients_btn = QPushButton("刷新客户端列表")
- self.refresh_clients_btn.clicked.connect(self.refresh_client_list)
- self.refresh_clients_btn.setEnabled(False)
- ws_layout.addWidget(self.refresh_clients_btn)
-
- ws_layout.addWidget(QLabel("装车客户端:"))
- self.send_client_combo = QComboBox()
- self.send_client_combo.setMinimumWidth(120)
- ws_layout.addWidget(self.send_client_combo)
-
- ws_layout.addWidget(QLabel("卸车客户端:"))
- self.receive_client_combo = QComboBox()
- self.receive_client_combo.setMinimumWidth(120)
- ws_layout.addWidget(self.receive_client_combo)
-
- # 测试按钮
- self.test_send_btn = QPushButton("测试发")
- self.test_send_btn.clicked.connect(self.test_send)
- self.test_send_btn.setEnabled(False)
- ws_layout.addWidget(self.test_send_btn)
-
- self.test_receive_btn = QPushButton("测试收")
- self.test_receive_btn.clicked.connect(self.test_receive)
- self.test_receive_btn.setEnabled(False)
- ws_layout.addWidget(self.test_receive_btn)
-
- ws_layout.addStretch()
- ws_group.setLayout(ws_layout)
- main_layout.addWidget(ws_group)
-
- # ===== 文件夹选择 =====
- folder_group = QGroupBox("文件路径设置")
- folder_layout = QHBoxLayout()
-
- folder_layout.addWidget(QLabel("选择文件夹:"))
- self.folder_path_input = QLineEdit()
- self.folder_path_input.setPlaceholderText("请选择文件夹路径")
- self.folder_path_input.setMinimumWidth(400)
- folder_layout.addWidget(self.folder_path_input)
-
- self.browse_folder_btn = QPushButton("浏览文件夹")
- self.browse_folder_btn.clicked.connect(self.browse_folder)
- folder_layout.addWidget(self.browse_folder_btn)
-
- self.clear_folder_btn = QPushButton("清空")
- self.clear_folder_btn.clicked.connect(self.clear_folder)
- folder_layout.addWidget(self.clear_folder_btn)
-
- folder_layout.addStretch()
- folder_group.setLayout(folder_layout)
- main_layout.addWidget(folder_group)
-
- # ===== 区域监控设置 =====
- region_group = QGroupBox("区域监控设置")
- region_layout = QGridLayout()
-
- region_layout.addWidget(QLabel("装车区域:"), 0, 0)
- self.load_region_combo = QComboBox()
- self.load_region_combo.setMinimumWidth(150)
- region_layout.addWidget(self.load_region_combo, 0, 1)
-
- region_layout.addWidget(QLabel("卸车区域:"), 0, 2)
- self.unload_region_combo = QComboBox()
- self.unload_region_combo.setMinimumWidth(150)
- region_layout.addWidget(self.unload_region_combo, 0, 3)
-
- self.refresh_region_btn = QPushButton("刷新区域")
- self.refresh_region_btn.clicked.connect(self.load_regions)
- self.refresh_region_btn.setEnabled(False)
- region_layout.addWidget(self.refresh_region_btn, 0, 4)
-
- self.start_btn = QPushButton("启动监控")
- self.start_btn.clicked.connect(self.start_monitoring)
- self.start_btn.setEnabled(False)
- region_layout.addWidget(self.start_btn, 0, 5)
-
- self.stop_btn = QPushButton("停止监控")
- self.stop_btn.clicked.connect(self.stop_monitoring)
- self.stop_btn.setEnabled(False)
- region_layout.addWidget(self.stop_btn, 0, 6)
-
- region_layout.addWidget(QLabel("检测间隔:10秒"), 0, 7)
-
- # 区域状态显示条
- self.region_status_label = QLabel("请选择区域")
- self.region_status_label.setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }")
- region_layout.addWidget(self.region_status_label, 1, 0, 1, 8)
-
- region_group.setLayout(region_layout)
- main_layout.addWidget(region_group)
-
- # ===== 车辆列表 =====
- splitter = QSplitter(Qt.Vertical)
-
- table_widget = QWidget()
- table_layout = QVBoxLayout(table_widget)
- table_layout.setContentsMargins(0, 0, 0, 0)
-
- self.table = QTableWidget()
- # 修改列数:增加"扫码"列和"操作"列
- self.table.setColumnCount(7)
- self.table.setHorizontalHeaderLabels(["启用", "扫码", "车队名", "车牌号", "状态", "定位地址", "操作"])
- self.table.horizontalHeader().setSectionResizeMode(5, QHeaderView.Stretch)
- self.table.setAlternatingRowColors(True)
- table_layout.addWidget(self.table)
-
- btn_layout = QHBoxLayout()
- self.select_all_btn = QPushButton("全选启用")
- self.select_all_btn.clicked.connect(self.select_all_enable)
- btn_layout.addWidget(self.select_all_btn)
- self.deselect_all_btn = QPushButton("取消全选启用")
- self.deselect_all_btn.clicked.connect(self.deselect_all_enable)
- btn_layout.addWidget(self.deselect_all_btn)
-
- self.select_all_scan_btn = QPushButton("全选扫码")
- self.select_all_scan_btn.clicked.connect(self.select_all_scan)
- btn_layout.addWidget(self.select_all_scan_btn)
- self.deselect_all_scan_btn = QPushButton("取消全选扫码")
- self.deselect_all_scan_btn.clicked.connect(self.deselect_all_scan)
- btn_layout.addWidget(self.deselect_all_scan_btn)
-
- btn_layout.addStretch()
- table_layout.addLayout(btn_layout)
-
- splitter.addWidget(table_widget)
-
- # ===== 日志 =====
- log_widget = QWidget()
- log_layout = QVBoxLayout(log_widget)
- log_layout.setContentsMargins(0, 0, 0, 0)
- log_layout.addWidget(QLabel("日志"))
- self.log_output = QTextEdit()
- self.log_output.setReadOnly(True)
- self.log_output.document().setMaximumBlockCount(500)
- log_layout.addWidget(self.log_output)
-
- splitter.addWidget(log_widget)
- splitter.setSizes([450, 300])
- main_layout.addWidget(splitter)
-
- self.statusBar().showMessage("就绪")
- self.triggered_records = {}
-
- def get_value_with_plate(self, car_plate):
- """生成带车牌的文件路径: 文件夹路径/车牌.jpg"""
- folder = self.folder_path_input.text().strip()
- if not folder:
- return ""
- # 确保路径格式正确
- folder = folder.rstrip('/\\')
- return f"{folder}/{car_plate}.jpg"
-
- def get_exe_path_with_plate(self, car_plate):
- """生成可执行文件路径: 文件夹路径/车牌号/运行程序.exe"""
- folder = self.folder_path_input.text().strip()
- if not folder:
- return ""
- # 确保路径格式正确
- folder = folder.rstrip('/\\')
- return f"{folder}/{car_plate}/运行程序.exe"
-
- # ===== 文件夹选择 =====
- def browse_folder(self):
- """浏览并选择文件夹"""
- folder_path = QFileDialog.getExistingDirectory(
- self,
- "选择文件夹",
- self.folder_path_input.text() if self.folder_path_input.text() else "",
- QFileDialog.ShowDirsOnly
- )
-
- if folder_path:
- self.folder_path = folder_path
- self.folder_path_input.setText(folder_path)
- self.add_log(f"📁 已选择文件夹: {folder_path}")
- self.save_config()
-
- def clear_folder(self):
- """清空文件夹路径"""
- self.folder_path = ""
- self.folder_path_input.clear()
- self.add_log("已清空文件夹路径")
- self.save_config()
-
- # ===== WebSocket连接 =====
- def connect_websocket(self, device_id):
- """连接WebSocket服务器"""
- if self.ws_client and self.ws_client.is_running:
- return
-
- self.ws_status_label.setText("连接中...")
- self.ws_status_label.setStyleSheet("QLabel { color: orange; }")
-
- # 创建WebSocket客户端,传入获取文件夹路径的回调
- self.ws_client = WebSocketClient(get_folder_callback=self.get_folder_path_for_ws)
- self.ws_client.connected.connect(self.on_ws_connected)
- self.ws_client.disconnected.connect(self.on_ws_disconnected)
- self.ws_client.message_received.connect(self.on_ws_message)
- self.ws_client.error.connect(self.on_ws_error)
- self.ws_client.connect(device_id)
- def get_folder_path_for_ws(self):
- """提供给WebSocket客户端的文件夹路径获取方法"""
- return self.folder_path_input.text().strip()
-
- def on_ws_connected(self):
- """WebSocket连接成功"""
- self.refresh_clients_btn.setEnabled(True)
- self.test_send_btn.setEnabled(True)
- self.test_receive_btn.setEnabled(True)
- self.ws_status_label.setText("已连接")
- self.ws_status_label.setStyleSheet("QLabel { color: green; }")
- self.add_log("✅ WebSocket连接成功")
-
- # 连接成功后获取客户端列表
- QTimer.singleShot(1000, self.refresh_client_list)
-
- def on_ws_disconnected(self):
- """WebSocket连接断开"""
- self.refresh_clients_btn.setEnabled(False)
- self.test_send_btn.setEnabled(False)
- self.test_receive_btn.setEnabled(False)
- self.ws_status_label.setText("已断开")
- self.ws_status_label.setStyleSheet("QLabel { color: red; }")
- self.add_log("⚠️ WebSocket连接断开")
-
- def on_ws_error(self, error_msg):
- """WebSocket错误"""
- self.add_log(f"❌ WebSocket错误: {error_msg}")
-
- def on_ws_message(self, data):
- """处理WebSocket消息"""
- msg_type = data.get('type')
- self.add_log(json.dumps(data, ensure_ascii=False))
- if msg_type == 'getList':
- # 客户端列表
- clients = data.get('value', [])
- self.on_client_list_received(clients)
- elif msg_type == 'userInfo':
- self.add_log(f"✅ 用户信息已获取: {data.get('value', {}).get('userID')}")
- elif msg_type == 'imgSaved':
- # 图片保存成功,触发扫码任务
- self.add_log(f"📸 图片已保存: {data.get('filepath', '')}")
- self.start_scan_tasks()
- elif msg_type == 'drive': # 收到"开车"命令
- # 外部WebSocket发来的开车命令
- car_plate = data.get('value', '')
- if car_plate:
- self.add_log(f"📤 收到外部WebSocket开车命令,车辆: {car_plate}")
- # 发送2001指令
- self.send_cmd_to_vehicle(car_plate, "2001")
- # 继续处理下一辆车
- self.process_next_scan()
- else:
- self.add_log(f"⚠️ 收到开车命令但车牌为空")
-
-
- def start_scan_tasks(self):
- """开始执行扫码任务"""
- # 如果正在处理,忽略新的请求
- if self.is_processing_scan:
- self.add_log("⏳ 扫码任务正在处理中,忽略本次请求")
- return
-
- # 获取所有勾选了扫码的车辆
- scan_vehicles = []
- for row in range(self.table.rowCount()):
- scan_checkbox = self.table.cellWidget(row, 1)
- if scan_checkbox and scan_checkbox.isChecked():
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
- if car_plate:
- scan_vehicles.append(car_plate)
-
- if not scan_vehicles:
- self.add_log("⚠️ 没有勾选扫码的车辆")
- return
-
- # 初始化队列
- self.scan_task_queue = scan_vehicles.copy()
- self.is_processing_scan = True
-
- self.add_log(f"🚀 开始扫码任务,共 {len(scan_vehicles)} 辆车")
- self.add_log(f"📋 车辆列表: {', '.join(scan_vehicles)}")
-
- # 立即处理第一个
- self.process_next_scan()
- def process_next_scan(self):
- """处理下一个扫码任务"""
- # 如果监控已停止,不再处理
- if not self.is_monitoring:
- self.add_log("⏹️ 监控已停止,取消扫码任务")
- self.is_processing_scan = False
- self.scan_task_queue = []
- self.current_scan_plate = None
- self.scan_timer.stop()
- return
-
- if not self.scan_task_queue:
- # 所有任务完成
- self.is_processing_scan = False
- self.add_log("✅ 所有扫码任务已完成")
- self.statusBar().showMessage("扫码任务全部完成")
- self.scan_timer.stop()
- self.current_scan_plate = None
- return
-
- # 取出第一个车辆
- car_plate = self.scan_task_queue.pop(0)
- self.current_scan_plate = car_plate
- self.add_log(f"🔄 正在处理扫码车辆: {car_plate} (剩余 {len(self.scan_task_queue)} 辆)")
-
- # 执行测试发车(标记为来自扫码任务)
- self.on_test_drive(car_plate, from_scan=True)
-
- # 启动定时器,1分钟后发送指令2001,然后处理下一辆
- # 使用 QTimer.singleShot 单次定时器,传入当前车牌
- self.add_log(f"⏳ 等待外部WebSocket通知发送2001指令到: {car_plate}")
- # QTimer.singleShot(60000, lambda: self.send_2001_and_next(car_plate))
- def send_2001_and_next(self, car_plate):
- """发送2001指令给指定车辆,然后继续处理下一个"""
- self.add_log(f"📤 发送指令 2001 到车辆: {car_plate}")
- self.send_cmd_to_vehicle(car_plate, "2001")
-
- # 发送完指令后,继续处理下一辆(立即执行,不再等待)
- self.process_next_scan()
- def check_scan_complete(self):
- """检查扫码任务是否完成"""
- if not self.scan_task_queue and self.is_processing_scan:
- self.is_processing_scan = False
- self.add_log("✅ 所有扫码任务已完成")
- self.statusBar().showMessage("扫码任务全部完成")
- def on_client_list_received(self, clients):
- """处理客户端列表,过滤掉 admin 为 true 的客户端"""
- self.online_clients = {}
- self.send_client_combo.clear()
- self.receive_client_combo.clear()
-
- # 添加默认选项
- self.send_client_combo.addItem("请选择装车客户端")
- self.receive_client_combo.addItem("请选择卸车客户端")
-
- for client in clients:
- # 过滤掉 admin 为 true 的客户端(这些是管理端,不是扫码客户端)
- if client.get('admin', False):
- continue
-
- client_id = client.get('id')
- user_id = client.get('userID')
- is_active = client.get('active', False)
-
- if client_id and user_id and is_active:
- # 使用 userID 作为键存储客户端信息
- self.online_clients[user_id] = client
- # 显示时显示 id,但存储 userID
- display_name = f"{client_id}"
- self.send_client_combo.addItem(display_name, user_id)
- self.receive_client_combo.addItem(display_name, user_id)
- self.add_log(f"发现客户端: {client_id} (userID: {user_id})")
-
- self.add_log(f"✅ 已加载 {len(self.online_clients)} 个在线扫码客户端")
-
- def refresh_client_list(self):
- """刷新客户端列表"""
- if not self.ws_client:
- return
-
- if not self.ws_client.is_running:
- self.add_log("❌ WebSocket未连接")
- return
-
- if self.ws_client.send("getList"):
- self.add_log("📡 正在获取客户端列表...")
- else:
- self.add_log("❌ 发送获取列表命令失败")
-
- def test_send(self):
- """测试发送(装车)"""
- if not self.ws_client or not self.ws_client.is_running:
- self.add_log("❌ WebSocket未连接")
- return
-
- user_id = self.send_client_combo.currentData()
- if not user_id:
- self.add_log("❌ 请选择装车客户端")
- return
-
- # 测试时使用一个示例车牌
- test_plate = "测试车牌"
- value = self.get_value_with_plate(test_plate)
-
- if not value:
- self.add_log("⚠️ 未选择文件夹,将发送空值")
-
- if self.ws_client.send("发货", value, user_id):
- self.add_log(f"✅ 测试发送(装车)指令已发送到 {user_id},value: {value}")
- else:
- self.add_log("❌ 测试发送失败")
-
- def test_receive(self):
- """测试接收(卸车)"""
- if not self.ws_client or not self.ws_client.is_running:
- self.add_log("❌ WebSocket未连接")
- return
-
- user_id = self.receive_client_combo.currentData()
- if not user_id:
- self.add_log("❌ 请选择卸车客户端")
- return
-
- # 测试时使用一个示例车牌
- test_plate = "测试车牌"
- value = self.get_value_with_plate(test_plate)
-
- if not value:
- self.add_log("⚠️ 未选择文件夹,将发送空值")
-
- if self.ws_client.send("收货", value, user_id):
- self.add_log(f"✅ 测试接收(卸车)指令已发送到 {user_id},value: {value}")
- else:
- self.add_log("❌ 测试接收失败")
-
- # ===== 登录 =====
- def handle_login(self):
- username = self.username_input.text().strip()
- password = self.password_input.text().strip()
- if not username or not password:
- QMessageBox.warning(self, "警告", "请输入账号和密码")
- return
- self.login_btn.setEnabled(False)
- self.status_label.setText("登录中...")
- self.login_thread = LoginThread(username, password)
- self.login_thread.finished.connect(self.on_login_success)
- self.login_thread.error.connect(self.on_login_error)
- self.login_thread.start()
-
- def on_login_success(self, data):
- self.session_id = data.get('sessionId')
- self.username = self.username_input.text().strip()
- if self.session_id:
- self.login_btn.setEnabled(True)
- self.status_label.setText(f"已登录: {self.username}")
- self.refresh_region_btn.setEnabled(True)
- self.start_btn.setEnabled(True)
- self.statusBar().showMessage("登录成功")
- self.load_regions()
- self.handle_query()
-
- # 自动连接WebSocket,使用用户名作为ID
- self.connect_websocket(self.username)
- self.save_config()
- else:
- self.on_login_error("登录响应中没有sessionId")
-
- def on_login_error(self, error_msg):
- self.login_btn.setEnabled(True)
- self.status_label.setText("登录失败")
- QMessageBox.critical(self, "错误", error_msg)
-
- # ===== 加载区域 =====
- def load_regions(self):
- if not self.session_id:
- QMessageBox.warning(self, "警告", "请先登录")
- return
-
- self.refresh_region_btn.setEnabled(False)
- self.statusBar().showMessage("正在加载区域...")
-
- self.region_thread = RegionQueryThread(self.session_id)
- self.region_thread.finished.connect(self.on_regions_loaded)
- self.region_thread.error.connect(self.on_regions_error)
- self.region_thread.start()
-
- def on_regions_loaded(self, data):
- self.refresh_region_btn.setEnabled(True)
- self.regions = data.get('list', [])
-
- self.load_region_combo.clear()
- self.unload_region_combo.clear()
- self.load_region_combo.addItem("请选择装车区域")
- self.unload_region_combo.addItem("请选择卸车区域")
-
- for region in self.regions:
- region_name = region.get('regionName', '未知')
- region_id = region.get('regionId', '')
- display = f"{region_name}"
- self.load_region_combo.addItem(display, region_id)
- self.unload_region_combo.addItem(display, region_id)
- self.region_cars[region_id] = region.get('carList', [])
-
- self.update_region_status()
- self.statusBar().showMessage(f"加载了 {len(self.regions)} 个区域")
- self.add_log(f"✅ 加载了 {len(self.regions)} 个区域")
-
- def on_regions_error(self, error_msg):
- self.refresh_region_btn.setEnabled(True)
- QMessageBox.critical(self, "错误", error_msg)
-
- def update_region_status(self):
- """更新区域状态显示条"""
- load_text = self.load_region_combo.currentText() if self.load_region_combo.currentIndex() > 0 else "未选择"
- unload_text = self.unload_region_combo.currentText() if self.unload_region_combo.currentIndex() > 0 else "未选择"
-
- # 获取车辆数量
- load_cars = 0
- unload_cars = 0
- if self.load_region_combo.currentIndex() > 0:
- region_id = self.load_region_combo.currentData()
- load_cars = len(self.region_cars.get(region_id, []))
- if self.unload_region_combo.currentIndex() > 0:
- region_id = self.unload_region_combo.currentData()
- unload_cars = len(self.region_cars.get(region_id, []))
-
- status = f"🚛 装车: {load_text} ({load_cars}辆) | 📦 卸车: {unload_text} ({unload_cars}辆)"
- self.region_status_label.setText(status)
-
- # ===== 查询车辆列表 =====
- def handle_query(self):
- if not self.session_id:
- return
-
- self.query_thread = QueryThread(self.session_id)
- self.query_thread.finished.connect(self.on_query_success)
- self.query_thread.error.connect(self.on_query_error)
- self.query_thread.start()
-
- def on_query_success(self, data):
- car_list = data.get('list', [])
- self.car_data = car_list
- self.save_check_state()
- self.table.setRowCount(len(car_list))
-
- for row, car in enumerate(car_list):
- car_plate = car.get('carPlate', '')
-
- # 第0列:启用复选框
- enable_checkbox = QCheckBox()
- enable_checkbox.setChecked(self.car_checked_state.get(car_plate, False))
- enable_checkbox.stateChanged.connect(lambda state, plate=car_plate: self.on_enable_changed(plate, state))
- self.table.setCellWidget(row, 0, enable_checkbox)
-
- # 第1列:扫码复选框
- scan_checkbox = QCheckBox()
- scan_checkbox.setChecked(self.car_scan_state.get(car_plate, False))
- scan_checkbox.stateChanged.connect(lambda state, plate=car_plate: self.on_scan_changed(plate, state))
- self.table.setCellWidget(row, 1, scan_checkbox)
-
- # 第2列:车队名
- self.table.setItem(row, 2, QTableWidgetItem(car.get('teamName', '')))
-
- # 第3列:车牌号
- self.table.setItem(row, 3, QTableWidgetItem(car_plate))
-
- # 第4列:状态
- self.table.setItem(row, 4, QTableWidgetItem(car.get('stateCn', '')))
-
- # 第5列:定位地址
- self.table.setItem(row, 5, QTableWidgetItem(car.get('addr', '')))
-
- # 第6列:操作按钮(测试发车)
- test_btn = QPushButton("测试发车")
- test_btn.clicked.connect(lambda checked, plate=car_plate: self.on_test_drive(plate))
- self.table.setCellWidget(row, 6, test_btn)
-
- self.statusBar().showMessage(f"车辆列表更新: {len(car_list)} 辆车")
-
- def on_query_error(self, error_msg):
- QMessageBox.critical(self, "错误", error_msg)
-
- # ===== 表格操作 =====
- def save_check_state(self):
- """保存启用和扫码状态"""
- for row in range(self.table.rowCount()):
- # 启用复选框
- enable_checkbox = self.table.cellWidget(row, 0)
- if enable_checkbox:
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
- if car_plate:
- self.car_checked_state[car_plate] = enable_checkbox.isChecked()
-
- # 扫码复选框
- scan_checkbox = self.table.cellWidget(row, 1)
- if scan_checkbox:
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
- if car_plate:
- self.car_scan_state[car_plate] = scan_checkbox.isChecked()
-
- def on_enable_changed(self, car_plate, state):
- """启用复选框状态变化"""
- self.car_checked_state[car_plate] = (state == Qt.Checked)
-
- def on_scan_changed(self, car_plate, state):
- """扫码复选框状态变化"""
- self.car_scan_state[car_plate] = (state == Qt.Checked)
-
- def select_all_enable(self):
- """全选启用"""
- for row in range(self.table.rowCount()):
- checkbox = self.table.cellWidget(row, 0)
- if checkbox:
- checkbox.setChecked(True)
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
- if car_plate:
- self.car_checked_state[car_plate] = True
-
- def deselect_all_enable(self):
- """取消全选启用"""
- for row in range(self.table.rowCount()):
- checkbox = self.table.cellWidget(row, 0)
- if checkbox:
- checkbox.setChecked(False)
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
- if car_plate:
- self.car_checked_state[car_plate] = False
-
- def select_all_scan(self):
- """全选扫码"""
- for row in range(self.table.rowCount()):
- checkbox = self.table.cellWidget(row, 1)
- if checkbox:
- checkbox.setChecked(True)
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
- if car_plate:
- self.car_scan_state[car_plate] = True
-
- def deselect_all_scan(self):
- """取消全选扫码"""
- for row in range(self.table.rowCount()):
- checkbox = self.table.cellWidget(row, 1)
- if checkbox:
- checkbox.setChecked(False)
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
- if car_plate:
- self.car_scan_state[car_plate] = False
-
- # ===== 测试发车功能 =====
- def on_test_drive(self, car_plate, from_scan=False):
- """测试发车按钮点击事件"""
- # 如果是扫码任务且监控已停止,不再执行
- if from_scan and not self.is_monitoring:
- self.add_log(f"⏹️ 监控已停止,取消测试发车: {car_plate}")
- return
- if not car_plate:
- if not from_scan:
- self.add_log("❌ 车牌号为空,无法执行测试发车")
- return
-
- # 获取可执行文件路径
- exe_path = self.get_exe_path_with_plate(car_plate)
-
- if not exe_path:
- if not from_scan:
- self.add_log(f"❌ 未选择文件夹,无法执行测试发车: {car_plate}")
- QMessageBox.warning(self, "警告", "请先选择文件夹路径")
- else:
- self.add_log(f"❌ 未选择文件夹,跳过: {car_plate}")
- # 继续处理下一个
- self.scan_timer.start(1000)
- return
-
- # 检查文件是否存在
- if not os.path.exists(exe_path):
- if not from_scan:
- self.add_log(f"❌ 可执行文件不存在: {exe_path}")
- QMessageBox.warning(self, "错误", f"可执行文件不存在:\n{exe_path}")
- else:
- self.add_log(f"❌ 可执行文件不存在,跳过: {car_plate}")
- # 继续处理下一个
- self.scan_timer.start(1000)
- return
-
- try:
- # 获取exe所在目录作为工作目录
- exe_dir = os.path.dirname(exe_path)
-
- if from_scan:
- self.add_log(f"🚀 [扫码任务] 执行测试发车: {car_plate}")
- else:
- self.add_log(f"🚀 执行测试发车: {car_plate}")
- self.add_log(f"📂 执行路径: {exe_path}")
- self.add_log(f"📁 工作目录: {exe_dir}")
-
- # 使用 ShellExecute 启动
- import ctypes
- result = ctypes.windll.shell32.ShellExecuteW(
- None, # 父窗口句柄
- "open", # 操作类型
- exe_path, # 要打开的程序路径
- "", # 命令行参数
- exe_dir, # 工作目录
- 1 # 窗口显示状态 (1=正常显示)
- )
-
- # ShellExecuteW 返回值 > 32 表示成功
- if result > 32:
- self.add_log(f"✅ [扫码任务] 测试发车启动成功: {car_plate}")
- self.statusBar().showMessage(f"测试发车已启动: {car_plate}")
- else:
- self.add_log(f"❌ [扫码任务] 启动失败: {car_plate},错误码: {result}")
-
- except Exception as e:
- self.add_log(f"❌ [扫码任务] 执行失败 {car_plate}: {str(e)}")
-
- # 注意:这里的定时器会在 process_next_scan 中启动,不需要在这里启动
-
- # ===== 监控 =====
- def start_monitoring(self):
- load_index = self.load_region_combo.currentIndex()
- unload_index = self.unload_region_combo.currentIndex()
-
- if load_index <= 0 and unload_index <= 0:
- QMessageBox.warning(self, "警告", "请选择装车区域和/或卸车区域")
- return
-
- if not self.session_id:
- QMessageBox.warning(self, "警告", "请先登录")
- return
-
- if self.send_client_combo.currentIndex() <= 0:
- QMessageBox.warning(self, "警告", "请选择装车客户端")
- return
-
- if self.receive_client_combo.currentIndex() <= 0:
- QMessageBox.warning(self, "警告", "请选择卸车客户端")
- return
-
- if not self.ws_client or not self.ws_client.is_running:
- QMessageBox.warning(self, "警告", "WebSocket未连接")
- return
-
- # 检查是否选择了文件夹
- if not self.folder_path_input.text().strip():
- reply = QMessageBox.question(
- self,
- "确认",
- "未选择文件夹,将发送空值,是否继续?",
- QMessageBox.Yes | QMessageBox.No,
- QMessageBox.No
- )
- if reply == QMessageBox.No:
- return
-
- self.is_monitoring = True
- self.start_btn.setEnabled(False)
- self.stop_btn.setEnabled(True)
- self.refresh_region_btn.setEnabled(False)
-
- self.triggered_records = {}
-
- load_region = self.load_region_combo.currentText() if load_index > 0 else "无"
- unload_region = self.unload_region_combo.currentText() if unload_index > 0 else "无"
-
- load_client = self.send_client_combo.currentText()
- unload_client = self.receive_client_combo.currentText()
-
- folder = self.folder_path_input.text().strip() or "无"
-
- self.add_log("=" * 50)
- self.add_log(f"监控启动 - 装车区域: {load_region}, 卸车区域: {unload_region}")
- self.add_log(f"装车客户端: {load_client}, 卸车客户端: {unload_client}")
- self.add_log(f"文件夹路径: {folder}")
- self.add_log("=" * 50)
-
- # 启动两个定时器
- self.refresh_timer.start(10000)
- self.region_timer.start(10000)
-
- self.statusBar().showMessage("监控运行中...")
-
- def stop_monitoring(self):
- self.is_monitoring = False
- self.refresh_timer.stop()
- self.region_timer.stop()
- self.start_btn.setEnabled(True)
- self.stop_btn.setEnabled(False)
- self.refresh_region_btn.setEnabled(True)
-
- # 停止扫码任务
- self.scan_timer.stop()
- self.is_processing_scan = False
- self.scan_task_queue = []
- self.current_scan_plate = None
-
- self.add_log("监控已停止")
- self.statusBar().showMessage("监控已停止")
-
- # ===== 区域检测 =====
- def check_regions(self):
- """检测区域车辆"""
- if not self.is_monitoring:
- return
-
- load_region_id = self.load_region_combo.currentData() if self.load_region_combo.currentIndex() > 0 else None
- unload_region_id = self.unload_region_combo.currentData() if self.unload_region_combo.currentIndex() > 0 else None
-
- if not load_region_id and not unload_region_id:
- return
-
- self.region_thread = RegionQueryThread(self.session_id)
- self.region_thread.finished.connect(lambda data: self.on_region_check(data, load_region_id, unload_region_id))
- self.region_thread.error.connect(lambda err: self.add_log(f"❌ 检测区域失败: {err}"))
- self.region_thread.start()
-
- def on_region_check(self, data, load_region_id, unload_region_id):
- """处理区域检测结果"""
- regions = data.get('list', [])
- current_time = datetime.now()
- self.add_log(json.dumps(regions))
- for region in regions:
- region_id = region.get('regionId')
- region_name = region.get('regionName', '未知')
- car_list = region.get('carList', [])
-
- # 更新车辆数据
- self.region_cars[region_id] = car_list
-
- # 检查装车区域
- if load_region_id and region_id == load_region_id:
- self.check_region_cars(region_name, car_list, 'load', current_time)
-
- # 检查卸车区域
- if unload_region_id and region_id == unload_region_id:
- self.check_region_cars(region_name, car_list, 'unload', current_time)
-
- self.update_region_status()
-
- def check_region_cars(self, region_name, car_list, task_type, current_time):
- for car_id in car_list:
- if not car_id:
- continue
- # 先初始化车牌
- car_plate = None
- car_state = None
- # 根据carId找车牌和状态
- for car in self.car_data:
- if str(car.get('carId')) == str(car_id):
- car_plate = car.get('carPlate')
- car_state = car.get('stateCn', '') # 获取状态
- break
-
- # 找不到车牌直接跳过
- if not car_plate:
- continue
- # 状态过滤:只处理 "在线-停车" 的车辆
- if "在线-停车" not in car_state:
- continue
- # 核心:启用过滤(必须放这里)
- if not self.car_checked_state.get(car_plate, False):
- continue
-
- # 初始化记录
- if car_plate not in self.triggered_records:
- self.triggered_records[car_plate] = {'load': None, 'unload': None}
- last_trigger = self.triggered_records[car_plate][task_type]
- if last_trigger is None or (current_time - last_trigger) >= timedelta(minutes=5):
- self.triggered_records[car_plate][task_type] = current_time
- task_name = "装车" if task_type == 'load' else "卸车"
- self.add_log(f"🚛 {car_plate} 在 {region_name} 检测到{task_name}")
- self.execute_task(car_plate, task_type)
-
- # ===== 执行任务 =====
- def execute_task(self, car_plate, task_type):
- """通过WebSocket执行任务"""
- if not self.ws_client or not self.ws_client.is_running:
- self.add_log("❌ WebSocket未连接,无法执行任务")
- return
-
- # 获取对应的客户端 userID
- if task_type == 'load':
- user_id = self.send_client_combo.currentData()
- else:
- user_id = self.receive_client_combo.currentData()
-
- if not user_id:
- self.add_log(f"❌ 未选择{task_type}客户端")
- return
-
- # 生成带车牌的文件路径: 文件夹路径/车牌.jpg
- value = self.get_value_with_plate(car_plate)
-
- if not value:
- self.add_log(f"⚠️ 未选择文件夹,发送空值,车牌: {car_plate}")
- else:
- self.add_log(f"📁 生成文件路径: {value}")
-
- # 发送指令
- route = "发货" if task_type == 'load' else "收货"
- success = self.ws_client.send(route, value, user_id)
-
- if success:
- self.add_log(f"📤 发送{route}指令到 {user_id},车牌: {car_plate},value: {value}")
- else:
- self.add_log(f"❌ 发送{route}指令失败")
-
- # ===== 发送指令(手动) =====
- def send_cmd_to_vehicle(self, car_plate, text_msg):
- """发送指令到车辆(保留原有功能)"""
- if not self.session_id or not self.username:
- self.add_log("❌ 未登录,无法发送指令")
- return
-
- params = {"textMsg": text_msg, "textType": 1, "signBit": "10010000"}
- url = "http://119.91.58.175:9999/gps-web/api/send_cmd.jsp"
- req_params = {
- "userId": self.username,
- "cmdId": "500",
- "cmd": "CmdSendText",
- "params": json.dumps(params, ensure_ascii=False),
- "sessionId": self.session_id,
- "plate": car_plate
- }
-
- try:
- response = requests.get(url, params=req_params, timeout=10)
- response.encoding = 'utf-8'
- self.add_log(f"📤 发送指令到 {car_plate}: {text_msg}")
- if response.status_code == 200:
- data = response.json()
- code = data.get('rspCode')
- if code == '0':
- self.add_log(f"✅ 指令发送成功")
- else:
- self.add_log(f"⚠️ {data.get('rspDesc', '发送失败')}")
- else:
- self.add_log(f"❌ HTTP {response.status_code}")
- except Exception as e:
- self.add_log(f"❌ {str(e)}")
-
- # ===== 配置 =====
- def save_config(self):
- config = {
- 'username': self.username_input.text(),
- 'folder_path': self.folder_path_input.text()
- }
- try:
- with open('config.json', 'w', encoding='utf-8') as f:
- json.dump(config, f, indent=4, ensure_ascii=False)
- except:
- pass
-
- def load_config(self):
- if os.path.exists('config.json'):
- try:
- with open('config.json', 'r', encoding='utf-8') as f:
- config = json.load(f)
- self.username_input.setText(config.get('username', '深金川'))
- folder_path = config.get('folder_path', '')
- if folder_path:
- self.folder_path_input.setText(folder_path)
- self.folder_path = folder_path
- except:
- pass
-
- # ===== 日志 =====
- def add_log(self, message, level="INFO"):
- """添加日志"""
- timestamp = datetime.now().strftime("%H:%M:%S")
- self.log_output.append(f"[{timestamp}] {message}")
- self.log_output.verticalScrollBar().setValue(
- self.log_output.verticalScrollBar().maximum()
- )
- # ==================== 启动 ====================
- def main():
- app = QApplication(sys.argv)
- window = MainWindow()
- window.show()
- sys.exit(app.exec_())
- if __name__ == '__main__':
- main()
|