华荣三照明、合信、荣欣八组合馈电

This commit is contained in:
冯佳
2025-06-25 11:36:43 +08:00
parent 37d39f4578
commit 25b3cb7f2e
494 changed files with 114074 additions and 0 deletions

View 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是BGRPIL是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)

View 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)

View 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)

View 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)

View 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_())

View 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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Binary file not shown.

View 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)

View 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)

View 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>