为什么当我的 PyQt window 变成 inactive/is 调整大小时 Python 崩溃?
Why does Python crash when my PyQt window becomes inactive/is resized?
在 PyQt4 window 中使用不同的方式显示网络摄像头源(使用 imageio/ffmpeg 获得)时,我偶然发现了 this answer。在 Python 2.7 中将此作为 ImageDisplayWidget
class 实现后(如下所述),一切似乎都运行良好: window 打开,显示我的网络摄像头源,没有出现故障.如果我关闭 window,一切都会停止并整齐地关闭。
但是...每当我单击此 PyQt window 之外的任何地方(当它显示网络摄像头源时),导致它失去焦点,Python.exe 崩溃并出现未处理的 win32 异常。当我尝试调整 window.
的大小时,也会发生同样的情况
我可能犯了一些极其愚蠢的初学者错误,但我就是没发现。谁能指出我正确的方向?我是否违反了 (Py)Qt 甚至 Python 的一些基本规则?
这是一个最小的例子:
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:
"""
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_()
我发现可以通过添加 wasActiveWindow
标志(在构造函数中初始化为 True
)并在某些逻辑中封装 drawImage()
调用来防止崩溃,如下所示:
if self.isActiveWindow():
if self.wasActiveWindow:
painter.drawImage(self.rect(), self.image, self.image.rect())
self.wasActiveWindow = True
else:
self.wasActiveWindow = False
但是,调整 window 的大小仍然会崩溃 python。
通过将 qt_image
的引用保持为 self.qt_image
:
解决了问题
...
# Emit image
self.qt_image = ImageQt.ImageQt(Image.fromarray(numpy_image, mode='RGB'))
self.signal_image_available.emit(self.qt_image)
...
这样它就可以正常工作了。不再需要 self.wasActiveWindow
解决方法。
仍然不确定为什么 不 保留引用会导致低级别 python 崩溃...
在 PyQt4 window 中使用不同的方式显示网络摄像头源(使用 imageio/ffmpeg 获得)时,我偶然发现了 this answer。在 Python 2.7 中将此作为 ImageDisplayWidget
class 实现后(如下所述),一切似乎都运行良好: window 打开,显示我的网络摄像头源,没有出现故障.如果我关闭 window,一切都会停止并整齐地关闭。
但是...每当我单击此 PyQt window 之外的任何地方(当它显示网络摄像头源时),导致它失去焦点,Python.exe 崩溃并出现未处理的 win32 异常。当我尝试调整 window.
的大小时,也会发生同样的情况我可能犯了一些极其愚蠢的初学者错误,但我就是没发现。谁能指出我正确的方向?我是否违反了 (Py)Qt 甚至 Python 的一些基本规则?
这是一个最小的例子:
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:
"""
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_()
我发现可以通过添加 wasActiveWindow
标志(在构造函数中初始化为 True
)并在某些逻辑中封装 drawImage()
调用来防止崩溃,如下所示:
if self.isActiveWindow():
if self.wasActiveWindow:
painter.drawImage(self.rect(), self.image, self.image.rect())
self.wasActiveWindow = True
else:
self.wasActiveWindow = False
但是,调整 window 的大小仍然会崩溃 python。
通过将 qt_image
的引用保持为 self.qt_image
:
...
# Emit image
self.qt_image = ImageQt.ImageQt(Image.fromarray(numpy_image, mode='RGB'))
self.signal_image_available.emit(self.qt_image)
...
这样它就可以正常工作了。不再需要 self.wasActiveWindow
解决方法。
仍然不确定为什么 不 保留引用会导致低级别 python 崩溃...