While playing around with different ways to show a webcam feed (obtained using imageio/ffmpeg) in a PyQt4 window, I stumbled upon this answer. After implementing this in Python 2.7 as an ImageDisplayWidget
class (as summarized below), everything seems to work just fine: A window opens, showing my webcam feed without a glitch. If I close the window, everything is stopped and closed neatly.
But... Whenever I click anywhere outside this PyQt window (while it is showing the webcam feed), causing it to lose focus, Python.exe crashes with an unhandled win32 exception. The same happens when I try to resize the window.
I am probably making some kind of exceedingly silly beginner's mistake, but I just don't see it. Can anyone point me in the right direction? Am I breaking some basic rule of (Py)Qt or even Python?
Here's a minimal example:
import sys
import numpy
from PIL import Image, ImageQt # pillow
from PyQt4 import QtGui, QtCore
class DummyVideoGrabber(QtCore.QTimer):
signal_image_available = QtCore.pyqtSignal(QtGui.QImage)
def __init__(self):
super(DummyVideoGrabber, self).__init__()
self.timeout.connect(self.update_image)
self.counter = 0
def update_image(self):
# Dummy rgb image (in reality we get a numpy array from imageio's Reader)
self.counter += 1
numpy_image = numpy.zeros(shape=(480, 640, 3), dtype=numpy.int8)
numpy_image[:, :, self.counter%3] = 255
qt_image = ImageQt.ImageQt(Image.fromarray(numpy_image, mode='RGB'))
# Emit image
self.signal_image_available.emit(qt_image)
class ImageDisplayWidget(QtGui.QWidget):
"""
Custom widget that displays an image using QPainter.
Mostly copied from: https://stackoverflow.com/a/22355028/4720018
"""
def __init__(self, size_wxh=None, parent=None):
super(ImageDisplayWidget, self).__init__(parent)
self.image = QtGui.QImage()
def set_image(self, qimage, resize_window=False):
self.image = qimage
self.repaint()
def paintEvent(self, QPaintEvent):
if not self.image:
return
painter = QtGui.QPainter(self)
painter.drawImage(self.rect(), self.image, self.image.rect())
app = QtGui.QApplication(sys.argv)
# instantiate a display object
display = ImageDisplayWidget()
display.resize(640, 480)
display.show()
# instantiate a grabber object
grabber = DummyVideoGrabber()
grabber.signal_image_available.connect(display.set_image)
grabber.start(100) # timer interval in ms
# start the event loop
app.exec_()
I found that the crash can be prevented by adding a wasActiveWindow
flag (initialized to True
in the constructor) and encapsulating the drawImage()
call in some logic like so:
if self.isActiveWindow():
if self.wasActiveWindow:
painter.drawImage(self.rect(), self.image, self.image.rect())
self.wasActiveWindow = True
else:
self.wasActiveWindow = False
However, resizing the window still crashes python.