文件分离函数类,增加提示信息

This commit is contained in:
冯佳
2025-09-05 15:40:00 +08:00
parent 79f733126b
commit 0557ba7f1c
6 changed files with 182 additions and 72 deletions

View File

@ -1,7 +1,7 @@
# This Python file uses the following encoding: utf-8 # This Python file uses the following encoding: utf-8
import sys import sys
import time import time
from PyQt5.QtWidgets import QWidget, QLineEdit from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget, QWidget, QLayout, QLabel, QLineEdit, QPushButton, QMessageBox, QShortcut, QDialog,QTextEdit from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget, QWidget, QLayout, QLabel, QLineEdit, QPushButton, QMessageBox, QShortcut, QDialog,QTextEdit
from PyQt5 import uic from PyQt5 import uic
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QObject, QRunnable, QMutex, QTimer, QEvent from PyQt5.QtCore import Qt, QThread, pyqtSignal, QObject, QRunnable, QMutex, QTimer, QEvent
@ -19,7 +19,9 @@ import uart_group_config as group_config
from mqtt_device import class_comm_mqtt_thread, class_comm_mqtt_interface from mqtt_device import class_comm_mqtt_thread, class_comm_mqtt_interface
from print_color import * from print_color import *
from Shared_CODE.get_tip_prop import * from Shared_CODE.get_tip_prop import *
from Shared_CODE.DialogFaceView import * from QT5_Project.Shared_CODE.DialogFaceVerify import VerifyDialog
from QT5_Project.Shared_CODE.DialogFaceEnrollItgSingle import EnrollItgSingleDialog
from QT5_Project.Shared_CODE.DialogFaceUserManage import UserManageDialog, save_user, load_users, save_users_list
# 设置 UI 目录的路径 # 设置 UI 目录的路径
ui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'UI')) ui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'UI'))
@ -441,8 +443,6 @@ class QFaceCameraViewPage(PageTemplate):
#信号绑定 #信号绑定
# self.btn_refresh.clicked.connect(self.refresh_ports)
# self.btn_conn.clicked.connect(self.toggle_conn)
self.btn_video.clicked.connect(self.toggle_video) self.btn_video.clicked.connect(self.toggle_video)
self.btn_video_mode.clicked.connect(self.toggle_video_mode) self.btn_video_mode.clicked.connect(self.toggle_video_mode)
self.chk_face_box.stateChanged.connect(self.toggle_face_box) self.chk_face_box.stateChanged.connect(self.toggle_face_box)
@ -468,7 +468,6 @@ class QFaceCameraViewPage(PageTemplate):
# 串口管理 # 串口管理
def auto_connect_serial(self): def auto_connect_serial(self):
"""根据 config.py 选择默认串口,但不立即打开"""
ports = [p.device for p in serial.tools.list_ports.comports()] ports = [p.device for p in serial.tools.list_ports.comports()]
if not ports: if not ports:
self.log("[WARN] 未检测到任何串口设备") self.log("[WARN] 未检测到任何串口设备")
@ -517,7 +516,6 @@ class QFaceCameraViewPage(PageTemplate):
except: except:
pass pass
self.ser = None self.ser = None
self.btn_conn.setText("连接")
self.log("[INFO] 串口已关闭") self.log("[INFO] 串口已关闭")
def toggle_conn(self): def toggle_conn(self):
@ -528,7 +526,8 @@ class QFaceCameraViewPage(PageTemplate):
def send(self, frame: bytes): def send(self, frame: bytes):
if not self.ser or not getattr(self.ser, "is_open", False): if not self.ser or not getattr(self.ser, "is_open", False):
QMessageBox.warning(self, "提示", "请先连接串口") inform_box : DialogInform = DialogInform()
inform_box.information("提示", "请先连接串口")
return return
try: try:
self.ser.write(frame) self.ser.write(frame)
@ -563,6 +562,8 @@ class QFaceCameraViewPage(PageTemplate):
self.video_label.setPixmap(QPixmap()) self.video_label.setPixmap(QPixmap())
self.btn_video.setText("打开视频") self.btn_video.setText("打开视频")
self.log("[INFO] 视频已关闭") self.log("[INFO] 视频已关闭")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "视频已关闭")
return return
if self.video_label.width()<50 or self.video_label.height()<50: if self.video_label.width()<50 or self.video_label.height()<50:
@ -574,26 +575,36 @@ class QFaceCameraViewPage(PageTemplate):
self.video_worker.start() self.video_worker.start()
self.btn_video.setText("关闭视频") self.btn_video.setText("关闭视频")
self.log("[INFO] 正在打开视频") self.log("[INFO] 正在打开视频")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "正在打开视频")
def toggle_video_mode(self): def toggle_video_mode(self):
if self.current_video_mode==0: if self.current_video_mode==0:
self.send_uvc(0) self.send_uvc(0)
self.current_video_mode=1 self.current_video_mode=1
self.log("[INFO] 已切换到红外视频模式") self.log("[INFO] 已切换到红外视频模式")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "已切换到红外视频模式")
else: else:
self.send_uvc(1) self.send_uvc(1)
self.current_video_mode=0 self.current_video_mode=0
self.log("[INFO] 已切换到彩色视频模式") self.log("[INFO] 已切换到彩色视频模式")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "已切换到彩色视频模式")
def toggle_face_box(self, state): def toggle_face_box(self, state):
if state==Qt.Checked: if state==Qt.Checked:
self.face_box_enabled=True self.face_box_enabled=True
self.send_face_box(1) self.send_face_box(1)
self.log("[INFO] 人脸框已开启") self.log("[INFO] 人脸框已开启")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "人脸框已开启")
else: else:
self.face_box_enabled=False self.face_box_enabled=False
self.send_face_box(0) self.send_face_box(0)
self.log("[INFO] 人脸框已关闭") self.log("[INFO] 人脸框已关闭")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "人脸框已关闭")
# ---------------- 发送指令 ---------------- # ---------------- 发送指令 ----------------
def send_uvc(self, mode): def send_uvc(self, mode):

