This commit is contained in:
scout 2024-12-04 11:53:38 +08:00
parent fb37463303
commit 540c3b0e47
3 changed files with 438 additions and 0 deletions

6
requirements.txt Normal file
View File

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

7
start.bat Normal file
View File

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

425
workspace_monitor.py Normal file
View File

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