I have a large image composed of subimages I have received from another program, I will eventually need to use the subimages as icons on buttons. I understand the math to access the bits + bytes of the QImage, it is described clearly in the below C++ question. What I don't understand is how to obtain the offseted bits/memory location with PySide2.
Dividing QImage to smaller pieces
It's a simple conversion of the above C++ to Python:
def myCreateSubImage(largeImage, subRect):
nOffset = subRect.x() * largeImage.depth() / 8 + subRect.y() * largeImage.bytesPerLine()
return QImage(largeImage.bits() + nOffset, subRect.width(), subRect.height(), largeImage.bytesPerLine(), subRect.format())
However:
Using "largeImage.bits() + nOffset" gives me a "No ""+"" function for <memory at 0x0000021EF8ABC700>" error.
Trying to treat it like a Python memoryview object like "(largeImage.bits())[nOffset]" gives me a "TypeError: a bytes-like object is required, not 'NoneType'"
Coercing the image data to a QByteArray hasn't worked, or I'm not doing it correctly. Trying to create it with a constructor (ie: QByteArray(largeImage.bits()) ) tells me there's no constructor that takes a memoryview. Trying QByteArray.fromRawData(largeImage.bits()) tells me the same thing.
The correct solution depends on whether PySide or PyQt is being used. When accessing the raw data of a QImage, PySide uses a python memoryview object, whereas PyQt uses a sip.array object (which is obtained via a sip.voidptr). However, both these objects provide support for Python's buffer protocol, so they can be used in the same way.
To fix your example, you must therefore create a slice of the array of image data that begins at the calculated offset. It's more efficient to use constBits for this as (unlike bits) it does not make a deep copy of the data first.
Below is simple demo script that works with both PySide and PyQt. When used with the sample image from the other question, it produces this result (for grid-ref [2, 4]):
from PySide2 import QtCore, QtGui, QtWidgets
# from PyQt5 import QtCore, QtGui, QtWidgets
image = QtGui.QImage('sample.png')
width, height = 192, 108
row, column = 2, 4
rect = QtCore.QRect(column * width, row * height, width, height)
offset = rect.x() * image.depth() // 8 + rect.y() * image.bytesPerLine()
bits = image.constBits()
if QtCore.__name__.startswith('PyQt'):
bits = bits.asarray(image.sizeInBytes())
subimage = QtGui.QImage(bits[offset:], width, height,
image.bytesPerLine(), image.format())
app = QtWidgets.QApplication(['Test'])
label = QtWidgets.QLabel()
label.setGeometry(600, 100, width + 50, height + 50)
label.setAlignment(QtCore.Qt.AlignCenter)
label.setPixmap(QtGui.QPixmap.fromImage(subimage))
label.show()
app.exec_()