PySide6使用cleanupFunction构造QImage触发段错误的问题排查
QImage搭配cleanupFunction构造时触发段错误的问题排查与解决
问题重现
使用空字节数组搭配清理函数构造QImage时直接触发段错误:
>>> from PySide6 import QtGui as qtg >>> qtg.QImage(b'', 10, 10, qtg.QImage.Format.Format_ARGB32, lambda: print(x)) [1] 119240 segmentation fault python3
不传入清理函数时则能正常初始化:
>>> qtg.QImage(b'', 10, 10, qtg.QImage.Format.Format_ARGB32) <PySide6.QtGui.QImage(QSize(10, 10),format=QImage::Format_ARGB32,depth=32,devicePixelRatio=1,bytesPerLine=40,sizeInBytes=400) at 0x7fc7af514fc0>
尝试将第一个参数改为字符串后,触发类型错误(字符串对应的构造签名不支持清理函数):
>>> qtg.QImage('', 10, 10, qtg.QImage.Format.Format_ARGB32, lambda: print(1)) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'PySide6.QtGui.QImage.__init__' called with wrong argument types: PySide6.QtGui.QImage.__init__(str, int, int, Format, function) Supported signatures: PySide6.QtGui.QImage.__init__() PySide6.QtGui.QImage.__init__(str, int, int, PySide6.QtGui.QImage.Format) PySide6.QtGui.QImage.__init__(str, int, int, int, PySide6.QtGui.QImage.Format) PySide6.QtGui.QImage.__init__(Union[PySide6.QtGui.QImage, str]) PySide6.QtGui.QImage.__init__(bytes, int, int, int, PySide6.QtGui.QImage.Format, Optional[Callable] = None, Optional[int] = None) PySide6.QtGui.QImage.__init__(bytes, int, int, PySide6.QtGui.QImage.Format, Optional[Callable] = None, Optional[int] = None) PySide6.QtGui.QImage.__init__(Union[str, bytes, os.PathLike], Optional[bytes] = None) PySide6.QtGui.QImage.__init__(PySide6.QtCore.QSize, PySide6.QtGui.QImage.Format) PySide6.QtGui.QImage.__init__(int, int, PySide6.QtGui.QImage.Format) PySide6.QtGui.QImage.__init__(Iterable)
错误原因
从签名可知,支持清理函数的QImage构造函数只有两个基于bytes的版本,问题根源有两点:
- 字节数据长度不足:你传入的
b''是空数组,而10×10的ARGB32格式图片需要10×10×4=400字节的数据。无清理函数时QImage会自动分配内存兜底,但搭配清理函数时,QImage会认为你传入的是外部内存,直接访问时因内存越界触发段错误。 - 参数逻辑隐含风险:虽然有一个签名不需要传入
bytesPerLine,但如果字节数组的实际行字节数和QImage默认计算值(宽×像素字节数)不匹配,也可能导致内存访问问题;你最初的调用参数位置虽符合签名,但空数据本身就是非法输入。
正确用法
使用清理函数时,必须保证传入的字节数组长度足够,且参数匹配对应签名:
from PySide6 import QtGui as qtg # 生成符合要求的字节数据:10x10 ARGB32,共400字节 pixel_data = b'\x00' * 400 # 用法1:不带bytesPerLine的签名(QImage自动计算行字节数) image1 = qtg.QImage( pixel_data, 10, 10, qtg.QImage.Format.Format_ARGB32, lambda: print("清理函数已执行") ) # 用法2:带bytesPerLine的签名(显式指定行字节数,需和数据匹配) image2 = qtg.QImage( pixel_data, 10, 10, 40, # 10×4=40,对应ARGB32每个像素4字节 qtg.QImage.Format.Format_ARGB32, lambda: print("带行字节数的清理函数已执行") ) print(image1) print(image2)
关于PySide文档质量
你不是唯一一个吐槽的人,不少开发者都觉得PySide的文档存在描述模糊、签名说明不够直观的问题,很多时候需要结合Qt C++官方文档和实际测试才能准确理解API用法。
内容的提问来源于stack exchange,提问作者Neat
相关产品推荐
相关产品推荐

