文件分离函数类,增加提示信息
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
import sys
|
||||
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 import uic
|
||||
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 print_color 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_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_mode.clicked.connect(self.toggle_video_mode)
|
||||
self.chk_face_box.stateChanged.connect(self.toggle_face_box)
|
||||
@ -468,7 +468,6 @@ class QFaceCameraViewPage(PageTemplate):
|
||||
# 串口管理
|
||||
|
||||
def auto_connect_serial(self):
|
||||
"""根据 config.py 选择默认串口,但不立即打开"""
|
||||
ports = [p.device for p in serial.tools.list_ports.comports()]
|
||||
if not ports:
|
||||
self.log("[WARN] 未检测到任何串口设备")
|
||||
@ -517,7 +516,6 @@ class QFaceCameraViewPage(PageTemplate):
|
||||
except:
|
||||
pass
|
||||
self.ser = None
|
||||
self.btn_conn.setText("连接")
|
||||
self.log("[INFO] 串口已关闭")
|
||||
|
||||
def toggle_conn(self):
|
||||
@ -528,7 +526,8 @@ class QFaceCameraViewPage(PageTemplate):
|
||||
|
||||
def send(self, frame: bytes):
|
||||
if not self.ser or not getattr(self.ser, "is_open", False):
|
||||
QMessageBox.warning(self, "提示", "请先连接串口")
|
||||
inform_box : DialogInform = DialogInform()
|
||||
inform_box.information("提示", "请先连接串口")
|
||||
return
|
||||
try:
|
||||
self.ser.write(frame)
|
||||
@ -563,6 +562,8 @@ class QFaceCameraViewPage(PageTemplate):
|
||||
self.video_label.setPixmap(QPixmap())
|
||||
self.btn_video.setText("打开视频")
|
||||
self.log("[INFO] 视频已关闭")
|
||||
inform_box : DialogInform = DialogInform()
|
||||
inform_box.information("提示", "视频已关闭")
|
||||
return
|
||||
|
||||
if self.video_label.width()<50 or self.video_label.height()<50:
|
||||
@ -574,26 +575,36 @@ class QFaceCameraViewPage(PageTemplate):
|
||||
self.video_worker.start()
|
||||
self.btn_video.setText("关闭视频")
|
||||
self.log("[INFO] 正在打开视频")
|
||||
inform_box : DialogInform = DialogInform()
|
||||
inform_box.information("提示", "正在打开视频")
|
||||
|
||||
def toggle_video_mode(self):
|
||||
if self.current_video_mode==0:
|
||||
self.send_uvc(0)
|
||||
self.current_video_mode=1
|
||||
self.log("[INFO] 已切换到红外视频模式")
|
||||
inform_box : DialogInform = DialogInform()
|
||||
inform_box.information("提示", "已切换到红外视频模式")
|
||||
else:
|
||||
self.send_uvc(1)
|
||||
self.current_video_mode=0
|
||||
self.log("[INFO] 已切换到彩色视频模式")
|
||||
inform_box : DialogInform = DialogInform()
|
||||
inform_box.information("提示", "已切换到彩色视频模式")
|
||||
|
||||
def toggle_face_box(self, state):
|
||||
if state==Qt.Checked:
|
||||
self.face_box_enabled=True
|
||||
self.send_face_box(1)
|
||||
self.log("[INFO] 人脸框已开启")
|
||||
inform_box : DialogInform = DialogInform()
|
||||
inform_box.information("提示", "人脸框已开启")
|
||||
else:
|
||||
self.face_box_enabled=False
|
||||
self.send_face_box(0)
|
||||
self.log("[INFO] 人脸框已关闭")
|
||||
inform_box : DialogInform = DialogInform()
|
||||
inform_box.information("提示", "人脸框已关闭")
|
||||
|
||||
# ---------------- 发送指令 ----------------
|
||||
def send_uvc(self, mode):
|
||||
|
||||
@ -20,7 +20,9 @@ from Shared_CODE.DialogModifyValue import DialogModifyValue
|
||||
from Shared_CODE.DialogModifyAlias import DialogModifyAlias
|
||||
from Shared_CODE.DialogModifyText import DialogModifyText
|
||||
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 print_color import *
|
||||
|
||||
65
QT5_Project/Shared_CODE/DialogFaceEnrollItgSingle.py
Normal file
65
QT5_Project/Shared_CODE/DialogFaceEnrollItgSingle.py
Normal 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)
|
||||
@ -6,13 +6,14 @@ 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
|
||||
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
|
||||
QLineEdit, QTableWidget, QTableWidgetItem,QDialogButtonBox,QShortcut
|
||||
)
|
||||
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
import cv2
|
||||
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__), '../..')))
|
||||
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")
|
||||
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 工具 --------------------"
|
||||
@ -71,8 +70,9 @@ def save_user(user_id: int, user_name: str) -> bool:
|
||||
class UserManageDialog(QDialog):
|
||||
def __init__(self, parent, send_func):
|
||||
super().__init__(parent)
|
||||
uic.loadUi(users_ui_file_path, self)
|
||||
self.send_func = send_func
|
||||
uic.loadUi(users_ui_file_path, self)
|
||||
|
||||
self.btn_delete.clicked.connect(self.delete_selected)
|
||||
self.btn_refresh.clicked.connect(self.refresh)
|
||||
self.btn_get.clicked.connect(self.get_from_device)
|
||||
@ -114,44 +114,6 @@ class UserManageDialog(QDialog):
|
||||
self.refresh()
|
||||
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__':
|
||||
app = QApplication(sys.argv)
|
||||
dialog = UserManageDialog()
|
||||
54
QT5_Project/Shared_CODE/DialogFaceVerify.py
Normal file
54
QT5_Project/Shared_CODE/DialogFaceVerify.py
Normal 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)
|
||||
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import struct
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Tuple, List
|
||||
from QT5_Project.Shared_CODE.DialogInform import DialogInform
|
||||
|
||||
SYNC = b"\xEF\xAA"
|
||||
|
||||
@ -296,71 +297,86 @@ def build_enroll_with_photo_chunk(seq: int, chunk: bytes) -> bytes:
|
||||
# ---- Parsers (Reply / Note / Image) ----
|
||||
def parse_reply(data: bytes) -> dict:
|
||||
if len(data) < 2:
|
||||
return {"type":"REPLY","error":"short"}
|
||||
return {"type": "REPLY", "error": "short"}
|
||||
|
||||
mid = data[0]
|
||||
result = data[1]
|
||||
rest = data[2:]
|
||||
|
||||
# 初始化 info 字典
|
||||
# 通用字段
|
||||
info = {
|
||||
"type":"REPLY",
|
||||
"type": "REPLY",
|
||||
"mid": mid,
|
||||
"mid_name": CMD_NAMES.get(mid, f"0x{mid:02X}"),
|
||||
"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:
|
||||
uid = (rest[0]<<8)|rest[1]
|
||||
uid = (rest[0] << 8) | rest[1]
|
||||
name = rest[2:34].rstrip(b"\x00").decode("utf-8", errors="ignore")
|
||||
admin = rest[34]
|
||||
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:
|
||||
uid = (rest[0]<<8)|rest[1]
|
||||
uid = (rest[0] << 8) | rest[1]
|
||||
face_dir = rest[2]
|
||||
info.update({"user_id": uid, "face_direction": face_dir})
|
||||
|
||||
elif mid == CMD_GET_STATUS and len(rest) >= 1:
|
||||
status = rest[0]
|
||||
info.update({"status": status, "status_name": {
|
||||
0: "空闲",
|
||||
1: "录入中",
|
||||
2: "验证中"
|
||||
}.get(status, f"0x{status:02X}")})
|
||||
status_map = {0: "空闲", 1: "录入中", 2: "验证中"}
|
||||
info.update({"status": status, "status_name": status_map.get(status, f"0x{status:02X}")})
|
||||
|
||||
elif mid == CMD_GET_USER_INFO and len(rest) >= 35:
|
||||
uid = (rest[0]<<8)|rest[1]
|
||||
uid = (rest[0] << 8) | rest[1]
|
||||
name = rest[2:34].decode("ascii", errors="ignore")
|
||||
admin = rest[34]
|
||||
info.update({"user_id": uid, "user_name": name, "admin": admin})
|
||||
|
||||
elif mid == CMD_GET_ALL_USERID and len(rest) >= 1:
|
||||
n = rest[0]
|
||||
ids = [(rest[i]<<8)|rest[i+1] for i in range(1, 1+2*n, 2) if i+1 < len(rest)]
|
||||
ids = [(rest[i] << 8) | rest[i + 1] for i in range(1, 1 + 2 * n, 2) if i + 1 < len(rest)]
|
||||
info.update({"count": n, "user_ids": ids})
|
||||
|
||||
elif mid == CMD_GET_VERSION:
|
||||
info["version_str"] = rest.decode("ascii", errors="ignore")
|
||||
|
||||
|
||||
elif mid == CMD_LED_CONTROL and len(rest) >= 1:
|
||||
led_state = rest[0]
|
||||
info.update({"led_state": led_state, "led_state_name": {
|
||||
0: "灭",
|
||||
1: "亮"
|
||||
}.get(led_state, f"0x{led_state:02X}")})
|
||||
led_map = {0: "灭", 1: "亮"}
|
||||
info.update({"led_state": led_state, "led_state_name": led_map.get(led_state, f"0x{led_state:02X}")})
|
||||
|
||||
elif mid == CMD_ENROLL_WITH_PHOTO:
|
||||
if len(rest) >= 2:
|
||||
seq = (rest[0]<<8)|rest[1]
|
||||
seq = (rest[0] << 8) | rest[1]
|
||||
info["seq"] = seq
|
||||
if len(rest) >= 6:
|
||||
uid = (rest[2]<<8)|rest[3]
|
||||
uid = (rest[2] << 8) | rest[3]
|
||||
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
|
||||
|
||||
|
||||
def parse_note(data: bytes) -> dict:
|
||||
if len(data) < 1:
|
||||
return {"type":"NOTE","error":"short"}
|
||||
|
||||
Reference in New Issue
Block a user