View File

@ -20,7 +20,9 @@ from Shared_CODE.DialogModifyValue import DialogModifyValue
from Shared_CODE.DialogModifyAlias import DialogModifyAlias from Shared_CODE.DialogModifyAlias import DialogModifyAlias
from Shared_CODE.DialogModifyText import DialogModifyText from Shared_CODE.DialogModifyText import DialogModifyText
from Shared_CODE.DialogInform import DialogInform from Shared_CODE.DialogInform import DialogInform
from Shared_CODE.DialogFaceView import UserManageDialog, VerifyDialog from QT5_Project.Shared_CODE.DialogFaceVerify import VerifyDialog
from QT5_Project.Shared_CODE.DialogFaceEnrollItgSingle import EnrollItgSingleDialog
from QT5_Project.Shared_CODE.DialogFaceUserManage import UserManageDialog
from Shared_CODE.get_tip_prop import * from Shared_CODE.get_tip_prop import *
from print_color import * from print_color import *

View File

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import sys, os, io, csv, time, datetime
from typing import Callable, Optional
# PyQt5
from PyQt5 import QtWidgets, uic
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QSize
from PyQt5.QtGui import QImage, QPixmap, QKeySequence
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QComboBox, QTextEdit, QFileDialog, QMessageBox,
QGroupBox, QGridLayout, QDialog, QFormLayout, QSpinBox, QCheckBox,
QLineEdit, QTableWidget, QTableWidgetItem,QDialogButtonBox,QShortcut
)
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import cv2
from PIL import Image
import serial
import serial.tools.list_ports
# ---------- 协议导入----------
from Shared_CODE.FaceRecognitionProtocol import (
build_reset, build_uvc_view, build_face_view, build_verify,
build_enroll_itg_single, build_delete_all, build_get_all_userid,build_delete_user,
MID_REPLY, MID_NOTE, CMD_ENROLL, CMD_ENROLL_ITG,
parse_reply, parse_note
)
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
ui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../Shared_UI'))
enrill_ui_file_path = os.path.join(ui_path, "enroll.ui")
class EnrollItgSingleDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
uic.loadUi(enrill_ui_file_path, self)
self.cb_admin.setCurrentIndex(0)
self.btn_ok.clicked.connect(self.accept)
def values(self):
admin_val = self.cb_admin.currentIndex()
uname = self.le_name.text().strip()[:32]
face_dir = 0
if self.chk_mid.isChecked(): face_dir |= 0x01
if self.chk_right.isChecked(): face_dir |= 0x02
if self.chk_left.isChecked(): face_dir |= 0x04
if self.chk_down.isChecked(): face_dir |= 0x08
if self.chk_up.isChecked(): face_dir |= 0x10
if face_dir == 0: face_dir = 0x01
timeout_val = self.sb_timeout.value()
try:
itg_val = int(self.le_itg.text().strip(), 0)
except ValueError:
itg_val = 0
return admin_val, uname, face_dir, timeout_val, itg_val
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = EnrollItgSingleDialog()
dialog.exec()
sys.exit(0)

