1.人脸验证执行操作成功

2.UI底图进行修改
This commit is contained in:
冯佳
2025-09-13 11:08:46 +08:00
parent a3bb7f0701
commit 621c107be1
23 changed files with 247 additions and 245 deletions

View File

@ -19,7 +19,7 @@ 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 QT5_Project.Shared_CODE.DialogFaceVerify import VerifyDialog
from QT5_Project.Shared_CODE.FaceRecognitionProtocol import *
from QT5_Project.Shared_CODE.DialogFaceEnrollItgSingle import EnrollItgSingleDialog
from QT5_Project.Shared_CODE.DialogFaceUserManage import UserManageDialog, save_user, load_users, save_users_list
@ -441,18 +441,6 @@ class QFaceCameraViewPage(PageTemplate):
self.current_video_mode = 0
self.current_face_box = 0
#信号绑定
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)
self.btn_save.clicked.connect(self.save_log)
self.btn_reset.clicked.connect(lambda: self.send(build_reset()))
self.btn_enroll.clicked.connect(self.do_enroll_itg_single)
self.btn_users.clicked.connect(self.do_manage_users)
# self.btn_verify.clicked.connect(self.do_verify)
# 定时器:串口监控 & 视频重连
self.t_port = QTimer(self)
self.t_port.setInterval(1000)
@ -464,6 +452,8 @@ class QFaceCameraViewPage(PageTemplate):
self.t_video.timeout.connect(self._check_video)
self.t_video.start()
self.face_verify_result = None # 用于存储人脸验证结果
self.auto_connect_serial()
# 串口管理
@ -535,91 +525,6 @@ class QFaceCameraViewPage(PageTemplate):
except Exception as e:
self.log(f"[ERR] write: {e}")
# 业务功能
# def do_verify(self):
# dlg = VerifyDialog(self)
# if dlg.exec_() == QDialog.Accepted:
# pd_val, timeout_val = dlg.values()
# self.send(build_verify(pd_val, timeout_val))
# def do_enroll_itg_single(self):
# dlg = EnrollItgSingleDialog(self)
# if dlg.exec_() == QDialog.Accepted:
# admin_val, uname, face_dir, timeout_val, itg_val = dlg.values()
# self.last_enroll_name = uname
# self.send(build_enroll_itg_single(admin_val, uname, face_dir, timeout_val, itg_val))
# def do_manage_users(self):
# UserManageDialog(self, self.send).exec_()
def toggle_video(self):
running = self.video_worker and self.video_worker.isRunning()
if running:
self.video_worker.stop()
self.video_worker.wait(300)
self.video_worker = None
self.video_label.setText("未打开")
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:
self.video_label.setMinimumSize(360,480)
self.video_worker = VideoWorker(cam_index=0, target_size=self.video_label.size())
self.video_worker.pixmap_ready.connect(self.video_label.setPixmap)
self.video_worker.log_message.connect(self.log)
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):
if self.ser and getattr(self.ser,"is_open",False):
self.ser.write(build_uvc_view(mode))
else:
self.log("[WARN] 串口未连接,无法切换视频模式")
def send_face_box(self, state):
if self.ser and getattr(self.ser,"is_open",False):
self.ser.write(build_face_view(state))
else:
self.log("[WARN] 串口未连接,无法控制人脸框")
# ---------------- 定时器检测 ----------------
def _check_video(self):
if self.video_worker and not self.video_worker.isRunning():
@ -644,6 +549,15 @@ class QFaceCameraViewPage(PageTemplate):
self.log(f"[INFO] 用户 {user_name}(ID={user_id}) 已保存")
else:
QMessageBox.warning(self, "提示", f"用户ID {user_id} 已存在!")
elif info.get("mid") == CMD_VERIFY :
if info.get("result") == 0x00:
user_id = info.get("user_id")
print(f"[INFO] 用户(ID={user_id}) 验证通过")
self.face_verify_result = True
else:
self.face_verify_result = False
elif msg_id == MID_NOTE:
info = parse_note(data)
self.log(f"[NOTE] {info}")
@ -848,16 +762,6 @@ class APPWindow(QMainWindow):
# self.setGeometry(0, 0, 1024, 768)
# self.stack.setGeometry(0, 0, 1024, 768)
self.ser = None
self.worker = SerialWorker(lambda: self.ser)
self.worker.frame_received.connect(self.on_frame)
self.worker.log_message.connect(self.log)
self.worker.start()
self.video_worker = None
self.last_port = None
self.last_enroll_name = ""
self.current_video_mode = 0
self.current_face_box = 0
self.showFullScreen()
@ -923,32 +827,6 @@ class APPWindow(QMainWindow):
test_init = system_parameter()
set_screen_blanking_time(test_init.get_screen_blanking_time())
###########################################################################
def on_frame(self, fr: dict):
self.log("< " + fr["raw"].hex(" "))
msg_id = fr.get("msg_id")
data = fr.get("data", b"")
if msg_id == MID_REPLY:
info = parse_reply(data)
self.log(f"[REPLY] {info}")
if info.get("mid") in (CMD_ENROLL, CMD_ENROLL_ITG) and info.get("result") == 0x00:
user_id = info.get("user_id")
user_name = self.last_enroll_name or ""
if user_id and user_name:
if save_user(user_id, user_name):
self.log(f"[INFO] 用户 {user_name}(ID={user_id}) 已保存")
else:
QMessageBox.warning(self, "提示", f"用户ID {user_id} 已存在!")
elif msg_id == MID_NOTE:
info = parse_note(data)
self.log(f"[NOTE] {info}")
def log(self, s: str):
ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"[{ts}] {s}") # 控制台打印日志
##########################################################################
def search_page_widget(self, page) :
for list_item in self.allpages_list:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -171,22 +171,6 @@ color: rgb(170, 0, 0);</string>
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QPushButton" name="btn_video">
<property name="geometry">
<rect>
<x>200</x>
<y>20</y>
<width>80</width>
<height>23</height>
</rect>
</property>
<property name="statusTip">
<string>Index=0</string>
</property>
<property name="text">
<string>打开视频</string>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="group_cmd">
<property name="geometry">
@ -229,28 +213,12 @@ color: rgb(170, 0, 0);</string>
</rect>
</property>
<property name="statusTip">
<string>Index=0, Action=Videomode,SelectImag=IMxx_00F.png</string>
<string>Index=0, Action=VideoMode,SelectImag=IMxx_00F.png</string>
</property>
<property name="text">
<string>视频模式</string>
</property>
</widget>
<widget class="QCheckBox" name="chk_face_box">
<property name="geometry">
<rect>
<x>12</x>
<y>88</y>
<width>85</width>
<height>21</height>
</rect>
</property>
<property name="statusTip">
<string/>
</property>
<property name="text">
<string>显示人脸框</string>
</property>
</widget>
<widget class="QPushButton" name="btn_verify">
<property name="geometry">
<rect>
@ -277,7 +245,7 @@ color: rgb(170, 0, 0);</string>
</rect>
</property>
<property name="statusTip">
<string>Index=0, Action=Enroll,SelectImag=IMxx_00F.png</string>
<string>Index=0, Action=EnrollItgSingle,SelectImag=IMxx_00F.png</string>
</property>
<property name="text">
<string>ITG注册</string>
@ -303,7 +271,7 @@ color: rgb(170, 0, 0);</string>
<property name="geometry">
<rect>
<x>0</x>
<y>210</y>
<y>310</y>
<width>120</width>
<height>25</height>
</rect>
@ -334,6 +302,38 @@ color: rgb(177, 229, 252);</string>
<bool>false</bool>
</property>
</widget>
<widget class="QPushButton" name="btn_video">
<property name="geometry">
<rect>
<x>10</x>
<y>210</y>
<width>80</width>
<height>23</height>
</rect>
</property>
<property name="statusTip">
<string>Index=0, Action=ConnectCamera,password,SelectImag=IMxx_00F.png</string>
</property>
<property name="text">
<string>打开视频</string>
</property>
</widget>
<widget class="QPushButton" name="btn_face_mode">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>80</width>
<height>23</height>
</rect>
</property>
<property name="statusTip">
<string>Index=0, Action=FaceBox,SelectImag=IMxx_00F.png</string>
</property>
<property name="text">
<string>人脸框</string>
</property>
</widget>
</widget>
<widget class="QLabel" name="P05_01BG">
<property name="geometry">

