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