View File

@ -6,13 +6,14 @@ from typing import Callable, Optional
# PyQt5 # PyQt5
from PyQt5 import QtWidgets, uic from PyQt5 import QtWidgets, uic
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QSize from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QSize
from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtGui import QImage, QPixmap, QKeySequence
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QComboBox, QTextEdit, QFileDialog, QMessageBox, QLabel, QPushButton, QComboBox, QTextEdit, QFileDialog, QMessageBox,
QGroupBox, QGridLayout, QDialog, QFormLayout, QSpinBox, QCheckBox, QGroupBox, QGridLayout, QDialog, QFormLayout, QSpinBox, QCheckBox,
QLineEdit, QTableWidget, QTableWidgetItem QLineEdit, QTableWidget, QTableWidgetItem,QDialogButtonBox,QShortcut
) )
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import cv2 import cv2
from PIL import Image from PIL import Image
@ -30,8 +31,6 @@ from Shared_CODE.FaceRecognitionProtocol import (
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
ui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../Shared_UI')) ui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../Shared_UI'))
users_ui_file_path = os.path.join(ui_path, "users.ui") users_ui_file_path = os.path.join(ui_path, "users.ui")
verify_ui_file_path = os.path.join(ui_path, "verify.ui")
enrill_ui_file_path = os.path.join(ui_path, "enroll.ui")
CSV_FILE = os.path.join(ui_path, "users.csv") CSV_FILE = os.path.join(ui_path, "users.csv")
# -------------------- CSV 工具 --------------------" # -------------------- CSV 工具 --------------------"
@ -71,8 +70,9 @@ def save_user(user_id: int, user_name: str) -> bool:
class UserManageDialog(QDialog): class UserManageDialog(QDialog):
def __init__(self, parent, send_func): def __init__(self, parent, send_func):
super().__init__(parent) super().__init__(parent)
uic.loadUi(users_ui_file_path, self)
self.send_func = send_func self.send_func = send_func
uic.loadUi(users_ui_file_path, self)
self.btn_delete.clicked.connect(self.delete_selected) self.btn_delete.clicked.connect(self.delete_selected)
self.btn_refresh.clicked.connect(self.refresh) self.btn_refresh.clicked.connect(self.refresh)
self.btn_get.clicked.connect(self.get_from_device) self.btn_get.clicked.connect(self.get_from_device)
@ -114,44 +114,6 @@ class UserManageDialog(QDialog):
self.refresh() self.refresh()
QMessageBox.information(self, "提示", "已请求删除所有用户并清空本地记录") QMessageBox.information(self, "提示", "已请求删除所有用户并清空本地记录")
class VerifyDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
uic.loadUi(verify_ui_file_path, self)
self.cb_rightaway.setCurrentIndex(0)
self.sb_timeout.setValue(10)
self.btn_ok.clicked.connect(self.accept)
def values(self):
pd_val = self.cb_rightaway.currentIndex()
timeout_val = self.sb_timeout.value()
return pd_val, timeout_val
class EnrollItgSingleDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
uic.loadUi(enrill_ui_file_path, self)
self.cb_admin.setCurrentIndex(0)
self.btn_ok.clicked.connect(self.accept)
def values(self):
admin_val = self.cb_admin.currentIndex()
uname = self.le_name.text().strip()[:32]
face_dir = 0
if self.chk_mid.isChecked(): face_dir |= 0x01
if self.chk_right.isChecked(): face_dir |= 0x02
if self.chk_left.isChecked(): face_dir |= 0x04
if self.chk_down.isChecked(): face_dir |= 0x08
if self.chk_up.isChecked(): face_dir |= 0x10
if face_dir == 0: face_dir = 0x01
timeout_val = self.sb_timeout.value()
try:
itg_val = int(self.le_itg.text().strip(), 0)
except ValueError:
itg_val = 0
return admin_val, uname, face_dir, timeout_val, itg_val
if __name__ == '__main__': if __name__ == '__main__':
app = QApplication(sys.argv) app = QApplication(sys.argv)
dialog = UserManageDialog() dialog = UserManageDialog()

