I am trying to run the following piece of code but it gives me an error RuntimeError: wrapped C/C++ object of type QLabel has been deleted. I'm unable to figure out why is it happening.
I searched other answers but they all said it has something to do with setCentralWidget. But I'm not using it. So, why's the error occuring in my code?
from PyQt4 import QtGui, QtCore
from threading import Thread
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils
import os
class CameraWidget(QtGui.QWidget):
"""Independent camera feed
Uses threading to grab IP camera frames in the background
@param width - Width of the video frame
@param height - Height of the video frame
@param stream_link - IP/RTSP/Webcam link
@param aspect_ratio - Whether to maintain frame aspect ratio or force into fraame
"""
def __init__(self, width, height, stream_link=0, btn_text=None, aspect_ratio=False, parent=None, deque_size=1):
super(CameraWidget, self).__init__(parent)
# Initialize deque used to store frames read from the stream
self.deque = deque(maxlen=deque_size)
self.screen_width = width - 8
self.screen_height = height - 8
self.maintain_aspect_ratio = aspect_ratio
self.camera_stream_link = stream_link
# Flag to check if camera is valid/working
self.online = False
self.capture = None
self.video_display_frame = QtGui.QFrame()
self.video_layout = QtGui.QVBoxLayout()
self.video_btn = QtGui.QPushButton(btn_text)
self.video_btn.setStyleSheet("background-color: rgb(128, 159, 255)")
self.video_btn.clicked.connect(self.message)
self.video_frame = QtGui.QLabel()
self.video_layout.addWidget(self.video_btn)
self.video_layout.addWidget(self.video_frame)
self.video_layout.setContentsMargins(0,0,0,0)
self.video_layout.setSpacing(0)
self.video_display_frame.setLayout(self.video_layout)
self.load_network_stream()
# Start background frame grabbing
self.get_frame_thread = Thread(target=self.get_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
# Periodically set video frame to display
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.set_frame)
self.timer.start(.5)
print('Started camera: {}'.format(self.camera_stream_link))
def load_network_stream(self):
"""Verifies stream link and open new stream if valid"""
def load_network_stream_thread():
if self.verify_network_stream(self.camera_stream_link):
self.capture = cv2.VideoCapture(self.camera_stream_link)
self.online = True
self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
self.load_stream_thread.daemon = True
self.load_stream_thread.start()
def verify_network_stream(self, link):
"""Attempts to receive a frame from given link"""
cap = cv2.VideoCapture(link)
if not cap.isOpened():
return False
cap.release()
return True
def get_frame(self):
"""Reads frame, resizes, and converts image to pixmap"""
while True:
try:
if self.capture.isOpened() and self.online:
# Read next frame from stream and insert into deque
status, frame = self.capture.read()
if status:
self.deque.append(frame)
else:
self.capture.release()
self.online = False
else:
# Attempt to reconnect
print('attempting to reconnect', self.camera_stream_link)
self.load_network_stream()
self.spin(2)
self.spin(.001)
except AttributeError:
pass
def spin(self, seconds):
"""Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""
time_end = time.time() + seconds
while time.time() < time_end:
QtGui.QApplication.processEvents()
def set_frame(self):
"""Sets pixmap image to video frame"""
if not self.online:
self.spin(1)
return
if self.deque and self.online:
# Grab latest frame
frame = self.deque[-1]
# Keep frame aspect ratio
if self.maintain_aspect_ratio:
self.frame = imutils.resize(frame, width=self.screen_width)
# Force resize
else:
self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
# Convert to pixmap and set to video frame
self.img = QtGui.QImage(self.frame, self.frame.shape[1], self.frame.shape[0], QtGui.QImage.Format_RGB888).rgbSwapped()
self.pix = QtGui.QPixmap.fromImage(self.img)
self.video_frame.setPixmap(self.pix) ### error comes in this line.
def get_video_frame(self):
return self.video_frame
def get_video_display_frame(self):
return self.video_display_frame
def message(self):
self.zone_config = ZoneConfig(self.camera_stream_link)
self.zone_config.show()
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.initUI()
def initUI(self):
self.setWindowTitle("VIDS")
titleBar_logo = os.path.join(dir, 'logos/logo.png')
self.setWindowIcon(QtGui.QIcon(titleBar_logo))
self.showMaximized()
self.screen_width = self.width()
self.screen_height = self.height()
# Layouts & frames
self.layout = QtGui.QVBoxLayout()
self.top_frame = QtGui.QFrame()
self.top_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
self.mid_frame = QtGui.QFrame()
self.mid_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
self.btm_frame = QtGui.QFrame()
self.btm_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
self.layout.addWidget(self.top_frame, 2)
self.layout.addWidget(self.mid_frame, 20)
self.layout.addWidget(self.btm_frame, 1)
self.layout.setContentsMargins(0,0,0,0)
self.layout.setSpacing(0)
self.setLayout(self.layout)
# Top frame
label = QtGui.QLabel('VIDS Dashboard')
label.setFont(QtGui.QFont('Arial', 20))
label.setAlignment(QtCore.Qt.AlignCenter)
self.top_layout = QtGui.QHBoxLayout()
self.top_layout.addWidget(label)
self.top_frame.setLayout(self.top_layout)
# Middle frame
self.mid_layout = QtGui.QStackedLayout()
# Create camera widgets
print('Creating Camera Widgets...')
self.one = CameraWidget(int(self.screen_width), int(self.screen_height), camera1, 'Camera 1')
self.two = CameraWidget(int(self.screen_width), int(self.screen_height), camera2, 'Camera 2')
cam_widget = Cam2(self.one, self.two, self)
self.mid_layout.addWidget(cam_widget)
self.mid_layout.setCurrentWidget(cam_widget)
self.mid_frame.setLayout(self.mid_layout)
# Bottom frame
btn1 = QtGui.QPushButton('1 Cam')
btn1.clicked.connect(self.button1)
btn2 = QtGui.QPushButton('2 Cams')
btn2.clicked.connect(self.button2)
self.btm_layout = QtGui.QHBoxLayout()
self.btm_layout.addStretch()
self.btm_layout.addWidget(btn1)
self.btm_layout.addWidget(btn2)
self.btm_frame.setLayout(self.btm_layout)
self.show()
def button1(self):
cam1_widget = Cam1(self.one, self)
self.mid_layout.addWidget(cam1_widget)
self.mid_layout.setCurrentWidget(cam1_widget)
def button2(self):
cam2_widget = Cam2(self.one, self.two, self)
self.mid_layout.addWidget(cam2_widget)
self.mid_layout.setCurrentWidget(cam2_widget)
class Cam1(QtGui.QWidget):
def __init__(self, one, parent=None):
super(Cam1, self).__init__(parent)
# Add widgets to layout
print('Adding widgets to layout...')
layout = QtGui.QGridLayout()
layout.addWidget(one.get_video_display_frame(),0,0,1,1)
self.setLayout(layout)
class Cam2(QtGui.QWidget):
def __init__(self, one, two, parent=None):
super(Cam2, self).__init__(parent)
# Add widgets to layout
print('Adding widgets to layout...')
layout = QtGui.QGridLayout()
layout.addWidget(one.get_video_display_frame(),0,0,1,1)
layout.addWidget(two.get_video_display_frame(),0,1,1,1)
self.setLayout(layout)
class ZoneConfig(QtGui.QWidget):
def __init__(self, camera, parent=None):
super(ZoneConfig, self).__init__(parent)
self.initUI(camera)
def initUI(self, camera):
self.setWindowTitle("VIDS")
titleBar_logo = os.path.join(dir, 'logos/logo.png')
self.setWindowIcon(QtGui.QIcon(titleBar_logo))
self.showMaximized()
self.screen_width = self.width()
self.screen_height = self.height()
self.camera = camera
# Layouts & frames
self.layout = QtGui.QVBoxLayout()
self.top_frame = QtGui.QFrame()
self.top_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
self.mid_frame = QtGui.QFrame()
# self.mid_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
self.btm_frame = QtGui.QFrame()
self.btm_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
self.layout.addWidget(self.top_frame, 2)
self.layout.addWidget(self.mid_frame, 20)
self.layout.addWidget(self.btm_frame, 1)
self.layout.setContentsMargins(0,0,0,0)
self.layout.setSpacing(0)
self.setLayout(self.layout)
# Top frame
self.label = QtGui.QLabel('VIDS - Zone Configuration')
self.label.setFont(QtGui.QFont('Arial', 20))
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.top_layout = QtGui.QHBoxLayout()
self.top_layout.addWidget(self.label)
self.top_frame.setLayout(self.top_layout)
# Middle frame
self.mid_layout = QtGui.QVBoxLayout()
self.mid_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
# Create camera widgets
print('Creating Camera Widgets...')
self.cam = CameraWidget(self.screen_width, self.screen_height, self.camera)
self.mid_layout = QtGui.QVBoxLayout()
self.mid_layout.addWidget(self.cam.get_video_frame())
self.mid_frame.setLayout(self.mid_layout)
self.mid_layout.setContentsMargins(0,0,0,0)
self.mid_layout.setSpacing(0)
self.mid_frame.setLayout(self.mid_layout)
# Bottom frame
btn = QtGui.QPushButton('Dashboard')
btn.clicked.connect(self.goMainWindow)
self.btm_layout = QtGui.QHBoxLayout()
self.btm_layout.addStretch()
self.btm_layout.addWidget(btn)
self.btm_frame.setLayout(self.btm_layout)
def goMainWindow(self):
self.close()
dir = os.path.dirname(__file__)
window = None
camera1 = 'streams/Fog.avi'
camera2 = 'streams/Overspeed.avi'
if __name__ == '__main__':
app = QtGui.QApplication([])
app.setStyle(QtGui.QStyleFactory.create("plastique")) # applies to entire window
window = MainWindow()
# window.show()
sys.exit(app.exec_())
I am using python 3, pyqt 4 and windows 10.
It seems that in the handling of threads it does not synchronize the elimination of the objects correctly, so a possible solution is to use sip to verify if the object was eliminated or not:
from PyQt4 import QtGui, QtCore
import sip
# ...
self.pix = QtGui.QPixmap.fromImage(self.img)
if not sip.isdeleted(self.video_frame):
self.video_frame.setPixmap(self.pix)