问题描述:
我正在尝试在 PyQt6 中创建一个应用程序来显示来自网络摄像头的视频。当我运行代码时,它显示一个空窗口。你能帮我理解我做错了什么吗?
源代码:
from PyQt6 import QtGui, QtCore
from PyQt6.QtWidgets import QWidget, QApplication, QLabel, QVBoxLayout, QPushButton, QSizePolicy, QHBoxLayout
from PyQt6.QtGui import QPixmap
from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QThread
import sys
import cv2
import numpy as np
class VideoThread(QThread):
change_pixmap_signal = pyqtSignal(np.ndarray)
def run(self):
cam = cv2.VideoCapture(0)
if not cam.isOpened():
print("Error opening video file")
return
while True:
ret, frame = cam.read()
if not ret:
break
frame = cv2.flip(frame, 1) # Отзеркаливание по вертикали
self.change_pixmap_signal.emit(frame) # Отправляем кадровое изображение в основной поток
cam.release()
class MovingApp(QThread):
change_pixmap_signal = pyqtSignal(np.ndarray)
def run(self):
cam = cv2.VideoCapture(0)
if not cam.isOpened():
print("Error opening video file")
return
backSub = cv2.createBackgroundSubtractorMOG2(history=100, varThreshold=16, detectShadows=True)
while True:
ret, frame = cam.read()
if not ret:
break
fg_mask = backSub.apply(frame)
contours, _ = cv2.findContours(fg_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
min_contour_area = 450
large_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_contour_area]
frame_out = frame.copy()
for cnt in large_contours:
x, y, w, h = cv2.boundingRect(cnt)
frame_out = cv2.rectangle(frame_out, (x, y), (x + w, y + h), (0, 0, 200), 3)
self.change_pixmap_signal.emit(frame_out) # Отправляем обработанный кадр в основной поток
cam.release()
class MainWindow(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowIcon(QtGui.QIcon('image.ico'))
self.setWindowTitle('Project')
self.setGeometry(100, 100, 800, 600)
layout = QHBoxLayout()
self.setLayout(layout)
self.video_label = QLabel(self)
self.video_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.video_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.thread = None
button_layout = QVBoxLayout() # Добавляем макет для кнопок
button2 = QPushButton('Отслеживание\nдвижения')
button2.setCheckable(True)
button2.toggled.connect(self.toggle_moving_app) # Изменяем соединение на toggle
button2.setFixedSize(220, 140)
button_layout.addWidget(button2)
layout.addLayout(button_layout)
layout.addWidget(self.video_label)
# Запускаем поток видео по умолчанию
self.start_video_thread()
self.showMaximized()
def toggle_moving_app(self, checked):
"""Переключаем между потоками видео и обработки."""
if checked:
self.start_moving_thread()
else:
self.start_video_thread()
def start_video_thread(self):
"""Запускаем поток для простого видео."""
if self.thread is not None:
self.thread.quit()
self.thread.wait() # Обязательно ждем завершения потока
self.thread = VideoThread()
self.thread.change_pixmap_signal.connect(self.update_image)
self.thread.start()
def start_moving_thread(self):
"""Запускаем поток для работы с движением."""
if self.thread is not None:
self.thread.quit()
self.thread.wait() # Обязательно ждем завершения потока
self.thread = MovingApp()
self.thread.change_pixmap_signal.connect(self.update_image)
self.thread.start()
@pyqtSlot(np.ndarray)
def update_image(self, frame):
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = cv2.resize(frame, (950, 750))
h, w, ch = frame.shape
bytes_per_line = ch * w
q_img = QtGui.QImage(frame.data, w, h, bytes_per_line, QtGui.QImage.Format.Format_RGB888)
self.video_label.setPixmap(QPixmap.fromImage(q_img))
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec())
错误或警告:
当我运行代码时,应用程序窗口是空的,并且不显示视频。控制台输出以下消息:
打开视频文件时出错
预期结果:
来自网络摄像头的视频应显示在应用程序窗口中。
如果有任何其他详细信息可以提供帮助,例如您正在使用的 Python 或 PyQt 版本或操作系统,也请将它们包含在您的问题中。
技术细节:
通过执行这些步骤,您将创建一个结构良好且易于理解的问题,其他 Stack Overflow 用户将更容易阅读和回答。
你是傻子吗?天哪,这是有史以来最简单的事情。你应该戒掉。