From 540c3b0e47c858701f38f1adf808c91472bd7771 Mon Sep 17 00:00:00 2001 From: scout <1134087124@qq.com> Date: Wed, 4 Dec 2024 11:53:38 +0800 Subject: [PATCH] init --- requirements.txt | 6 + start.bat | 7 + workspace_monitor.py | 425 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 requirements.txt create mode 100644 start.bat create mode 100644 workspace_monitor.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6f27204 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +opencv-python==4.8.1.78 +mediapipe==0.10.8 +PyQt6==6.6.1 +PyQt6-Qt6==6.6.1 +PyQt6-sip==13.6.0 +PyQt6-WebEngine==6.6.0 \ No newline at end of file diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..71946b0 --- /dev/null +++ b/start.bat @@ -0,0 +1,7 @@ +@echo off +echo Installing/Updating requirements... +"C:\Users\user\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\python.exe" -m pip install -r requirements.txt + +echo Starting application... +"C:\Users\user\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\python.exe" workspace_monitor.py +pause \ No newline at end of file diff --git a/workspace_monitor.py b/workspace_monitor.py new file mode 100644 index 0000000..da21a80 --- /dev/null +++ b/workspace_monitor.py @@ -0,0 +1,425 @@ +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.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.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) \ No newline at end of file