View File

@ -864,47 +864,11 @@ color: rgb(207, 0, 13);</string>
</size>
</property>
</widget>
<widget class="QLineEdit" name="FaceRecogTimeoutEdit">
<property name="geometry">
<rect>
<x>880</x>
<y>640</y>
<width>120</width>
<height>25</height>
</rect>
</property>
<property name="font">
<font>
<family>Arial</family>
<pointsize>16</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="statusTip">
<string>System=FaceRecogTimeout, Action=ModifySystem, Index=13,SelectImag=P4_ParaSelect.png, password</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgba(85, 170, 127,0);
color: rgb(177, 229, 252);</string>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
<zorder>sys_P00_2</zorder>
<zorder>sys_P00_3</zorder>
<zorder>BindNum_Title</zorder>
<zorder>sys_home</zorder>
<zorder>sys_menu</zorder>
<zorder>FaceRecogTimeoutEdit</zorder>
</widget>
<widget class="QLabel" name="indicator_Label">
<property name="geometry">

View File

@ -801,36 +801,124 @@ class UIFrameWork(QMainWindow, class_comm_mqtt_interface):
print("密码认证成功")
elif choice == "face":
# face_page = self.parent_window.P05_01_FaceCameraView
# 人脸认证异步处理
face_frame = self.parent_window.P05_01_FaceCameraView
face_frame.face_verify_result = None # 重置标志位
self.do_verify() # 发送 CMD_VERIFY
timeout = system_parameter().get_verify_timeout()
start_time = time.time()
# 创建定时器轮询标志位
self._face_verify_timer = QTimer(self)
self._face_verify_timer.setInterval(50) # 每50ms检查一次
def check_face_result():
nonlocal input
if face_frame.face_verify_result is not None:
self._face_verify_timer.stop()
if face_frame.face_verify_result:
input = True
inform_box = DialogInform()
inform_box.information("提示", "人脸认证成功")
self._after_face_verify(select_object, action_str)
else:
input = False
inform_box = DialogInform()
inform_box.information("提示", "人脸认证失败")
elif time.time() - start_time > timeout:
self._face_verify_timer.stop()
input = False
inform_box = DialogInform()
inform_box.information("提示", "人脸认证超时")
self._face_verify_timer.timeout.connect(check_face_result)
self._face_verify_timer.start()
return # 异步处理,直接返回
else:
# 无认证要求,直接通过
input = True
if input :
if "CmdExecute" in action_str :
# ---------------- 认证成功后执行操作 ----------------
if input:
if "CmdExecute" in action_str:
mqtt_name = get_tip_mqtt_name(tip_str)
unique_name = self.get_unique_name_from_object(select_object)
if mqtt_name != None and unique_name != None:
if len(mqtt_name) > 0 and self.mqtt_thread != None:
if mqtt_name and unique_name and self.mqtt_thread and len(mqtt_name) > 0:
publish_topic = "request/action/" + unique_name
publish_message = '{"name" : "%s"}'%(mqtt_name)
publish_message = '{"name" : "%s"}' % mqtt_name
self.mqtt_publish_and_wait_response(publish_topic, publish_message, select_object, 1000)
elif "ModifySystem" in action_str:
self.on_system_param_modify(tip_str)
elif "Modify" in action_str :
if isinstance(select_object, QLineEdit) :
elif "Modify" in action_str and isinstance(select_object, QLineEdit):
self.on_line_edit_modify_click(select_object)
elif "Users" in action_str :
elif "Users" in action_str:
self.do_manage_users()
elif "Verify" in action_str :
elif "Verify" in action_str:
self.do_verify()
elif "EnrollItgSingle" in action_str :
elif "EnrollItgSingle" in action_str:
self.do_enroll_itg_single()
elif "Reset" in action_str :
elif "Reset" in action_str:
self.reset_command()
elif "ConnectCamera" in action_str:
self.connectcamera()
elif "VideoMode" in action_str:
self.toggle_video_mode()
elif "FaceBox" in action_str:
self.toggle_face_box()
self.virtual_widget_action_process(select_object, action_str)
# ------------------ 后续操作回调 ------------------
def _after_face_verify(self, select_object, action_str):
"""
人脸认证成功后调用,继续执行后续动作
"""
if "CmdExecute" in action_str:
mqtt_name = get_tip_mqtt_name(select_object.statusTip())
unique_name = self.get_unique_name_from_object(select_object)
if mqtt_name and unique_name and self.mqtt_thread and len(mqtt_name) > 0:
publish_topic = "request/action/" + unique_name
publish_message = '{"name" : "%s"}' % mqtt_name
self.mqtt_publish_and_wait_response(publish_topic, publish_message, select_object, 1000)
elif "ModifySystem" in action_str:
self.on_system_param_modify(select_object.statusTip())
elif "Modify" in action_str and isinstance(select_object, QLineEdit):
self.on_line_edit_modify_click(select_object)
elif "Users" in action_str:
self.do_manage_users()
elif "Verify" in action_str:
self.do_verify()
elif "EnrollItgSingle" in action_str:
self.do_enroll_itg_single()
elif "Reset" in action_str:
self.reset_command()
elif "ConnectCamera" in action_str:
self.connectcamera()
elif "VideoMode" in action_str:
self.toggle_video_mode()
elif "FaceBox" in action_str:
self.toggle_face_box()
self.virtual_widget_action_process(select_object, action_str)
def search_menu_match_object(self, object) :
@ -850,6 +938,7 @@ class UIFrameWork(QMainWindow, class_comm_mqtt_interface):
timeout_val = system_parameter().get_verify_timeout()
self.send(build_verify(pd_val, timeout_val))
def do_enroll_itg_single(self):
dlg = EnrollItgSingleDialog(self)
if dlg.exec_() == QDialog.Accepted:
@ -863,6 +952,74 @@ class UIFrameWork(QMainWindow, class_comm_mqtt_interface):
def reset_command(self):
self.send(build_reset())
def connectcamera(self):
running = self.video_worker and self.video_worker.isRunning()
if running:
self.video_worker.stop()
self.video_worker.wait(300)
self.video_worker = None
self.video_label.setText("未打开")
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:
self.video_label.setMinimumSize(360,480)
self.video_worker = VideoWorker(cam_index=0, target_size=self.video_label.size())
self.video_worker.pixmap_ready.connect(self.video_label.setPixmap)
self.video_worker.log_message.connect(self.log)
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(1)
self.current_video_mode=1
self.log("[INFO] 已切换到红外视频模式")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "已切换到红外视频模式")
else:
self.send_uvc(0)
self.current_video_mode=0
self.log("[INFO] 已切换到彩色视频模式")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "已切换到彩色视频模式")
def toggle_face_box(self):
if self.current_face_box==0:
self.send_face_box(1)
self.current_face_box=1
self.log("[INFO] 人脸框已开启")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "人脸框已开启")
else:
self.send_face_box(0)
self.current_face_box=0
self.log("[INFO] 人脸框已关闭")
inform_box : DialogInform = DialogInform()
inform_box.information("提示", "人脸框已关闭")
# ---------------- 发送指令 ----------------
def send_uvc(self, mode):
if self.ser and getattr(self.ser,"is_open",False):
self.ser.write(build_uvc_view(mode))
else:
self.log("[WARN] 串口未连接,无法切换视频模式")
def send_face_box(self, state):
if self.ser and getattr(self.ser,"is_open",False):
self.ser.write(build_face_view(state))
else:
self.log("[WARN] 串口未连接,无法控制人脸框")
################################################################################
#刷新屏幕上的系统信息, 例如时间日期之类
def flush_system_info(self, child = None) :
@ -1009,8 +1166,6 @@ class UIFrameWork(QMainWindow, class_comm_mqtt_interface):
modify_screen_time_str = dialog_modify_text.value
result = frt.set_verify_timeout(modify_screen_time_str)
if result is not None:
self.FaceRecogTimeoutEdit.setText(str(result))
dialog_inform = DialogInform(self)
dialog_inform.information("人脸识别超时时间修改", "人脸识别超时时间修改成功")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 219 KiB

View File

@ -126,6 +126,11 @@ NOTE_NAMES = {
# 帧头同步字
SYNC = b"\xEF\xAA"
#########################################################################################
#########################################################################################
def xor_checksum(data: bytes) -> int:
"""
计算异或校验 (XOR),范围为整个帧的 MsgID + Size + Data 部分,
@ -366,15 +371,15 @@ def parse_reply(data: bytes) -> dict:
info["user_id"] = uid
# 自动生成提示消息
if info.get("ok"):
if info["ok"]:
if mid == CMD_VERIFY and "user_id" in info:
info["message"] = (
f"{info['mid_name']} 成功 - 用户ID: {info['user_id']}"
)
else:
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

View File

@ -1 +1 @@
1,1,2025-09-11 16:21:21
1,1,2025-09-13 10:06:01

1 1 1 2025-09-11 16:21:21 2025-09-13 10:06:01