import cv2 import time import ctypes import sys import traceback import logging import os import mediapipe as mp from datetime import datetime from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QSpinBox, QStyle, QProgressBar, QSystemTrayIcon, QMenu) from PyQt6.QtCore import Qt, QTimer from PyQt6.QtGui import QImage, QPixmap, QIcon, QFont # 设置日志 logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('app.log'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) def handle_exception(exc_type, exc_value, exc_traceback): logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)) sys.__excepthook__(exc_type, exc_value, exc_traceback) sys.excepthook = handle_exception try: logger.info("Starting application...") class WorkspaceMonitor(QMainWindow): def __init__(self): try: super().__init__() logger.info("Initializing WorkspaceMonitor...") self.setWindowTitle("自动锁屏") self.setMinimumSize(1000, 700) # 系统托盘 self.tray_icon = QSystemTrayIcon(self) self.tray_icon.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_ComputerIcon)) self.create_tray_menu() self.tray_icon.show() # 初始化变量 self.running = False self.timeout = 10 logger.info("Initializing MediaPipe...") self.mp_face_detection = mp.solutions.face_detection self.mp_drawing = mp.solutions.drawing_utils self.face_detection = self.mp_face_detection.FaceDetection( model_selection=1, min_detection_confidence=0.5 ) logger.info("Opening camera...") self.cap = cv2.VideoCapture(0) if not self.cap.isOpened(): logger.error("Failed to open camera!") raise Exception("无法打开摄像头") self.last_face_time = time.time() self.no_face_duration = 0 self.setup_ui() logger.info("Initialization complete.") except Exception as e: logger.error(f"Error in WorkspaceMonitor initialization: {str(e)}") logger.error(traceback.format_exc()) raise def setup_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # 标题 title_label = QLabel("自动锁屏") title_label.setFont(QFont('Arial', 16, QFont.Weight.Bold)) title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) main_layout.addWidget(title_label) # 摄像头预览 preview_container = QWidget() preview_container.setStyleSheet("background-color: #f0f0f0; border-radius: 10px;") preview_layout = QVBoxLayout(preview_container) self.camera_label = QLabel() self.camera_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.camera_label.setMinimumHeight(480) preview_layout.addWidget(self.camera_label) main_layout.addWidget(preview_container) # 控制面板 control_panel = QWidget() control_panel.setStyleSheet(""" QWidget { background-color: #ffffff; border-radius: 10px; padding: 10px; } QPushButton { background-color: #4CAF50; color: white; border-radius: 5px; padding: 8px; min-width: 100px; } QPushButton:hover { background-color: #45a049; } QLabel { color: #333333; } """) control_layout = QHBoxLayout(control_panel) # 开始/停止按钮 self.start_stop_btn = QPushButton("开始监控") self.start_stop_btn.clicked.connect(self.toggle_monitoring) control_layout.addWidget(self.start_stop_btn) # 超时设置 timeout_container = QWidget() timeout_layout = QHBoxLayout(timeout_container) timeout_label = QLabel("锁屏等待时间:") self.timeout_spinbox = QSpinBox() self.timeout_spinbox.setRange(5, 300) self.timeout_spinbox.setValue(10) self.timeout_spinbox.setSuffix(" 秒") self.timeout_spinbox.valueChanged.connect(self.update_timeout) timeout_layout.addWidget(timeout_label) timeout_layout.addWidget(self.timeout_spinbox) control_layout.addWidget(timeout_container) # 状态显示 status_container = QWidget() status_layout = QVBoxLayout(status_container) self.status_label = QLabel("状态: 未开始监控") self.status_label.setStyleSheet(""" QLabel { font-weight: bold; font-size: 14px; color: #333333; margin-bottom: 5px; } """) self.confidence_label = QLabel("置信度: 0%") self.confidence_label.setStyleSheet(""" QLabel { color: #666666; margin-bottom: 5px; } """) # 进度条容器 progress_container = QWidget() progress_container.setFixedHeight(45) progress_container.setStyleSheet(""" QWidget { background-color: transparent; padding: 5px; } """) progress_layout = QVBoxLayout(progress_container) progress_layout.setContentsMargins(0, 0, 0, 0) # 进度条 self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) self.progress_bar.setFormat("离开时间: 0s") self.progress_bar.setMinimumWidth(250) self.progress_bar.setFixedHeight(20) self.set_progress_bar_style() progress_layout.addWidget(self.progress_bar) status_layout.addWidget(self.status_label) status_layout.addWidget(self.confidence_label) status_layout.addWidget(progress_container) control_layout.addWidget(status_container) main_layout.addWidget(control_panel) self.timer = QTimer() self.timer.timeout.connect(self.update_frame) def create_tray_menu(self): menu = QMenu() show_action = menu.addAction("显示主窗口") show_action.triggered.connect(self.show) quit_action = menu.addAction("退出") quit_action.triggered.connect(self.close) self.tray_icon.setContextMenu(menu) self.tray_icon.activated.connect(self.tray_icon_activated) def tray_icon_activated(self, reason): if reason == QSystemTrayIcon.ActivationReason.DoubleClick: self.show() def update_frame(self): # 检查摄像头是否连接 if not self.cap.isOpened(): self.handle_camera_disconnected() return ret, frame = self.cap.read() if not ret: self.handle_camera_disconnected() return frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = self.face_detection.process(frame_rgb) max_confidence = 0 current_time = time.time() if results.detections: for detection in results.detections: confidence = detection.score[0] max_confidence = max(max_confidence, confidence) bbox = detection.location_data.relative_bounding_box h, w, _ = frame.shape x = int(bbox.xmin * w) y = int(bbox.ymin * h) width = int(bbox.width * w) height = int(bbox.height * h) cv2.rectangle(frame, (x, y), (x + width, y + height), (0, 255, 0), 2) cv2.putText(frame, f"置信度: {confidence:.2f}", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) self.last_face_time = current_time self.no_face_duration = 0 self.status_label.setText("状态: 检测到人脸") self.confidence_label.setText(f"置信度: {max_confidence*100:.1f}%") self.progress_bar.setValue(0) self.progress_bar.setFormat("离开时间: 0s") self.set_progress_bar_style("#4CAF50") else: self.no_face_duration = current_time - self.last_face_time self.status_label.setText(f"状态: 未检测到人脸 ({int(self.no_face_duration)}秒)") self.confidence_label.setText("置信度: 0%") # 计算进度并更新进度条 progress = min(int(self.no_face_duration / self.timeout * 100), 100) self.progress_bar.setValue(progress) self.progress_bar.setFormat(f"离开时间: {int(self.no_face_duration)}s") # 根据进度设置颜色 if progress > 70: self.set_progress_bar_style("#FF4444") # 红色 elif progress > 30: self.set_progress_bar_style("#FFA726") # 橙色 else: self.set_progress_bar_style("#4CAF50") # 绿色 if self.no_face_duration > self.timeout: self.status_label.setText(f"状态: 锁定屏幕 - {datetime.now()}") self.tray_icon.showMessage("工位监控", "系统即将锁定", QSystemTrayIcon.MessageIcon.Warning) self.lock_windows() # 重置所有状态 self.last_face_time = time.time() self.no_face_duration = 0 self.progress_bar.setValue(0) self.progress_bar.setFormat("离开时间: 0s") self.set_progress_bar_style("#4CAF50") # 重置为绿色 time.sleep(1) rgb_display = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = rgb_display.shape bytes_per_line = ch * w qt_image = QImage(rgb_display.data, w, h, bytes_per_line, QImage.Format.Format_RGB888) scaled_pixmap = QPixmap.fromImage(qt_image).scaled( self.camera_label.size(), Qt.AspectRatioMode.KeepAspectRatio ) self.camera_label.setPixmap(scaled_pixmap) def toggle_monitoring(self): if not self.running: # 开始监控时重置所有计时相关变量 self.last_face_time = time.time() self.no_face_duration = 0 self.progress_bar.setValue(0) self.progress_bar.setFormat("离开时间: 0s") self.set_progress_bar_style("#4CAF50") self.running = True self.start_stop_btn.setText("停止监控") self.start_stop_btn.setStyleSheet("background-color: #ff4444;") self.status_label.setText("状态: 正在监控") self.timer.start(30) self.tray_icon.showMessage("工位监控", "监控已开始", QSystemTrayIcon.MessageIcon.Information) else: self.running = False self.start_stop_btn.setText("开始监控") self.start_stop_btn.setStyleSheet("background-color: #4CAF50;") self.status_label.setText("状态: 已停止监控") self.timer.stop() # 重置计时相关的变量 self.last_face_time = time.time() self.no_face_duration = 0 self.progress_bar.setValue(0) self.progress_bar.setFormat("离开时间: 0s") self.set_progress_bar_style("#4CAF50") # 重置进度条颜色为绿色 self.confidence_label.setText("置信度: 0%") self.tray_icon.showMessage("工位监控", "监控已停止", QSystemTrayIcon.MessageIcon.Information) def update_timeout(self, value): self.timeout = value def lock_windows(self): ctypes.windll.user32.LockWorkStation() def closeEvent(self, event): if self.running: self.tray_icon.showMessage("工位监控", "程序已最小化系统托盘", QSystemTrayIcon.MessageIcon.Information) self.hide() event.ignore() else: self.running = False self.timer.stop() self.cap.release() self.tray_icon.hide() event.accept() def handle_camera_disconnected(self): if self.running: self.running = False self.timer.stop() self.start_stop_btn.setText("开始监控") self.start_stop_btn.setStyleSheet("background-color: #4CAF50;") self.status_label.setText("状态: 摄像头已断开") self.confidence_label.setText("置信度: 0%") self.progress_bar.setValue(0) self.progress_bar.setFormat("摄像头未连接") self.tray_icon.showMessage( "工位监控", "摄像头已断开,监控已停止", QSystemTrayIcon.MessageIcon.Warning ) # 尝试重新打开摄像头 self.try_reconnect_camera() def try_reconnect_camera(self): if self.cap.isOpened(): self.cap.release() # 创建定时器尝试重新连接 self.reconnect_timer = QTimer() self.reconnect_timer.timeout.connect(self.attempt_camera_reconnect) self.reconnect_timer.start(2000) # 每2秒尝试重连 def attempt_camera_reconnect(self): self.cap = cv2.VideoCapture(0) if self.cap.isOpened(): self.reconnect_timer.stop() self.status_label.setText("状态: 摄像头已重新连接") self.progress_bar.setFormat("离开时间: %vs") self.tray_icon.showMessage( "工位监控", "摄像头已重新连接,请手动开启监控", QSystemTrayIcon.MessageIcon.Information ) def set_progress_bar_style(self, color="#4CAF50"): base_style = """ QProgressBar { border: 1px solid #E0E0E0; border-radius: 4px; text-align: center; background-color: #F5F5F5; padding: 1px; font-size: 12px; } QProgressBar::chunk { background-color: %s; border-radius: 3px; margin: 0px; } """ self.progress_bar.setStyleSheet(base_style % color) def main(): try: logger.info("Starting main function...") app = QApplication(sys.argv) app.setStyle('Fusion') window = WorkspaceMonitor() window.show() logger.info("Application started successfully.") sys.exit(app.exec()) except Exception as e: logger.error(f"Error in main function: {str(e)}") logger.error(traceback.format_exc()) raise if __name__ == "__main__": try: main() except Exception as e: logger.error(f"Fatal error: {str(e)}") logger.error(traceback.format_exc()) # 显示错误消息框 if QApplication.instance() is None: app = QApplication(sys.argv) from PyQt6.QtWidgets import QMessageBox msg = QMessageBox() msg.setIcon(QMessageBox.Icon.Critical) msg.setText("程序发生错误") msg.setInformativeText(str(e)) msg.setWindowTitle("错误") msg.setDetailedText(traceback.format_exc()) msg.exec() sys.exit(1) except Exception as e: logger.error(f"Fatal error outside main: {str(e)}") logger.error(traceback.format_exc()) sys.exit(1)