From 6c8e07c3771ad35c2d01b924095d4f51d8a638f3 Mon Sep 17 00:00:00 2001 From: KIU Shueng Chuan Date: Thu, 11 Feb 2021 09:02:33 +0800 Subject: [PATCH 1/2] use 64-bits qimage size for PyQt5 5.15 PyQt5 5.15 has both methods sizeInBytes() (64-bits) and byteCount() (32-bits) the previous formulation would use 32-bits qimage size for PyQt5 5.15. --- pyqtgraph/functions.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 0281aacd..d7ddae0c 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -1358,21 +1358,23 @@ def imageToArray(img, copy=False, transpose=True): The array will have shape (width, height, (b,g,r,a)). """ fmt = img.format() - ptr = img.bits() - if QT_LIB in ['PySide', 'PySide2', 'PySide6']: - arr = np.frombuffer(ptr, dtype=np.ubyte) - else: + img_ptr = img.bits() + + if QT_LIB.startswith('PyQt'): + # sizeInBytes() was introduced in Qt 5.10 + # however PyQt5 5.12 will fail with: + # "TypeError: QImage.sizeInBytes() is a private method" + # note that sizeInBytes() works fine with: + # PyQt5 5.15, PySide2 5.12, PySide2 5.15 try: - # removed in Qt6 - nbytes = img.byteCount() - except AttributeError: - # introduced in Qt 5.10 - # however Python 3.7 + PyQt5-5.12 in the CI fails with - # "TypeError: QImage.sizeInBytes() is a private method" + # 64-bits size nbytes = img.sizeInBytes() - ptr.setsize(nbytes) - arr = np.asarray(ptr) - + except (TypeError, AttributeError): + # 32-bits size + nbytes = img.byteCount() + img_ptr.setsize(nbytes) + + arr = np.frombuffer(img_ptr, dtype=np.ubyte) arr = arr.reshape(img.height(), img.width(), 4) if fmt == img.Format_RGB32: arr[...,3] = 255 From 659392d4bb3322c0215942b99a4117fb409d9233 Mon Sep 17 00:00:00 2001 From: KIU Shueng Chuan Date: Thu, 11 Feb 2021 09:35:48 +0800 Subject: [PATCH 2/2] don't rely on QImage being non-const the previous formulation creates an ndarray and then creates an QImage over it w/o copying. this relies on the QImage thus created being non-const. if the QImage was (unintentionally) created as const, the subsequent QPainter render on the const QImage would trigger a COW. i.e. the original underlying ndarray wouldn't be updated. this is only an issue for PyQt bindings where const QImages can be created. --- pyqtgraph/tests/image_testing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyqtgraph/tests/image_testing.py b/pyqtgraph/tests/image_testing.py index 1fcfeb53..bfb8dc0f 100644 --- a/pyqtgraph/tests/image_testing.py +++ b/pyqtgraph/tests/image_testing.py @@ -137,12 +137,14 @@ def assertImageApproved(image, standardFile, message=None, **kwargs): QtGui.QApplication.processEvents() graphstate = scenegraphState(w, standardFile) - image = np.zeros((w.height(), w.width(), 4), dtype=np.ubyte) - qimg = fn.makeQImage(image, alpha=True, copy=False, transpose=False) + qimg = QtGui.QImage(w.size(), QtGui.QImage.Format.Format_ARGB32) + qimg.fill(QtCore.Qt.GlobalColor.transparent) painter = QtGui.QPainter(qimg) w.render(painter) painter.end() + image = fn.imageToArray(qimg, copy=False, transpose=False) + # transpose BGRA to RGBA image = image[..., [2, 1, 0, 3]]