View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
import sys, os, io, csv, time, datetime
from typing import Callable, Optional
# PyQt5
from PyQt5 import QtWidgets, uic
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QSize
from PyQt5.QtGui import QImage, QPixmap, QKeySequence
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QComboBox, QTextEdit, QFileDialog, QMessageBox,
QGroupBox, QGridLayout, QDialog, QFormLayout, QSpinBox, QCheckBox,
QLineEdit, QTableWidget, QTableWidgetItem,QDialogButtonBox,QShortcut
)
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import cv2
from PIL import Image
import serial
import serial.tools.list_ports
# ---------- 协议导入----------
from Shared_CODE.FaceRecognitionProtocol import (
build_reset, build_uvc_view, build_face_view, build_verify,
build_enroll_itg_single, build_delete_all, build_get_all_userid,build_delete_user,
MID_REPLY, MID_NOTE, CMD_ENROLL, CMD_ENROLL_ITG,
parse_reply, parse_note
)
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
ui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../Shared_UI'))
verify_ui_file_path = os.path.join(ui_path, "verify.ui")
class VerifyDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
uic.loadUi(verify_ui_file_path, self)
self.cb_rightaway.setCurrentIndex(0)
self.sb_timeout.setValue(10)
self.btn_ok.clicked.connect(self.accept)
def values(self):
pd_val = self.cb_rightaway.currentIndex()
timeout_val = self.sb_timeout.value()
return pd_val, timeout_val
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = VerifyDialog()
dialog.exec()
sys.exit(0)

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import struct import struct
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, Tuple, List from typing import Optional, Tuple, List
from QT5_Project.Shared_CODE.DialogInform import DialogInform
SYNC = b"\xEF\xAA" SYNC = b"\xEF\xAA"
@ -297,25 +298,33 @@ def build_enroll_with_photo_chunk(seq: int, chunk: bytes) -> bytes:
def parse_reply(data: bytes) -> dict: def parse_reply(data: bytes) -> dict:
if len(data) < 2: if len(data) < 2:
return {"type": "REPLY", "error": "short"} return {"type": "REPLY", "error": "short"}
mid = data[0] mid = data[0]
result = data[1] result = data[1]
rest = data[2:] rest = data[2:]
# 初始化 info 字典 # 通用字段
info = { info = {
"type": "REPLY", "type": "REPLY",
"mid": mid, "mid": mid,
"mid_name": CMD_NAMES.get(mid, f"0x{mid:02X}"), "mid_name": CMD_NAMES.get(mid, f"0x{mid:02X}"),
"result": result, "result": result,
"result_name": RESULT_NAMES.get(result, f"0x{result:02X}") "result_name": RESULT_NAMES.get(result, f"0x{result:02X}"),
"ok": (result == 0), # 成功标志
} }
# ========== 分支解析 ==========
if mid == CMD_VERIFY and len(rest) >= 36: if mid == CMD_VERIFY and len(rest) >= 36:
uid = (rest[0] << 8) | rest[1] uid = (rest[0] << 8) | rest[1]
name = rest[2:34].rstrip(b"\x00").decode("utf-8", errors="ignore") name = rest[2:34].rstrip(b"\x00").decode("utf-8", errors="ignore")
admin = rest[34] admin = rest[34]
unlock = rest[35] unlock = rest[35]
info.update({"user_id": uid, "user_name": name, "admin": admin, "unlock_status": unlock}) info.update({
"user_id": uid,
"user_name": name,
"admin": admin,
"unlock_status": unlock,
})
elif mid in (CMD_ENROLL, 0x1D, CMD_ENROLL_ITG) and len(rest) >= 3: elif mid in (CMD_ENROLL, 0x1D, CMD_ENROLL_ITG) and len(rest) >= 3:
uid = (rest[0] << 8) | rest[1] uid = (rest[0] << 8) | rest[1]
@ -324,11 +333,8 @@ def parse_reply(data: bytes) -> dict:
elif mid == CMD_GET_STATUS and len(rest) >= 1: elif mid == CMD_GET_STATUS and len(rest) >= 1:
status = rest[0] status = rest[0]
info.update({"status": status, "status_name": { status_map = {0: "空闲", 1: "录入中", 2: "验证中"}
0: "空闲", info.update({"status": status, "status_name": status_map.get(status, f"0x{status:02X}")})
1: "录入中",
2: "验证中"
}.get(status, f"0x{status:02X}")})
elif mid == CMD_GET_USER_INFO and len(rest) >= 35: elif mid == CMD_GET_USER_INFO and len(rest) >= 35:
uid = (rest[0] << 8) | rest[1] uid = (rest[0] << 8) | rest[1]
@ -346,10 +352,8 @@ def parse_reply(data: bytes) -> dict:
elif mid == CMD_LED_CONTROL and len(rest) >= 1: elif mid == CMD_LED_CONTROL and len(rest) >= 1:
led_state = rest[0] led_state = rest[0]
info.update({"led_state": led_state, "led_state_name": { led_map = {0: "", 1: ""}
0: "", info.update({"led_state": led_state, "led_state_name": led_map.get(led_state, f"0x{led_state:02X}")})
1: ""
}.get(led_state, f"0x{led_state:02X}")})
elif mid == CMD_ENROLL_WITH_PHOTO: elif mid == CMD_ENROLL_WITH_PHOTO:
if len(rest) >= 2: if len(rest) >= 2:
@ -359,8 +363,20 @@ def parse_reply(data: bytes) -> dict:
uid = (rest[2] << 8) | rest[3] uid = (rest[2] << 8) | rest[3]
info["user_id"] = uid info["user_id"] = uid
# 自动生成提示消息
if info.get("ok"):
info["message"] = f"{info['mid_name']} 成功"
inform_box : DialogInform = DialogInform()
inform_box.information("提示", f"{info['mid_name']} 成功")
else:
info["message"] = f"{info['mid_name']} 失败: {info['result_name']}"
inform_box : DialogInform = DialogInform()
inform_box.information("提示", f"{info['mid_name']} 失败: {info['result_name']}")
return info return info
def parse_note(data: bytes) -> dict: def parse_note(data: bytes) -> dict:
if len(data) < 1: if len(data) < 1:
return {"type":"NOTE","error":"short"} return {"type":"NOTE","error":"short"}