import sys
import cv2
import numpy as np
import pyttsx3
from pyzbar.pyzbar import decode
from PyQt5.QtWidgets import (
QApplication, QLabel, QVBoxLayout, QWidget, QLineEdit, QPushButton, QListWidget, QHBoxLayout
)
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import QTimer, QTime, QDate, QDateTime, Qt
from multiprocessing import Process, Manager, Lock
import time
import threading
# 初始化语音引擎
engine = pyttsx3.init()
engine.setProperty('voice', 'zh')
engine.setProperty('rate', 200)
engine.setProperty('voice', engine.getProperty('voices')[0].id)
# 摄像头进程
def camera_process(share_data, lock):
cap = cv2.VideoCapture(0)
while share_data['running']:
ret, rgb_image = cap.read()
if ret:
with lock:
share_data['frame'] = rgb_image # 存储 RGB 图像
cap.release()
# QR 码识别进程
def qr_code_process(share_data, lock):
#previous_qr_data = "" # 记录上一个二维码数据,避免重复播报
while share_data['running']:
with lock:
frame = share_data.get('frame', None)
if frame is not None:
decoded_objects = decode(frame)
for obj in decoded_objects:
qr_data = obj.data.decode('utf-8')
#if qr_data != previous_qr_data: # 如果内容有变化才播报
share_data['qr_data'] = qr_data
text_to_speech(qr_data)
#previous_qr_data = qr_data
# 语音播报
def text_to_speech(text):
engine.say(text)
engine.runAndWait()
print('二维码内容播报完毕')
# NumPy 数组转 QImage
def rgbimg_to_qimage(array):
array = cv2.cvtColor(array, cv2.COLOR_BGR2RGB)
height, width, channel = array.shape
bytes_per_line = 3 * width
q_image = QImage(array.data, width, height, bytes_per_line, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(q_image)
return pixmap
class MainWindow(QWidget):
def __init__(self, share_data, lock):
super().__init__()
self.share_data = share_data
self.lock = lock
self.init_ui()
self.start_timer()
# 线程列表,用于管理每个预约任务的线程和事件
self.reminder_threads = []
self.reminder_events = []
def init_ui(self):
self.setWindowTitle('QR Code Scanner & Task Scheduler')
self.setGeometry(200, 200, 700, 600)
# Video display
self.video_label = QLabel(self)
self.qr_label = QLabel('QR Code Content: ', self)
# Time display
self.time_label = QLabel(self)
self.time_label.setAlignment(Qt.AlignCenter)
# Input fields
self.time_label_input = QLabel('预约时间:', self)
self.time_input = QLineEdit(self)
self.time_input.setPlaceholderText("Enter time (yyyy-MM-dd HH:mm:ss)")
self.time_input.setText(QDateTime.currentDateTime().toString('yyyy-MM-dd HH:mm:ss'))
self.content_label_input = QLabel('提示内容:', self)
self.content_input = QLineEdit(self)
self.content_input.setPlaceholderText("Enter reminder content")
# Buttons
self.add_reminder_button = QPushButton('添加预约任务', self)
self.add_reminder_button.clicked.connect(self.set_reminder)
self.delete_button = QPushButton('删除预约任务', self)
self.delete_button.clicked.connect(self.delete_task)
# List box
self.task_list = QListWidget(self)
# Layout setup
layout = QVBoxLayout()
layout.addWidget(self.video_label)
layout.addWidget(self.qr_label)
layout.addWidget(self.time_label)
layout.addWidget(self.time_label_input)
layout.addWidget(self.time_input)
layout.addWidget(self.content_label_input)
layout.addWidget(self.content_input)
layout.addWidget(self.add_reminder_button)
layout.addWidget(self.task_list)
layout.addWidget(self.delete_button)
self.setLayout(layout)
def start_timer(self):
self.timer = QTimer(self)
self.timer.timeout.connect(self.update_frame)
self.timer.start(50)
# Time update timer
self.time_update_timer = QTimer(self)
self.time_update_timer.timeout.connect(self.update_time)
self.time_update_timer.start(1000)
def update_frame(self):
with self.lock:
frame = self.share_data.get('frame', None)
if frame is not None:
pixmap = rgbimg_to_qimage(frame)
self.video_label.setPixmap(pixmap)
qr_data = self.share_data.get('qr_data', '')
if qr_data:
self.qr_label.setText(f'QR Code Content: {qr_data}')
def update_time(self):
current_time = QDateTime.currentDateTime().toString('yyyy-MM-dd HH:mm:ss')
self.time_label.setText(f'Current Time: {current_time}')
def set_reminder(self):
reminder_time = self.time_input.text()
reminder_content = self.content_input.text()
if reminder_time and reminder_content:
reminder_item = f"At {reminder_time} - {reminder_content}"
self.task_list.addItem(reminder_item)
# Create an event to control the thread
stop_event = threading.Event()
# Create a new reminder thread
reminder_thread = threading.Thread(target=self.trigger_reminder, args=(reminder_time, reminder_content, stop_event))
reminder_thread.daemon = True
self.reminder_threads.append(reminder_thread)
self.reminder_events.append(stop_event)
reminder_thread.start()
def trigger_reminder(self, reminder_time, reminder_content, stop_event):
while self.share_data['running'] and not stop_event.is_set():
current_time = QDateTime.currentDateTime().toString('yyyy-MM-dd HH:mm:ss')
if current_time == reminder_time:
text_to_speech(reminder_content)
print(f"Reminder triggered: {reminder_content}")
break
time.sleep(1)
def delete_task(self):
selected_item = self.task_list.currentRow()
if selected_item >= 0:
item_text = self.task_list.item(selected_item).text()
# Remove from task list widget
self.task_list.takeItem(selected_item)
# Stop the corresponding reminder thread
if 0 <= selected_item < len(self.reminder_threads):
stop_event = self.reminder_events[selected_item]
stop_event.set() # Stop the thread by setting the event
# Optionally join the thread to ensure it has finished
self.reminder_threads[selected_item].join()
# Remove the thread and event from the list
self.reminder_threads[selected_item] = None
self.reminder_events[selected_item] = None
def closeEvent(self, event):
""" 关闭窗口时修改 share_data['running'] = False """
self.share_data['running'] = False
event.accept()
if __name__ == '__main__':
manager = Manager()
share_data = manager.dict()
lock = manager.Lock()
# 初始化共享数据
share_data['frame'] = None # 图像数据
share_data['qr_data'] = '' # 二维码识别结果
share_data['running'] = True # 线程运行标志位
# 启动摄像头进程
p1 = Process(target=camera_process, args=(share_data, lock))
p1.daemon = True
p1.start()
# 启动 QR 码识别进程
p2 = Process(target=qr_code_process, args=(share_data, lock))
p2.daemon = True
p2.start()
app = QApplication(sys.argv)
window = MainWindow(share_data, lock)
window.show()
try:
sys.exit(app.exec_())
finally:
share_data['running'] = False
p1.join()
p2.join()