autoCloseScreen/workspace_monitor.py
2024-12-04 16:05:47 +08:00

437 lines
17 KiB
Python

import cv2
import time
import ctypes
import sys
import traceback
import logging
import os
from datetime import datetime
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QPushButton, QLabel, QSpinBox,
QStyle, QProgressBar, QSystemTrayIcon, QMenu, QMessageBox)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.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.SP_ComputerIcon))
self.create_tray_menu()
self.tray_icon.show()
# 初始化变量
self.running = False
self.timeout = 10
logger.info("Loading face detection model...")
# 使用OpenCV的人脸检测器
self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
if self.face_cascade.empty():
raise Exception("无法加载人脸检测模型")
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.Bold))
title_label.setAlignment(Qt.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.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.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
# 转换为灰度图进行人脸检测
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = self.face_cascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30)
)
current_time = time.time()
max_confidence = 0
if len(faces) > 0:
# 简单地使用检测到的人脸数量作为置信度的基础
max_confidence = min(len(faces) * 0.5, 1.0) # 限制最大值为1.0
for (x, y, w, h) in faces:
# 绘制人脸框
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 显示置信度
cv2.putText(frame, f"置信度: {max_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.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_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_frame.shape
bytes_per_line = ch * w
qt_image = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
scaled_pixmap = QPixmap.fromImage(qt_image).scaled(
self.camera_label.size(),
Qt.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.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.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.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.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.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)
msg = QMessageBox()
msg.setIcon(QMessageBox.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)