[PyQt] QImage and the GIL

Erik Hvatum ice.rikh at gmail.com
Fri Mar 13 23:19:46 GMT 2015


On Thu, Mar 12, 2015 at 2:58 PM, Martin Altmayer <martin.altmayer at web.de>
wrote:

> As far as I understand the docs at
> http://pyqt.sourceforge.net/Docs/PyQt5/pyqt4_differences.
> html#releasing-the-gil
> the GIL needs to be released before I call a Qt function from Python. And
> as far as I understand qimage.sip the GIL is not released for QImage
> methods.
>
> Am 12.03.2015 20:52 schrieb Jason H:
>
>  The GIL is not in effect when you are in C++ code. If you are in
>> QImage::scale, then you aren't experiencing GIL.
>>
>>
>>  Sent: Thursday, March 12, 2015 at 1:24 PM
>>> From: "Martin Altmayer" <martin.altmayer at web.de>
>>> To: pyqt at riverbankcomputing.com
>>> Subject: [PyQt] QImage and the GIL
>>>
>>> Hello,
>>>
>>> I use a separate thread to open and scale a large set of images.
>>> Unfortunately, with PyQt5 the main thread blocks while an image is
>>> loaded (it did work in PyQt4). I believe that the reason is that the
>>> QImage-constructor and QImage.scale do not release the GIL. Wouldn't it
>>> be better if they did?
>>>
>>> Thanks,
>>> Martin <http://www.riverbankcomputing.com/mailman/listinfo/pyqt>
>>
>>
Hi Martin,

It looks like you are correct:
https://github.com/baoboa/pyqt5/blob/dcfb8fa0f23e4f962e753bb1b336c5783c0c7cd9/sip/QtGui/qimage.sip
(this is not my repo - just linking to it for convenience).  The only two
functions in there that do ReleaseGIL are stream operators (overloaded
bitshift operators) for serializing and deserializing to/from a
QDataStream.  This might potentially be useful if you resort to using the
Python multiprocessing module for decoding and scaling images that then
need to be streamed back to the main process, perhaps.

If you need more speed just for your own purposes, then, in that sip file,
replacing

 bool load(QIODevice *device, const char *format);bool load(const
QString &fileName, const char *format = 0);bool loadFromData(const
uchar *data /Array/, int len /ArraySize/, const char *format = 0);bool
loadFromData(const QByteArray &data, const char *format = 0);bool
save(const QString &fileName, const char *format = 0, int quality =
-1) const;bool save(QIODevice *device, const char *format = 0, int
quality = -1) const;

 with

bool load(QIODevice *device, const char *format) /ReleaseGIL/;bool
load(const QString &fileName, const char *format = 0)
/ReleaseGIL/;bool loadFromData(const uchar *data /Array/, int len
/ArraySize/, const char *format = 0) /ReleaseGIL/;bool
loadFromData(const QByteArray &data, const char *format = 0)
/ReleaseGIL/;bool save(const QString &fileName, const char *format =
0, int quality = -1) const /ReleaseGIL/;bool save(QIODevice *device,
const char *format = 0, int quality = -1) const /ReleaseGIL/;

and then rebuilding PyQt5 should make the GIL be released.  You might also
want to give the same treatment to QImage's constructors.  I expect that
these functions can safely drop the GIL; if this is not the case, it would
be interesting to know why :)

In my own code, I work around this by using a ctypes freeimage wrapper:
https://github.com/erikhvatum/zplab/blob/master/freeimage.py (this is my
repo, but credit goes to Dr. Zach Pincus for this file).  It drops the GIL
in C functions such as image reading and writing, courtesy of ctypes doing
that by default.  Going to/from the numpy arrays that the wrapper uses to
QImage is straightforward.  For 24-bit RGB images:
Qt.QImage(sip.voidptr(array_from_freeimage.ctypes.data),
array_from_freeimage.shape[1], array_from_freeimage.shape[0],
Qt.QImage.Format_RGB888).  Keep a reference to array_from_freeimage as long
as you may need the resulting QImage.

Cheers,
Erik
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20150313/ae05c0b7/attachment.html>


More information about the PyQt mailing list