华荣三照明、合信、荣欣八组合馈电
This commit is contained in:
443
QT5_Project/Shared_CODE/CameraThread.py
Normal file
443
QT5_Project/Shared_CODE/CameraThread.py
Normal file
@ -0,0 +1,443 @@
|
||||
# import sys
|
||||
# import cv2
|
||||
# import os
|
||||
# import time
|
||||
# from PyQt5.QtCore import QThread, pyqtSignal, QObject
|
||||
# import face_recognition
|
||||
|
||||
# current_dir = os.path.dirname(__file__)
|
||||
# share_code_dir = os.path.abspath(os.path.join(current_dir, '..', '..'))
|
||||
# sys.path.append(share_code_dir)
|
||||
|
||||
# from print_color import *
|
||||
|
||||
# DEFAULT_VIDEO_SLEEP_MS = 20
|
||||
|
||||
# # 图像处理线程
|
||||
# class ImageProcessingThread(QThread):
|
||||
# processed_image_signal = pyqtSignal(object)
|
||||
|
||||
# def __init__(self):
|
||||
# super().__init__()
|
||||
|
||||
# def run(self):
|
||||
# while True:
|
||||
# # 在这里添加图像处理代码
|
||||
# # 这里暂时只是将图像传递给下一个线程
|
||||
# time.sleep(0.01)
|
||||
# self.processed_image_signal.emit(None)
|
||||
|
||||
# # 摄像头采集线程
|
||||
# class CameraThread(QThread):
|
||||
# image_signal = pyqtSignal(object)
|
||||
|
||||
# def __init__(self, camera_url, circuit_id: int = 0):
|
||||
# super().__init__()
|
||||
|
||||
# self.camera_url = camera_url # 摄像头url地址, 整数 或者 字符串
|
||||
# self.face_detection: bool = False
|
||||
|
||||
# if isinstance(camera_url, int):
|
||||
# self.camera_url_str = str(camera_url)
|
||||
# else:
|
||||
# self.camera_url_str = camera_url
|
||||
|
||||
# self.cap: cv2.VideoCapture = None
|
||||
# self.running: bool = True
|
||||
|
||||
# self.fps = 0
|
||||
|
||||
# self.cycle_limit = DEFAULT_VIDEO_SLEEP_MS
|
||||
# self.cycle_ms = 0
|
||||
# self.circuit_id = circuit_id # 摄像头对应的回路, 组合开关每一个回路都有对应的摄像头
|
||||
# self.is_signal_connect = False
|
||||
# self.is_emit = False
|
||||
|
||||
# self.set_video_cycle_ms(2000)
|
||||
|
||||
# def signal_connect(self, slot_func):
|
||||
# self.image_signal.connect(slot_func)
|
||||
# self.is_signal_connect = True
|
||||
|
||||
# def signal_disconnect(self):
|
||||
# # 判断信号是否已连接
|
||||
# if self.is_signal_connect:
|
||||
# self.image_signal.disconnect()
|
||||
# self.is_signal_connect = False
|
||||
|
||||
# def set_video_cycle_ms(self, cycle_ms: int = 10):
|
||||
# if self.cycle_limit != cycle_ms:
|
||||
# if cycle_ms <= DEFAULT_VIDEO_SLEEP_MS:
|
||||
# self.cycle_limit = DEFAULT_VIDEO_SLEEP_MS
|
||||
# else:
|
||||
# self.cycle_limit = cycle_ms
|
||||
|
||||
# # 新建函数,改变函数延时方法
|
||||
# def change_camera_url(self, camera_url: str):
|
||||
# if self.circuit_id == camera_url:
|
||||
# self.set_video_cycle_ms(DEFAULT_VIDEO_SLEEP_MS)
|
||||
# else:
|
||||
# self.set_video_cycle_ms(1000)
|
||||
|
||||
# def close(self):
|
||||
# if self.cap == None: # 初始化一直未完成
|
||||
# self.terminate()
|
||||
# self.running = False
|
||||
|
||||
# def run(self):
|
||||
# process_count = 0
|
||||
# fps_time = 0
|
||||
|
||||
# while self.running == True:
|
||||
# inform_msg = "cameral init start, url = " + self.camera_url_str
|
||||
# print_inform_msg(inform_msg)
|
||||
|
||||
# try:
|
||||
# if isinstance(self.camera_url, int):
|
||||
# # 在 Windows 平台下,使用默认的 cv2.VideoCapture 接口
|
||||
# self.cap = cv2.VideoCapture(self.camera_url, cv2.CAP_DSHOW)
|
||||
# else:
|
||||
# # 在 Linux 平台下,使用默认的 cv2.VideoCapture 接口
|
||||
# self.cap = cv2.VideoCapture(self.camera_url)
|
||||
|
||||
# if self.cap != None:
|
||||
# inform_msg = "cameral Init Success, url = " + self.camera_url_str
|
||||
# self.is_emit = True
|
||||
# print_inform_msg(inform_msg)
|
||||
# else:
|
||||
# inform_msg = "cameral connection timeout, url = " + self.camera_url_str
|
||||
# print_inform_msg(inform_msg)
|
||||
|
||||
# except Exception as e:
|
||||
# self.cap = None
|
||||
# inform_msg = "cameral camera Init Fail, url = " + self.camera_url_str
|
||||
# print_error_msg(inform_msg)
|
||||
|
||||
# base_time = time.time()
|
||||
|
||||
# while self.running and self.cap != None:
|
||||
# # 延时20ms
|
||||
# time.sleep(DEFAULT_VIDEO_SLEEP_MS / 1000)
|
||||
# if self.cycle_ms + DEFAULT_VIDEO_SLEEP_MS < self.cycle_limit:
|
||||
# self.cycle_ms += DEFAULT_VIDEO_SLEEP_MS
|
||||
# self.cap.grab() # 抛弃多余的帧,保持最新帧
|
||||
# continue
|
||||
# else:
|
||||
# self.cycle_ms = 0
|
||||
|
||||
# try:
|
||||
# ret, frame = self.cap.read()
|
||||
# cur_time = time.time()
|
||||
|
||||
# execution_time = cur_time - base_time
|
||||
# base_time = cur_time
|
||||
# process_count += 1
|
||||
|
||||
# fps_time += execution_time
|
||||
|
||||
# if fps_time >= 1:
|
||||
# self.fps = process_count
|
||||
# process_count = 0
|
||||
# fps_time = 0
|
||||
|
||||
# if execution_time < 5:
|
||||
# image_object: QObject = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
# self.image_signal.emit(image_object)
|
||||
# else: # 时间差大于5秒, 表示网络可能中断过, 退出并重新连接
|
||||
# err_message = "cameral read timeout, url = " + self.camera_url_str
|
||||
# print_error_msg(err_message)
|
||||
# break
|
||||
# except Exception as e:
|
||||
# err_message = "cameral read timeout, url = " + self.camera_url_str
|
||||
# self.is_emit = False
|
||||
# print_error_msg(err_message)
|
||||
# if self.running == True:
|
||||
# time.sleep(2)
|
||||
# break
|
||||
|
||||
# print_inform_msg("cameral connection End")
|
||||
# time.sleep(0.01)
|
||||
|
||||
# if self.cap != None:
|
||||
# self.cap.release()
|
||||
# self.cap = None
|
||||
# - * - coding:utf - 8 - * -
|
||||
import sys
|
||||
import cv2
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
import face_recognition
|
||||
import numpy as np
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, QObject
|
||||
from print_color import *
|
||||
current_dir = os.path.dirname(__file__)
|
||||
share_code_dir = os.path.abspath(os.path.join(current_dir, '..', '..'))
|
||||
sys.path.append(share_code_dir)
|
||||
|
||||
DEFAULT_VIDEO_SLEEP_MS = 20
|
||||
|
||||
# 定义路径常量
|
||||
npy_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data/face_data.npy'))
|
||||
image_folder_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data'))
|
||||
font_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'font/hanzi.ttc'))
|
||||
def load_face_data():
|
||||
face_data = {}
|
||||
try:
|
||||
face_data = np.load(npy_file_path, allow_pickle=True).item()
|
||||
print_debug_msg(f"Loaded {len(face_data)} face encodings from {npy_file_path}.")
|
||||
except Exception as e:
|
||||
print_error_msg(f"Error loading face encodings: {e}")
|
||||
|
||||
current_images = [f for f in os.listdir(image_folder_path) if f.endswith('.jpg')]
|
||||
current_image_set = set(os.path.splitext(f)[0] for f in current_images)
|
||||
known_image_set = set(face_data.keys())
|
||||
|
||||
added_images = current_image_set - known_image_set
|
||||
removed_images = known_image_set - current_image_set
|
||||
|
||||
for image_name in added_images:
|
||||
image_path = os.path.join(image_folder_path, f"{image_name}.jpg")
|
||||
try:
|
||||
image = face_recognition.load_image_file(image_path)
|
||||
face_encodings = face_recognition.face_encodings(image)
|
||||
if face_encodings:
|
||||
face_data[image_name] = face_encodings[0].tolist()
|
||||
print_debug_msg(f"Added encoding for {image_name}.")
|
||||
else:
|
||||
print_warning_msg(f"[WARNING] No face detected in {image_name}.jpg.")
|
||||
except Exception as e:
|
||||
print_error_msg(f"[ERROR] Error processing {image_name}.jpg: {e}")
|
||||
|
||||
for removed_image in removed_images:
|
||||
del face_data[removed_image]
|
||||
print_debug_msg(f"Removed encoding for {removed_image}.")
|
||||
|
||||
np.save(npy_file_path, face_data)
|
||||
print_debug_msg(f"Updated face data saved with {len(face_data)} entries.")
|
||||
return face_data
|
||||
|
||||
class ImageProcessingThread(QThread):
|
||||
processed_image_signal = pyqtSignal(object)
|
||||
|
||||
def __init__(self, frame=None):
|
||||
super().__init__()
|
||||
self.frame = frame # 添加 frame 参数
|
||||
self.face_encodings = []
|
||||
self.face_names = []
|
||||
self.face_locations = []
|
||||
self.face_data = load_face_data()
|
||||
self.total_image_name = list(self.face_data.keys())
|
||||
self.total_face_encoding = [np.array(self.face_data[name]) for name in self.total_image_name]
|
||||
|
||||
def process_frame(self, frame):
|
||||
"""实时处理帧,匹配已知人脸"""
|
||||
self.face_locations = face_recognition.face_locations(frame)
|
||||
face_encodings = face_recognition.face_encodings(frame, self.face_locations)
|
||||
names = []
|
||||
for face_encoding in face_encodings:
|
||||
name = "Unknown"
|
||||
face_distances = face_recognition.face_distance(self.total_face_encoding, face_encoding)
|
||||
|
||||
if face_distances.size > 0:
|
||||
min_distance = np.min(face_distances)
|
||||
best_match_index = np.argmin(face_distances)
|
||||
|
||||
if min_distance < self.dynamic_tolerance(): # 使用动态容忍度
|
||||
name = self.total_image_name[best_match_index]
|
||||
|
||||
names.append(name)
|
||||
|
||||
self.face_names = names
|
||||
return frame
|
||||
|
||||
def dynamic_tolerance(self):
|
||||
# 动态调整容忍度,可以基于一些规则来调整,如帧内错误率、识别准确率等
|
||||
error_rate = self.calculate_error_rate()
|
||||
if error_rate > 0.1:
|
||||
return 0.5 # 高容忍度以减少错误
|
||||
else:
|
||||
return 0.4 # 适中容忍度
|
||||
|
||||
def calculate_error_rate(self):
|
||||
# 计算识别错误率,作为动态调整容忍度的依据
|
||||
total_faces = len(self.total_face_encoding)
|
||||
unmatched_faces = sum(1 for name in self.face_names if name == "Unknown")
|
||||
return unmatched_faces / total_faces if total_faces > 0 else 0
|
||||
|
||||
def set_frame(self, frame):
|
||||
self.frame = frame
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
if self.frame is not None:
|
||||
self.process_frame(self.frame)
|
||||
self.processed_image_signal.emit(self.frame)
|
||||
time.sleep(0.01) # 控制帧率
|
||||
|
||||
|
||||
# 摄像头采集线程
|
||||
class CameraThread(QThread):
|
||||
image_signal = pyqtSignal(object)
|
||||
|
||||
def __init__(self, camera_url, circuit_id: int = 0):
|
||||
super().__init__()
|
||||
self.camera_url = camera_url
|
||||
self.face_detection: bool = True
|
||||
|
||||
if isinstance(camera_url, int):
|
||||
self.camera_url_str = str(camera_url)
|
||||
else:
|
||||
self.camera_url_str = camera_url
|
||||
|
||||
self.cap: cv2.VideoCapture = None
|
||||
self.running: bool = True
|
||||
|
||||
self.fps = 0
|
||||
self.frame_count = 0
|
||||
self.fps_time = 0
|
||||
|
||||
self.cycle_limit = DEFAULT_VIDEO_SLEEP_MS
|
||||
self.cycle_ms = 0
|
||||
self.circuit_id = circuit_id
|
||||
self.is_signal_connect = False
|
||||
self.is_emit = False
|
||||
|
||||
self.set_video_cycle_ms(2000)
|
||||
|
||||
def signal_connect(self, slot_func):
|
||||
self.image_signal.connect(slot_func)
|
||||
self.is_signal_connect = True
|
||||
|
||||
def signal_disconnect(self):
|
||||
if self.is_signal_connect:
|
||||
self.image_signal.disconnect()
|
||||
self.is_signal_connect = False
|
||||
|
||||
def set_video_cycle_ms(self, cycle_ms: int = 10):
|
||||
if self.cycle_limit != cycle_ms:
|
||||
if cycle_ms <= DEFAULT_VIDEO_SLEEP_MS:
|
||||
self.cycle_limit = DEFAULT_VIDEO_SLEEP_MS
|
||||
else:
|
||||
self.cycle_limit = cycle_ms
|
||||
|
||||
def change_camera_url(self, camera_url: str):
|
||||
if self.circuit_id == camera_url:
|
||||
self.set_video_cycle_ms(DEFAULT_VIDEO_SLEEP_MS)
|
||||
else:
|
||||
self.set_video_cycle_ms(1000)
|
||||
|
||||
def close(self):
|
||||
if self.cap is None:
|
||||
self.terminate()
|
||||
self.running = False
|
||||
|
||||
def run(self):
|
||||
process_count = 0
|
||||
fps_time = 0
|
||||
face_thread = ImageProcessingThread()
|
||||
face_thread.start()
|
||||
|
||||
self.match_count = 0 # 初始化匹配帧数计数器
|
||||
|
||||
while self.running:
|
||||
inform_msg = "Camera init start, URL = " + self.camera_url_str
|
||||
print_inform_msg(inform_msg)
|
||||
|
||||
try:
|
||||
self.cap = cv2.VideoCapture(self.camera_url) # 强制使用 BGR 格式
|
||||
if self.cap.isOpened():
|
||||
inform_msg = "Camera Init Success, URL = " + self.camera_url_str
|
||||
self.is_emit = True
|
||||
print_inform_msg(inform_msg)
|
||||
else:
|
||||
inform_msg = "Camera connection timeout, URL = " + self.camera_url_str
|
||||
print_error_msg(inform_msg)
|
||||
|
||||
except Exception as e:
|
||||
self.cap = None
|
||||
inform_msg = "Camera Init Fail, URL = " + self.camera_url_str
|
||||
print_error_msg(inform_msg)
|
||||
|
||||
base_time = time.time()
|
||||
|
||||
while self.running and self.cap:
|
||||
time.sleep(DEFAULT_VIDEO_SLEEP_MS / 1000)
|
||||
|
||||
if self.cycle_ms + DEFAULT_VIDEO_SLEEP_MS < self.cycle_limit:
|
||||
self.cycle_ms += DEFAULT_VIDEO_SLEEP_MS
|
||||
self.cap.grab()
|
||||
continue
|
||||
else:
|
||||
self.cycle_ms = 0
|
||||
|
||||
try:
|
||||
ret, frame = self.cap.read()
|
||||
if not ret:
|
||||
break
|
||||
|
||||
face_thread.set_frame(frame)
|
||||
|
||||
cur_time = time.time()
|
||||
execution_time = cur_time - base_time
|
||||
base_time = cur_time
|
||||
process_count += 1
|
||||
|
||||
fps_time += execution_time
|
||||
if fps_time >= 1: # 每隔1秒重新计算 FPS
|
||||
self.fps = process_count
|
||||
process_count = 0
|
||||
fps_time = 0
|
||||
|
||||
if execution_time < 5:
|
||||
frame: QObject = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
for (top, right, bottom, left), name in zip(face_thread.face_locations, face_thread.face_names):
|
||||
if name != "Unknown": # 只绘制已匹配的结果
|
||||
self.match_count += 1 # 计数成功匹配的帧
|
||||
# cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
|
||||
# cv2.putText(frame, name, (left + 6, bottom - 6), cv2.FONT_HERSHEY_DUPLEX, 1.0, (255, 255, 255), 1)
|
||||
|
||||
# 将OpenCV图像转换为PIL图像格式(注意要转换色彩空间,OpenCV是BGR,PIL是RGB)
|
||||
pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
|
||||
draw = ImageDraw.Draw(pil_image)
|
||||
# 这里选择合适的中文字体文件路径,比如系统自带的宋体(不同系统路径可能有差异),并设置字体大小
|
||||
font = ImageFont.truetype(font_path, 15)
|
||||
# 在指定位置绘制中文
|
||||
draw.text((left + 12, bottom + 12), name, font=font, fill=(255, 255, 255))
|
||||
# 再将PIL图像转换回OpenCV图像格式
|
||||
frame = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
|
||||
cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
|
||||
else:
|
||||
name = "未在人脸库中,无操作权限"
|
||||
pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
|
||||
draw = ImageDraw.Draw(pil_image)
|
||||
#设置字体大小
|
||||
font = ImageFont.truetype(font_path, 15)
|
||||
# 在指定位置绘制中文
|
||||
draw.text((left + 12, bottom + 12), name, font=font, fill=(255, 255, 255))
|
||||
# 再将PIL图像转换回OpenCV图像格式
|
||||
frame = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
|
||||
cv2.rectangle(frame, (left, top), (right, bottom), (255, 0, 0), 2)
|
||||
self.image_signal.emit(frame)
|
||||
else:
|
||||
err_message = "Camera read timeout, URL = " + self.camera_url_str
|
||||
print_error_msg(err_message)
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
err_message = "Camera read exception, URL = " + self.camera_url_str
|
||||
print_error_msg(err_message)
|
||||
if self.running:
|
||||
time.sleep(2)
|
||||
break
|
||||
|
||||
if self.cap:
|
||||
self.cap.release()
|
||||
self.cap = None
|
||||
|
||||
print_inform_msg("Camera connection ended")
|
||||
time.sleep(0.01)
|
||||
|
||||
|
||||
|
||||
67
QT5_Project/Shared_CODE/DialogInform.py
Normal file
67
QT5_Project/Shared_CODE/DialogInform.py
Normal file
@ -0,0 +1,67 @@
|
||||
import sys
|
||||
import os
|
||||
from PyQt5.QtWidgets import QApplication, QDialog, QLabel, QLineEdit, QWidget, QPushButton, QMessageBox, QComboBox, QDialogButtonBox, QShortcut
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtCore import Qt, QEvent, QObject, QTimer
|
||||
|
||||
|
||||
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_file_path = os.path.join(ui_path, "DialogInform.ui")
|
||||
|
||||
class DialogInform(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super(DialogInform, self).__init__(parent)
|
||||
# Load UI file
|
||||
uic.loadUi(ui_file_path, self)
|
||||
label :QLabel = self.label
|
||||
label.setText("")
|
||||
label.setWordWrap(True) # 允许文本自动换行
|
||||
|
||||
|
||||
self.message_timer = QTimer()
|
||||
self.message_timer.timeout.connect(self.close_dialog_timeout)
|
||||
|
||||
self.setWindowTitle("消息提示")
|
||||
self.setWindowFlag(Qt.FramelessWindowHint)
|
||||
button_box : QDialogButtonBox = self.buttonBox
|
||||
ok_button = button_box.button(QDialogButtonBox.Ok)
|
||||
if ok_button:
|
||||
ok_button.setText('确定')
|
||||
else:
|
||||
button_box.addButton(QDialogButtonBox.Ok)
|
||||
#定义4个快捷键, Key_Enter, Key_Escape经常被各类程序模块使用, 用Key_End, 与Key_Home来代替
|
||||
QShortcut(QKeySequence(Qt.Key_PageDown), self, activated=self.key_enter_process)
|
||||
QShortcut(QKeySequence(Qt.Key_PageUp), self, activated=self.key_escape_process)
|
||||
QShortcut(QKeySequence(Qt.Key_End), self, activated=self.key_enter_process)
|
||||
QShortcut(QKeySequence(Qt.Key_Home), self, activated=self.key_escape_process)
|
||||
|
||||
def information(self, title : str, message : str, timeout_ms : int = 2000) :
|
||||
self.setWindowTitle(title)
|
||||
label :QLabel = self.label
|
||||
label.setText(message)
|
||||
self.message_timer.start(timeout_ms)
|
||||
self.exec()
|
||||
|
||||
def key_enter_process(self):
|
||||
button_box : QDialogButtonBox = self.buttonBox
|
||||
select_button = button_box.button(QDialogButtonBox.Ok)
|
||||
if select_button:
|
||||
select_button.click()
|
||||
|
||||
def key_escape_process(self):
|
||||
button_box : QDialogButtonBox = self.buttonBox
|
||||
select_button = button_box.button(QDialogButtonBox.Cancel)
|
||||
if select_button:
|
||||
select_button.click()
|
||||
|
||||
#消息超时
|
||||
def close_dialog_timeout(self):
|
||||
self.key_enter_process()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
dialog = DialogInform()
|
||||
dialog.exec()
|
||||
sys.exit(0)
|
||||
147
QT5_Project/Shared_CODE/DialogModifyAlias.py
Normal file
147
QT5_Project/Shared_CODE/DialogModifyAlias.py
Normal file
@ -0,0 +1,147 @@
|
||||
import sys
|
||||
import os
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QDialog, QLabel, QVBoxLayout, QPushButton, QMessageBox, QComboBox, QDialogButtonBox, QShortcut
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtCore import Qt, QEvent, QObject
|
||||
import json
|
||||
# from print_color 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'))
|
||||
ui_file_path = os.path.join(ui_path, "DialogModifyAlias.ui")
|
||||
|
||||
class DialogModifyAlias(QDialog):
|
||||
def __init__(self, main_window, parent=None):
|
||||
super(DialogModifyAlias, self).__init__(parent)
|
||||
# Load UI file
|
||||
uic.loadUi(ui_file_path, self)
|
||||
combo : QComboBox = self.comboBox
|
||||
|
||||
combo.clear()
|
||||
self.alias_item_dict = {}
|
||||
self.main_window = main_window
|
||||
|
||||
self.setWindowTitle("别名参数修改")
|
||||
self.setWindowFlag(Qt.FramelessWindowHint)
|
||||
self.buttonBox.button(QDialogButtonBox.Ok).setText('确定')
|
||||
self.buttonBox.button(QDialogButtonBox.Cancel).setText('取消')
|
||||
self.buttonBox.clicked.connect(self.dialog_button_ok_clicked)
|
||||
|
||||
#定义4个快捷键, Key_Enter, Key_Escape经常被各类程序模块使用, 用Key_End, 与Key_Home来代替
|
||||
QShortcut(QKeySequence(Qt.Key_Up), self, activated=self.key_increase_parameter)
|
||||
QShortcut(QKeySequence(Qt.Key_Down), self, activated=self.key_decrease_parameter)
|
||||
QShortcut(QKeySequence(Qt.Key_PageDown), self, activated=self.key_enter_process)
|
||||
QShortcut(QKeySequence(Qt.Key_PageUp), self, activated=self.key_escape_process)
|
||||
QShortcut(QKeySequence(Qt.Key_End), self, activated=self.key_enter_process)
|
||||
QShortcut(QKeySequence(Qt.Key_Home), self, activated=self.key_escape_process)
|
||||
|
||||
home_button = self.findChild(QPushButton, "KEY_HOME")
|
||||
if home_button:
|
||||
home_button.clicked.connect(self.key_escape_process)
|
||||
|
||||
end_button = self.findChild(QPushButton, "KEY_END")
|
||||
if end_button:
|
||||
end_button.clicked.connect(self.key_enter_process)
|
||||
|
||||
up_button = self.findChild(QPushButton, "KEY_UP")
|
||||
if up_button:
|
||||
up_button.clicked.connect(self.key_increase_parameter)
|
||||
|
||||
down_button = self.findChild(QPushButton, "KEY_DOWN")
|
||||
if down_button:
|
||||
down_button.clicked.connect(self.key_decrease_parameter)
|
||||
|
||||
def set_caption_name(self, name : str) :
|
||||
label : QLabel = self.label
|
||||
label.setText(name)
|
||||
|
||||
def key_increase_parameter(self):
|
||||
combo : QComboBox = self.comboBox
|
||||
index = combo.currentIndex()
|
||||
index += 1
|
||||
if index >= combo.count() :
|
||||
index = 0
|
||||
combo.setCurrentIndex(index)
|
||||
|
||||
def key_decrease_parameter(self):
|
||||
combo : QComboBox = self.comboBox
|
||||
index = combo.currentIndex()
|
||||
index -= 1
|
||||
if index < 0 :
|
||||
index = combo.count() - 1
|
||||
combo.setCurrentIndex(index)
|
||||
|
||||
def get_alias_key_str(self):
|
||||
combo : QComboBox = self.comboBox
|
||||
select_text = combo.currentText()
|
||||
alias_key = None
|
||||
for alias_key, alias_str in self.alias_item_dict.items() :
|
||||
if alias_str == select_text :
|
||||
return alias_key
|
||||
return alias_key
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
alias_key = self.get_alias_key_str()
|
||||
return alias_key
|
||||
|
||||
def modify_alias_value(self, modify_str) :
|
||||
if self.main_window != None :
|
||||
if hasattr(self.main_window, "func_call_back_on_mqtt_param_modify") :
|
||||
self.main_window.func_call_back_on_mqtt_param_modify(modify_str)
|
||||
|
||||
def key_enter_process(self):
|
||||
alias_key = self.get_alias_key_str()
|
||||
self.modify_alias_value(alias_key)
|
||||
self.buttonBox.button(QDialogButtonBox.Ok).click()
|
||||
|
||||
def key_escape_process(self):
|
||||
self.buttonBox.button(QDialogButtonBox.Cancel).click()
|
||||
|
||||
def set_alias_item_info(self, alias_item_dict, key_value, inform_message : str = "系统参数") :
|
||||
combo : QComboBox = self.comboBox
|
||||
|
||||
self.alias_item_dict = alias_item_dict
|
||||
combo.clear()
|
||||
current_index = 0
|
||||
select_index = -1
|
||||
|
||||
self.set_caption_name(inform_message)
|
||||
|
||||
for key, value_str in alias_item_dict.items() :
|
||||
combo.addItem(value_str)
|
||||
if key_value == key :
|
||||
select_index = current_index
|
||||
current_index += 1
|
||||
|
||||
if select_index >= 0 :
|
||||
combo.setCurrentIndex(select_index)
|
||||
else :
|
||||
combo.setCurrentText(key_value)
|
||||
|
||||
def dialog_button_ok_clicked(self, button):
|
||||
if button == self.buttonBox.button(QDialogButtonBox.Ok) :
|
||||
alias_key = self.get_alias_key_str()
|
||||
self.modify_alias_value(alias_key)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
alias_select = {0 : "380V", 1 : "660V", 2: "1140V", 3 : "3300V"}
|
||||
json_msg : str = json.dumps(alias_select, ensure_ascii=False)
|
||||
# print_inform_msg(json_msg)
|
||||
|
||||
alias_2 = {"alias_voltage" : {0 : "380V", 1 : "660V", 2: "1140V", 3 : "3300V"}}
|
||||
json_msg2 : str = json.dumps(alias_2, ensure_ascii=False)
|
||||
# print_inform_msg(json_msg2)
|
||||
|
||||
|
||||
dialog = DialogModifyAlias(None)
|
||||
dialog.set_alias_item_info(alias_select, 1)
|
||||
dialog.show()
|
||||
result = dialog.exec_()
|
||||
|
||||
# if dialog.result() == QDialog.Accepted:
|
||||
# print_inform_msg(dialog.get_alias_key_str()) # 输出: Button clicked
|
||||
|
||||
sys.exit(0)
|
||||
155
QT5_Project/Shared_CODE/DialogModifyText.py
Normal file
155
QT5_Project/Shared_CODE/DialogModifyText.py
Normal file
@ -0,0 +1,155 @@
|
||||
import sys
|
||||
import os
|
||||
from PyQt5.QtWidgets import QApplication, QDialog, QLineEdit, QLabel, QWidget, QPushButton, QMessageBox, QComboBox, QDialogButtonBox, QShortcut
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtCore import Qt, QEvent, QObject, QTimer
|
||||
from Shared_CODE.get_tip_prop 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'))
|
||||
ui_file_path = os.path.join(ui_path, "DialogModifyText.ui")
|
||||
|
||||
def modify_string(string, index, new_char):
|
||||
string_list = list(string)
|
||||
string_list[index] = new_char
|
||||
modified_string = ''.join(string_list)
|
||||
return modified_string
|
||||
|
||||
def chr_adjust(old_chr, dif : int) :
|
||||
new_chr = old_chr
|
||||
if old_chr == '+':
|
||||
new_chr = '-'
|
||||
elif old_chr == '-':
|
||||
new_chr = '+'
|
||||
elif dif > 0:
|
||||
new_chr = chr(ord(old_chr) + 1)
|
||||
if new_chr > '9' :
|
||||
new_chr = '0'
|
||||
else :
|
||||
new_chr = chr(ord(old_chr) - 1)
|
||||
if new_chr < '0' :
|
||||
new_chr = '9'
|
||||
return new_chr
|
||||
|
||||
class DialogModifyText(QDialog):
|
||||
def __init__(self, main_window, parent=None):
|
||||
super(DialogModifyText, self).__init__(parent)
|
||||
# Load UI file
|
||||
uic.loadUi(ui_file_path, self)
|
||||
|
||||
self.Text = "2019-09-10 10:22:23"
|
||||
self.select_bit = 0
|
||||
self.main_window = main_window
|
||||
|
||||
self.setWindowTitle("参数修改")
|
||||
self.setWindowFlag(Qt.FramelessWindowHint)
|
||||
self.buttonBox.button(QDialogButtonBox.Ok).setText(' ')
|
||||
self.buttonBox.button(QDialogButtonBox.Cancel).setText(' ')
|
||||
|
||||
self.buttonBox.clicked.connect(self.dialog_button_ok_clicked)
|
||||
|
||||
modify_text = self.Text
|
||||
self.update_modify_info(modify_text, "缺省多字符修改")
|
||||
|
||||
#让定时器周期刷修改字符串, 防止出现焦点切换时选中消失的情况
|
||||
self.timer = QTimer(self)
|
||||
self.timer.timeout.connect(self.timeout_modify_string_refresh)
|
||||
self.timer.start(100)
|
||||
|
||||
#定义4个快捷键, Key_Enter, Key_Escape经常被各类程序模块使用, 用Key_End, 与Key_Home来代替
|
||||
QShortcut(QKeySequence(Qt.Key_Up), self, activated=self.key_increase_parameter)
|
||||
QShortcut(QKeySequence(Qt.Key_Down), self, activated=self.key_decrease_parameter)
|
||||
# QShortcut(QKeySequence(Qt.Key_PageDown), self, activated=self.key_enter_process)
|
||||
# QShortcut(QKeySequence(Qt.Key_PageUp), self, activated=self.key_escape_process)
|
||||
QShortcut(QKeySequence(Qt.Key_End), self, activated=self.key_enter_process)
|
||||
QShortcut(QKeySequence(Qt.Key_Home), self, activated=self.key_escape_process)
|
||||
|
||||
home_button = self.findChild(QPushButton, "KEY_HOME")
|
||||
if home_button:
|
||||
home_button.clicked.connect(self.key_escape_process)
|
||||
|
||||
end_button = self.findChild(QPushButton, "KEY_END")
|
||||
if end_button:
|
||||
end_button.clicked.connect(self.key_enter_process)
|
||||
|
||||
up_button = self.findChild(QPushButton, "KEY_UP")
|
||||
if up_button:
|
||||
up_button.clicked.connect(self.key_increase_parameter)
|
||||
|
||||
down_button = self.findChild(QPushButton, "KEY_DOWN")
|
||||
if down_button:
|
||||
down_button.clicked.connect(self.key_decrease_parameter)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.Text
|
||||
|
||||
def timeout_modify_string_refresh(self) :
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
modify_str : str = line_modify.text()
|
||||
self.update_modify_string(modify_str)
|
||||
|
||||
def update_modify_info(self, modify_str, caption : str) :
|
||||
if hasattr(self, "label") :
|
||||
label : QLabel = self.label
|
||||
label.setText(caption)
|
||||
self.update_modify_string(modify_str)
|
||||
|
||||
def update_modify_string(self, modify_str : str) :
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
line_modify.setText(modify_str)
|
||||
|
||||
if self.select_bit < len(modify_str) :
|
||||
if modify_str[self.select_bit] == ' ':
|
||||
self.key_enter_process()
|
||||
|
||||
# 设置选择从第self.select_bit个字符开始,长度为1
|
||||
line_modify.setSelection(self.select_bit, 1)
|
||||
|
||||
def key_increase_parameter(self):
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
text : str = line_modify.text()
|
||||
if self.select_bit < len(text) :
|
||||
old_chr = text[self.select_bit]
|
||||
new_chr = chr_adjust(old_chr, 1)
|
||||
new_modify_text = modify_string(text, self.select_bit, new_chr)
|
||||
self.update_modify_string(new_modify_text)
|
||||
|
||||
def key_decrease_parameter(self):
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
text : str = line_modify.text()
|
||||
if self.select_bit < len(text) :
|
||||
old_chr = text[self.select_bit]
|
||||
new_chr = chr_adjust(old_chr, -1)
|
||||
new_modify_text = modify_string(text, self.select_bit, new_chr)
|
||||
self.update_modify_string(new_modify_text)
|
||||
|
||||
|
||||
def key_enter_process(self):
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
text : str = line_modify.text()
|
||||
|
||||
if self.select_bit < len(text) - 1:
|
||||
self.select_bit += 1
|
||||
if not str.isdigit(text[self.select_bit]) :
|
||||
self.key_enter_process()
|
||||
else :
|
||||
self.update_modify_string(text)
|
||||
else :
|
||||
self.buttonBox.button(QDialogButtonBox.Ok).click()
|
||||
|
||||
def key_escape_process(self):
|
||||
self.buttonBox.button(QDialogButtonBox.Cancel).click()
|
||||
|
||||
def dialog_button_ok_clicked(self, button):
|
||||
if button == self.buttonBox.button(QDialogButtonBox.Ok) :
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
self.Text = line_modify.text()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
dialog = DialogModifyText(None)
|
||||
dialog.show()
|
||||
dialog.exec_()
|
||||
sys.exit(0)
|
||||
175
QT5_Project/Shared_CODE/DialogModifyValue.py
Normal file
175
QT5_Project/Shared_CODE/DialogModifyValue.py
Normal file
@ -0,0 +1,175 @@
|
||||
import sys
|
||||
import os
|
||||
from PyQt5.QtWidgets import QApplication, QDialog, QLineEdit, QLabel, QWidget, QPushButton, QMessageBox, QComboBox, QDialogButtonBox, QShortcut
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtCore import Qt, QEvent, QObject, QTimer
|
||||
|
||||
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_file_path = os.path.join(ui_path, "DialogModifyValue.ui")
|
||||
|
||||
def modify_string(string, index, new_char):
|
||||
string_list = list(string)
|
||||
string_list[index] = new_char
|
||||
modified_string = ''.join(string_list)
|
||||
return modified_string
|
||||
|
||||
def chr_adjust(old_chr, dif : int) :
|
||||
new_chr = old_chr
|
||||
if old_chr == '+':
|
||||
new_chr = '-'
|
||||
elif old_chr == '-':
|
||||
new_chr = '+'
|
||||
elif dif > 0:
|
||||
new_chr = chr(ord(old_chr) + 1)
|
||||
if new_chr > '9' :
|
||||
new_chr = '0'
|
||||
else :
|
||||
new_chr = chr(ord(old_chr) - 1)
|
||||
if new_chr < '0' :
|
||||
new_chr = '9'
|
||||
return new_chr
|
||||
|
||||
|
||||
class DialogModifyValue(QDialog):
|
||||
def __init__(self, main_window, parent=None):
|
||||
super(DialogModifyValue, self).__init__(parent)
|
||||
# Load UI file
|
||||
uic.loadUi(ui_file_path, self)
|
||||
|
||||
self.value_origin = 1000.3
|
||||
self.value_modify = 1000.3
|
||||
self.format = "%05.1f"
|
||||
self.scale = 1.0
|
||||
self.offset = 0.0
|
||||
self.select_bit = 0
|
||||
self.main_window = main_window
|
||||
|
||||
self.setWindowTitle("参数修改")
|
||||
self.setWindowFlag(Qt.FramelessWindowHint)
|
||||
ok_button = self.buttonBox.button(QDialogButtonBox.Ok)
|
||||
if ok_button is None:
|
||||
pass
|
||||
else:
|
||||
ok_button.setText('')
|
||||
Cancel_button = self.buttonBox.button(QDialogButtonBox.Cancel)
|
||||
if Cancel_button is None:
|
||||
pass
|
||||
else:
|
||||
Cancel_button.setText('')
|
||||
|
||||
self.buttonBox.clicked.connect(self.dialog_button_ok_clicked)
|
||||
|
||||
modify_value = self.value_origin * self.scale + self.offset
|
||||
self.update_modify_info(self.format, modify_value, "缺省参数修改", self.offset)
|
||||
|
||||
#让定时器周期刷修改字符串, 防止出现焦点切换时选中消失的情况
|
||||
self.timer = QTimer(self)
|
||||
self.timer.timeout.connect(self.timeout_modify_string_refresh)
|
||||
self.timer.start(100)
|
||||
|
||||
#定义4个快捷键, Key_Enter, Key_Escape经常被各类程序模块使用, 用Key_End, 与Key_Home来代替
|
||||
QShortcut(QKeySequence(Qt.Key_Up), self, activated=self.key_increase_parameter)
|
||||
QShortcut(QKeySequence(Qt.Key_Down), self, activated=self.key_decrease_parameter)
|
||||
QShortcut(QKeySequence(Qt.Key_PageDown), self, activated=self.key_enter_process)
|
||||
QShortcut(QKeySequence(Qt.Key_PageUp), self, activated=self.key_escape_process)
|
||||
QShortcut(QKeySequence(Qt.Key_End), self, activated=self.key_enter_process)
|
||||
QShortcut(QKeySequence(Qt.Key_Home), self, activated=self.key_escape_process)
|
||||
|
||||
home_button = self.findChild(QPushButton, "KEY_HOME")
|
||||
if home_button:
|
||||
home_button.clicked.connect(self.key_escape_process)
|
||||
|
||||
end_button = self.findChild(QPushButton, "KEY_END")
|
||||
if end_button:
|
||||
end_button.clicked.connect(self.key_enter_process)
|
||||
|
||||
up_button = self.findChild(QPushButton, "KEY_UP")
|
||||
if up_button:
|
||||
up_button.clicked.connect(self.key_increase_parameter)
|
||||
|
||||
down_button = self.findChild(QPushButton, "KEY_DOWN")
|
||||
if down_button:
|
||||
down_button.clicked.connect(self.key_decrease_parameter)
|
||||
@property
|
||||
def value(self):
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
text : str = line_modify.text()
|
||||
return text
|
||||
|
||||
def timeout_modify_string_refresh(self) :
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
modify_str : str = line_modify.text()
|
||||
self.update_modify_string(modify_str)
|
||||
|
||||
def update_modify_info(self, format_str, value, caption : str, offset = 0) :
|
||||
self.value_origin = value
|
||||
self.format = format_str
|
||||
self.offset = offset
|
||||
if hasattr(self, "label") :
|
||||
label : QLabel = self.label
|
||||
label.setText(caption)
|
||||
if self.format != "" :
|
||||
modify_str = self.format%(value)
|
||||
else :
|
||||
modify_str = str(value)
|
||||
self.update_modify_string(modify_str)
|
||||
|
||||
def update_modify_string(self, modify_str : str) :
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
line_modify.setText(modify_str)
|
||||
|
||||
if self.select_bit < len(modify_str) :
|
||||
if modify_str[self.select_bit] == ' ':
|
||||
self.key_enter_process()
|
||||
|
||||
# 设置选择从第self.select_bit个字符开始,长度为1
|
||||
line_modify.setSelection(self.select_bit, 1)
|
||||
|
||||
def key_increase_parameter(self):
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
text : str = line_modify.text()
|
||||
if self.select_bit < len(text) :
|
||||
old_chr = text[self.select_bit]
|
||||
new_chr = chr_adjust(old_chr, 1)
|
||||
new_modify_text = modify_string(text, self.select_bit, new_chr)
|
||||
self.update_modify_string(new_modify_text)
|
||||
|
||||
def key_decrease_parameter(self):
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
text : str = line_modify.text()
|
||||
if self.select_bit < len(text) :
|
||||
old_chr = text[self.select_bit]
|
||||
new_chr = chr_adjust(old_chr, -1)
|
||||
new_modify_text = modify_string(text, self.select_bit, new_chr)
|
||||
self.update_modify_string(new_modify_text)
|
||||
|
||||
def key_enter_process(self):
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
text : str = line_modify.text()
|
||||
|
||||
if self.select_bit < len(text) - 1:
|
||||
self.select_bit += 1
|
||||
if not str.isdigit(text[self.select_bit]) :
|
||||
self.key_enter_process()
|
||||
else :
|
||||
self.update_modify_string(text)
|
||||
else :
|
||||
self.buttonBox.button(QDialogButtonBox.Ok).click()
|
||||
|
||||
def key_escape_process(self):
|
||||
self.buttonBox.button(QDialogButtonBox.Cancel).click()
|
||||
|
||||
def dialog_button_ok_clicked(self, button):
|
||||
if button == self.buttonBox.button(QDialogButtonBox.Ok) :
|
||||
line_modify : QLineEdit = self.lineEdit
|
||||
text : str = line_modify.text()
|
||||
self.value_modify = float(text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
dialog = DialogModifyValue()
|
||||
dialog.show()
|
||||
|
||||
sys.exit(dialog.exec_())
|
||||
5
QT5_Project/Shared_CODE/__init__.py
Normal file
5
QT5_Project/Shared_CODE/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
import sys
|
||||
sys_path = sys.path[0].replace("\\", "/")
|
||||
sys_path_linux = sys_path + "/../.."
|
||||
if sys_path_linux not in sys.path :
|
||||
sys.path.append(sys_path_linux)
|
||||
BIN
QT5_Project/Shared_CODE/__pycache__/CameraThread.cpython-311.pyc
Normal file
BIN
QT5_Project/Shared_CODE/__pycache__/CameraThread.cpython-311.pyc
Normal file
Binary file not shown.
BIN
QT5_Project/Shared_CODE/__pycache__/DialogInform.cpython-311.pyc
Normal file
BIN
QT5_Project/Shared_CODE/__pycache__/DialogInform.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
QT5_Project/Shared_CODE/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
QT5_Project/Shared_CODE/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
QT5_Project/Shared_CODE/__pycache__/get_tip_prop.cpython-311.pyc
Normal file
BIN
QT5_Project/Shared_CODE/__pycache__/get_tip_prop.cpython-311.pyc
Normal file
Binary file not shown.
BIN
QT5_Project/Shared_CODE/face_data/MC.jpg
Normal file
BIN
QT5_Project/Shared_CODE/face_data/MC.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
BIN
QT5_Project/Shared_CODE/face_data/SJX.jpg
Normal file
BIN
QT5_Project/Shared_CODE/face_data/SJX.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
BIN
QT5_Project/Shared_CODE/face_data/face_data.npy
Normal file
BIN
QT5_Project/Shared_CODE/face_data/face_data.npy
Normal file
Binary file not shown.
BIN
QT5_Project/Shared_CODE/font/hanzi.ttc
Normal file
BIN
QT5_Project/Shared_CODE/font/hanzi.ttc
Normal file
Binary file not shown.
219
QT5_Project/Shared_CODE/get_tip_prop.py
Normal file
219
QT5_Project/Shared_CODE/get_tip_prop.py
Normal file
@ -0,0 +1,219 @@
|
||||
from print_color import *
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
def get_value_from_lead_value_str(lead_value_str : str, lead_str, default_value = None) :
|
||||
value = default_value
|
||||
if lead_str in lead_value_str :
|
||||
value_str = lead_value_str.replace(lead_str, "")
|
||||
value_str = value_str.replace("=", "")
|
||||
|
||||
try :
|
||||
value = int(value_str)
|
||||
except Exception as e :
|
||||
pass
|
||||
return value
|
||||
|
||||
def get_tip_value_str(tip_str : str, key_str : str, default_string : str = None) :
|
||||
value_str = default_string
|
||||
if len(tip_str) == 0 :
|
||||
return value_str
|
||||
|
||||
tip_split_strs = tip_str.split(",")
|
||||
for prop_str in tip_split_strs :
|
||||
if key_str in prop_str :
|
||||
prop_split_strs = prop_str.split("=")
|
||||
full_cmp_str : str = prop_split_strs[0].replace(" ", "")
|
||||
if len(prop_split_strs) >= 2 and full_cmp_str == key_str:
|
||||
value_str = prop_split_strs[1]
|
||||
return value_str
|
||||
|
||||
def get_tip_value(tip_str : str, key_str : str, default_value : int) :
|
||||
value_str = get_tip_value_str(tip_str, key_str)
|
||||
value = default_value
|
||||
try :
|
||||
value = int(value_str)
|
||||
except Exception as e :
|
||||
value = default_value
|
||||
return value
|
||||
|
||||
def get_tip_visible_mqtt(tip_str : str, default_mqtt : str) :
|
||||
value_str = get_tip_value_str(tip_str, "Visible", default_string = default_mqtt)
|
||||
return value_str
|
||||
|
||||
def get_tip_face_detection(tip_str : str) :
|
||||
return get_tip_value(tip_str, "FaceDetect", default_value = 0)
|
||||
|
||||
def get_tip_frame_box(tip_str : str) :
|
||||
return get_tip_value(tip_str, "SelectBox", default_value = 0)
|
||||
|
||||
def get_tip_page(tip_str : str) :
|
||||
return get_tip_value(tip_str, "Page", default_value = -1)
|
||||
|
||||
def get_tip_caption_str(tip_str : str) :
|
||||
return get_tip_value_str(tip_str, "Caption", default_string = "")
|
||||
|
||||
def get_tip_alias_str(tip_str : str) :
|
||||
return get_tip_value_str(tip_str, "Alias", default_string = None)
|
||||
|
||||
def get_tip_menu_sub_index(tip_str : str) :
|
||||
return get_tip_value(tip_str, "Index", default_value = -1)
|
||||
|
||||
def get_tip_timeout(tip_str : str) :
|
||||
return get_tip_value(tip_str, "Timeout", default_value = -1)
|
||||
|
||||
def get_tip_caption_index(tip_str : str) :
|
||||
return get_tip_value(tip_str, "CaptionIndex", default_value = -1)
|
||||
|
||||
def get_tip_format(tip_str : str) :
|
||||
return get_tip_value_str(tip_str, "Format", default_string = "%1.0f")
|
||||
|
||||
def get_tip_circuit(tip_str : str) :
|
||||
return get_tip_value(tip_str, "circuit", default_value = -1)
|
||||
|
||||
def get_tip_main_index(tip_str : str) :
|
||||
return get_tip_value(tip_str, "main", default_value = -1)
|
||||
|
||||
def get_tip_binding_circuit(tip_str : str) :
|
||||
return get_tip_value(tip_str, "bindingcircuit", default_value = -1)
|
||||
|
||||
# def get_tip_group_start(tip_str : str) :
|
||||
# print_normal_msg(f"Tip String:{tip_str}")
|
||||
# return get_tip_value(tip_str, "groupstart", default_value = -1)
|
||||
|
||||
# def get_tip_group_end(tip_str : str) :
|
||||
# print_normal_msg(f"Tip String:{tip_str}")
|
||||
# return get_tip_value(tip_str, "groupend", default_value = -1)
|
||||
def get_tip_group_start(tip_str: str):
|
||||
# 创建一个局部变量来保存传入的参数 tip_str
|
||||
local_tip_str = tip_str
|
||||
# 直接返回状态提示信息中的组开始标签数量
|
||||
return get_tip_value(local_tip_str, "groupstart", default_value=-1)
|
||||
|
||||
def get_tip_group_end(tip_str: str):
|
||||
# 创建一个局部变量来保存传入的参数 tip_str
|
||||
local_tip_str = tip_str
|
||||
# 直接返回状态提示信息中的组结束标签数量
|
||||
return get_tip_value(local_tip_str, "groupend", default_value=-1)
|
||||
|
||||
def get_tip_system(tip_str : str) :
|
||||
return get_tip_value_str(tip_str, "System", default_string = None)
|
||||
|
||||
def get_tip_password(tip_str : str) :
|
||||
return get_tip_value_str(tip_str, "Password", default_string = None)
|
||||
|
||||
#获取画布Id和摄像头Id
|
||||
def get_tip_canvas_id_and_camera_id(tip_str : str) :
|
||||
canvas_id = -1
|
||||
camera_id = -1
|
||||
if len(tip_str) == 0 :
|
||||
return canvas_id, camera_id
|
||||
|
||||
tip_split_strs = tip_str.split(",")
|
||||
for prop_str in tip_split_strs :
|
||||
if "canvas" in prop_str and "camera" in prop_str:
|
||||
prop_split_strs = prop_str.split("=")
|
||||
if len(prop_split_strs) >= 2 :
|
||||
canvas_id = get_value_from_lead_value_str(prop_split_strs[0], "canvas", -1)
|
||||
camera_id = get_value_from_lead_value_str(prop_split_strs[1], "camera", -1)
|
||||
break
|
||||
return canvas_id, camera_id
|
||||
|
||||
def get_tip_mqtt_name(tip_str : str) :
|
||||
mqtt_name = get_tip_value_str(tip_str, "mqtt", default_string = None)
|
||||
if mqtt_name != None :
|
||||
return mqtt_name
|
||||
|
||||
prop_split_strs = tip_str.split(",")
|
||||
if len(prop_split_strs[0]) == 0:
|
||||
return None
|
||||
elif "=" not in prop_split_strs[0] :
|
||||
return prop_split_strs[0]
|
||||
else :
|
||||
return None
|
||||
class system_parameter :
|
||||
_instance = None
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
cls._instance.initialize()
|
||||
|
||||
return cls._instance
|
||||
|
||||
def initialize(self):
|
||||
# 初始化属性,设置默认值
|
||||
default_config = {
|
||||
'screen_blanking_time': '360',
|
||||
'system_password': '2008',
|
||||
'language': 'zh_CN'
|
||||
}
|
||||
|
||||
# 如果配置文件存在,加载配置
|
||||
config = configparser.ConfigParser()
|
||||
if os.path.exists("system_parameter.ini"):
|
||||
try:
|
||||
config.read("system_parameter.ini")
|
||||
system_param = config['SystemParam']
|
||||
self.screen_blanking_time = system_param.get('blanking_time', default_config['screen_blanking_time'])
|
||||
self.system_password = system_param.get('password', default_config['system_password'])
|
||||
self.language = system_param.get('language', default_config['language'])
|
||||
except (configparser.Error, KeyError) as e:
|
||||
# 处理读取配置文件时的错误
|
||||
print(f"读取配置错误: {e}。使用默认设置。")
|
||||
self.apply_defaults(default_config)
|
||||
else:
|
||||
self.apply_defaults(default_config)
|
||||
self.write_to_ini()
|
||||
|
||||
def apply_defaults(self, default_config):
|
||||
"""应用默认设置。"""
|
||||
self.screen_blanking_time = default_config['screen_blanking_time']
|
||||
self.system_password = default_config['system_password']
|
||||
self.language = default_config['language']
|
||||
|
||||
def write_to_ini(self):
|
||||
"""将默认配置写入ini文件。"""
|
||||
config = configparser.ConfigParser()
|
||||
config['SystemParam'] = {
|
||||
'blanking_time': self.screen_blanking_time,
|
||||
'password': self.system_password,
|
||||
'language': self.language
|
||||
}
|
||||
with open("system_parameter.ini", 'w') as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
|
||||
def change_screen_blanking_time(self, time):
|
||||
self.screen_blanking_time = time
|
||||
self.write_to_ini()
|
||||
|
||||
def get_screen_blanking_time(self):
|
||||
return self.screen_blanking_time
|
||||
|
||||
def set_system_password(self, password):
|
||||
self.system_password = password
|
||||
self.write_to_ini()
|
||||
|
||||
def get_system_password(self):
|
||||
return self.system_password
|
||||
|
||||
def set_system_language(self, language):
|
||||
self.language = language
|
||||
self.write_to_ini()
|
||||
|
||||
def get_system_language(self):
|
||||
return self.language
|
||||
|
||||
# 向文件存储系统信息
|
||||
def write_to_ini(self):
|
||||
system_param = configparser.ConfigParser()
|
||||
system_param['SystemParam'] = {'language': self.language,
|
||||
'blanking_time': self.screen_blanking_time,
|
||||
'password': self.system_password}
|
||||
with open('system_parameter.ini', 'w') as f:
|
||||
system_param.write(f)
|
||||
|
||||
199
QT5_Project/Shared_CODE/run.py
Normal file
199
QT5_Project/Shared_CODE/run.py
Normal file
@ -0,0 +1,199 @@
|
||||
# - * - coding:utf - 8 - * -
|
||||
import os
|
||||
from flask import Flask, request, jsonify, render_template, url_for, send_from_directory
|
||||
import numpy as np
|
||||
import face_recognition
|
||||
from PIL import Image
|
||||
|
||||
# 初始化 Flask 应用
|
||||
app = Flask(__name__)
|
||||
|
||||
# 定义路径常量
|
||||
npy_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data/face_data.npy'))
|
||||
image_folder_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data'))
|
||||
|
||||
# 加载 face_data 数据
|
||||
def load_face_data():
|
||||
print(f"加载人脸数据: {npy_file_path}") # 打印加载数据的路径
|
||||
if os.path.exists(npy_file_path):
|
||||
try:
|
||||
face_data = np.load(npy_file_path, allow_pickle=True).item()
|
||||
return face_data
|
||||
except Exception as e:
|
||||
print(f"加载人脸编码时出错: {e}")
|
||||
return {}
|
||||
print("人脸数据文件不存在。")
|
||||
return {}
|
||||
|
||||
# 保存 face_data 数据
|
||||
def save_face_data(face_data):
|
||||
print(f"保存人脸数据到: {npy_file_path}") # 打印保存数据的路径
|
||||
np.save(npy_file_path, face_data)
|
||||
print("人脸数据保存成功。")
|
||||
|
||||
# 获取图片的 URL
|
||||
def get_image_url(name):
|
||||
# 使用 Flask 的自定义路由返回图片
|
||||
return url_for('serve_face_image', filename=f'{name}.jpg')
|
||||
|
||||
# 获取图片的绝对路径
|
||||
def get_image_path(name):
|
||||
image_path = os.path.join(image_folder_path, f'{name}.jpg')
|
||||
return os.path.abspath(image_path) # 返回绝对路径
|
||||
|
||||
# 比较人脸编码之间的相似度
|
||||
def is_same_person(new_encoding, existing_encoding, threshold=0.5):
|
||||
"""
|
||||
判断两个人脸编码是否属于同一个人
|
||||
threshold: 距离小于该阈值认为是同一个人
|
||||
"""
|
||||
distance = np.linalg.norm(np.array(new_encoding) - np.array(existing_encoding))
|
||||
return distance < threshold
|
||||
|
||||
# 检查上传的图片是否有效
|
||||
def check_image_validity(file_path):
|
||||
try:
|
||||
img = Image.open(file_path)
|
||||
img.verify() # 验证图片是否有效
|
||||
return True
|
||||
except (IOError, SyntaxError):
|
||||
return False
|
||||
|
||||
# 保存图片为标准的 .jpg 格式
|
||||
def save_image_as_jpeg(file, file_path):
|
||||
img = Image.open(file)
|
||||
img.convert("RGB").save(file_path, "JPEG")
|
||||
|
||||
# 自定义路由来返回图片
|
||||
@app.route('/face_image/<filename>')
|
||||
def serve_face_image(filename):
|
||||
try:
|
||||
return send_from_directory(image_folder_path, filename)
|
||||
except Exception as e:
|
||||
return jsonify({"error": "Image not found"}), 404
|
||||
|
||||
# 检查并创建 image_folder_path
|
||||
if not os.path.exists(image_folder_path):
|
||||
print(f"创建人脸图片文件夹: {image_folder_path}") # 打印创建的文件夹路径
|
||||
os.makedirs(image_folder_path)
|
||||
|
||||
# 检查上传的图片是否有效
|
||||
def check_image_validity(file_path):
|
||||
try:
|
||||
img = Image.open(file_path)
|
||||
img.verify() # 验证图片是否有效
|
||||
print(f"图片 {file_path} 是有效的图像文件。")
|
||||
except (IOError, SyntaxError) as e:
|
||||
print(f"无效的图片文件: {file_path}, 错误信息: {e}")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
print("访问首页,加载已注册人脸...")
|
||||
face_data = load_face_data()
|
||||
faces_and_images = [
|
||||
{"name": name, "image": get_image_url(name)} # 使用统一的函数生成路径
|
||||
for name in face_data.keys()
|
||||
if os.path.exists(os.path.join(image_folder_path, f"{name}.jpg"))
|
||||
]
|
||||
print(f"加载的已注册人脸: {faces_and_images}") # 打印加载的所有人脸
|
||||
return render_template('index.html', faces_and_images=faces_and_images, error_message=None)
|
||||
|
||||
@app.route('/add_face', methods=['POST'])
|
||||
def add_face():
|
||||
print("接收到添加人脸请求...")
|
||||
face_data = load_face_data()
|
||||
if 'file' not in request.files or 'name' not in request.form:
|
||||
return jsonify({"error": "请求中缺少 'file' 或 'name' 参数。"}), 400
|
||||
|
||||
file = request.files['file']
|
||||
name = request.form['name']
|
||||
print(f"上传的人脸名称: {name}, 文件: {file.filename}")
|
||||
|
||||
try:
|
||||
image = face_recognition.load_image_file(file)
|
||||
face_encodings = face_recognition.face_encodings(image)
|
||||
|
||||
if not face_encodings:
|
||||
return jsonify({"error": "上传的图片中没有检测到人脸。"}), 400
|
||||
|
||||
new_face_encoding = face_encodings[0]
|
||||
existing_faces = [face_data[stored_name] for stored_name in face_data.keys()]
|
||||
|
||||
# 检查是否有已存在的相似编码
|
||||
for existing_encoding in existing_faces:
|
||||
if is_same_person(new_face_encoding, existing_encoding):
|
||||
return jsonify({"error": "已有相似的人脸编码,无法添加。"}), 400
|
||||
|
||||
# 检查是否已有相同名字的图片
|
||||
file_path = os.path.join(image_folder_path, f"{name}.jpg")
|
||||
if os.path.exists(file_path):
|
||||
return jsonify({"error": f"'{name}' 的图片已经存在。"}), 400
|
||||
|
||||
# 保存图片为 JPEG 格式
|
||||
save_image_as_jpeg(file, file_path)
|
||||
|
||||
# 检查图片是否有效
|
||||
if not check_image_validity(file_path):
|
||||
return jsonify({"error": "上传的图片无效或损坏。"}), 400
|
||||
|
||||
# 将人脸编码保存
|
||||
face_data[name] = new_face_encoding.tolist()
|
||||
save_face_data(face_data)
|
||||
|
||||
return jsonify({"success": "人脸添加成功。"}), 200
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"error": f"发生错误: {str(e)}"}), 500
|
||||
|
||||
@app.route('/delete_face', methods=['POST'])
|
||||
def delete_face():
|
||||
print("接收到删除人脸请求...")
|
||||
face_data = load_face_data()
|
||||
name = request.form.get('name')
|
||||
|
||||
if not name:
|
||||
print("缺少 'name' 参数。")
|
||||
return jsonify({"error": "缺少 'name' 参数。"}), 400
|
||||
|
||||
if name not in face_data:
|
||||
print(f"未找到名为 '{name}' 的人脸数据。")
|
||||
return jsonify({"error": f"未找到名为 '{name}' 的人脸数据。"}), 404
|
||||
|
||||
try:
|
||||
# 删除面部数据
|
||||
del face_data[name]
|
||||
save_face_data(face_data)
|
||||
|
||||
# 删除图片
|
||||
image_path = os.path.join(image_folder_path, f"{name}.jpg")
|
||||
if os.path.exists(image_path):
|
||||
os.remove(image_path)
|
||||
print(f"成功删除图片: {image_path}")
|
||||
else:
|
||||
print(f"未找到图片文件: {image_path}")
|
||||
|
||||
print(f"成功删除人脸: {name}")
|
||||
return jsonify({"success": f"人脸 '{name}' 删除成功。"}), 200
|
||||
|
||||
except Exception as e:
|
||||
print(f"发生错误: {str(e)}")
|
||||
return jsonify({"error": f"发生错误: {str(e)}"}), 500
|
||||
|
||||
@app.route('/list_faces', methods=['GET'])
|
||||
def list_faces():
|
||||
print("获取已注册人脸列表...")
|
||||
face_data = load_face_data()
|
||||
faces_with_images = [
|
||||
{"name": name, "image": get_image_url(name)} # 使用统一的函数生成路径
|
||||
for name in face_data.keys()
|
||||
if os.path.exists(os.path.join(image_folder_path, f'{name}.jpg'))
|
||||
]
|
||||
print(f"已注册人脸列表: {faces_with_images}") # 打印已注册人脸
|
||||
return jsonify({"faces": faces_with_images}), 200
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("启动 Flask 应用...")
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
291
QT5_Project/Shared_CODE/templates/index.html
Normal file
291
QT5_Project/Shared_CODE/templates/index.html
Normal file
@ -0,0 +1,291 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>人脸识别应用</title>
|
||||
<style>
|
||||
/* 基本布局和样式 */
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f1f3f5;
|
||||
padding: 30px;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 30px;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
/* 卡片布局 */
|
||||
.face-card {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 20px;
|
||||
justify-items: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.face-item {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.face-item:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.face-item img {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.face-item button {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.face-item button:hover {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
|
||||
/* 添加人脸表单 */
|
||||
form {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
form input, form button {
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ddd;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
form button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
form button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
/* 状态消息框 */
|
||||
#alert-box {
|
||||
padding: 15px;
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border-radius: 5px;
|
||||
margin-top: 30px;
|
||||
display: none;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#alert-box.alert-error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
/* 媒体查询: 调整在小屏设备(手机)上的显示 */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.face-card {
|
||||
grid-template-columns: 1fr 1fr; /* 在小屏设备上两列显示 */
|
||||
}
|
||||
|
||||
.face-item {
|
||||
width: 160px; /* 缩小卡片尺寸 */
|
||||
}
|
||||
|
||||
form input, form button {
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
/* 在极小屏幕上调整 */
|
||||
.face-card {
|
||||
grid-template-columns: 1fr; /* 单列显示 */
|
||||
}
|
||||
|
||||
.face-item {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
form input, form button {
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>人脸识别应用</h1>
|
||||
|
||||
<h2>已注册的人脸:</h2>
|
||||
|
||||
<!-- 使用卡片式布局展示人脸 -->
|
||||
<div class="face-card" id="face-list">
|
||||
<!-- 人脸将动态加载到这里 -->
|
||||
</div>
|
||||
|
||||
<!-- 状态消息框: 现在放在上传表单上面 -->
|
||||
<div id="alert-box"></div>
|
||||
|
||||
<!-- 添加人脸表单 -->
|
||||
<form id="add-face-form" enctype="multipart/form-data">
|
||||
<input type="text" id="add-name" placeholder="请输入姓名" required>
|
||||
<input type="file" id="add-file" accept="image/*" required>
|
||||
<button type="submit">添加人脸</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
// 加载所有人脸
|
||||
async function loadFaces() {
|
||||
try {
|
||||
const response = await fetch('/list_faces');
|
||||
const data = await response.json();
|
||||
|
||||
const faceList = document.getElementById('face-list');
|
||||
faceList.innerHTML = ''; // 清空现有列表
|
||||
|
||||
// 为每个注册的人脸生成卡片项
|
||||
data.faces.forEach(face => {
|
||||
const listItem = document.createElement('div');
|
||||
listItem.classList.add('face-item');
|
||||
listItem.innerHTML = `
|
||||
<img src="${face.image}" alt="${face.name}">
|
||||
<h3>${face.name}</h3>
|
||||
<button onclick="deleteFace('${face.name}')">删除</button>
|
||||
`;
|
||||
faceList.appendChild(listItem);
|
||||
});
|
||||
} catch (error) {
|
||||
showAlert('加载人脸时发生错误。');
|
||||
}
|
||||
}
|
||||
|
||||
// 删除人脸
|
||||
async function deleteFace(name) {
|
||||
try {
|
||||
const response = await fetch('/delete_face', {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({ name: name })
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showAlert(result.success, '/'); // 删除成功后重定向
|
||||
} else if (result.error) {
|
||||
showAlert(result.error); // 显示错误消息
|
||||
}
|
||||
} catch (error) {
|
||||
showAlert('发生了意外错误。');
|
||||
}
|
||||
}
|
||||
|
||||
// 提交添加人脸表单
|
||||
document.getElementById('add-face-form').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const name = document.getElementById('add-name').value;
|
||||
const file = document.getElementById('add-file').files[0];
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('name', name);
|
||||
formData.append('file', file);
|
||||
|
||||
try {
|
||||
const response = await fetch('/add_face', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showAlert(result.success, '/'); // 成功后重定向
|
||||
} else if (result.error) {
|
||||
showAlert(result.error); // 显示错误消息
|
||||
}
|
||||
} catch (error) {
|
||||
showAlert('发生了意外错误。');
|
||||
}
|
||||
});
|
||||
|
||||
// 显示消息
|
||||
function showAlert(message, redirectUrl = null) {
|
||||
const alertBox = document.getElementById('alert-box');
|
||||
alertBox.innerText = message;
|
||||
|
||||
if (message.includes('成功')) {
|
||||
alertBox.classList.add('alert-success');
|
||||
alertBox.classList.remove('alert-error');
|
||||
} else {
|
||||
alertBox.classList.add('alert-error');
|
||||
alertBox.classList.remove('alert-success');
|
||||
}
|
||||
|
||||
alertBox.style.display = 'block';
|
||||
|
||||
// 如果需要重定向,则等待并执行重定向
|
||||
if (redirectUrl) {
|
||||
setTimeout(() => {
|
||||
window.location.href = redirectUrl; // 重定向
|
||||
}, 6000);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
alertBox.style.display = 'none';
|
||||
}, 3000); // 3秒后隐藏警告框
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时加载所有人脸
|
||||
loadFaces();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user