init
This commit is contained in:
parent
fb37463303
commit
540c3b0e47
6
requirements.txt
Normal file
6
requirements.txt
Normal 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
7
start.bat
Normal 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
425
workspace_monitor.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user