2016/11/26(土) [n年前の日記]
#2 [python] Windows + PySide + Pillow(PIL)の組み合わせでImageQtの動作が怪しい
Windows10 x64 + Python 2.7.12 + PySide 1.2.4 + Pillow(PIL) 3.4.2 の環境で、Pillow に入ってる ImageQt を使って PIL Image を PySide の QImage に変換しようとしていたのだけど、Python が強制終了・crashしまくることに気づいて悩んだり。
ちなみに、PySide 1.2.4 と PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe を両方インストールしてある環境。
例えば、以下のソースと画像を使って実験すると…。
_imageqt_test.py
_tmp_grayscale.png
_tmp_indexed.png
_tmp_rgb.png
_tmp_rgba.png
やってることは、
コレを実行すると、Python が死ぬ。
これがまたよく分からんのだけど、PySide を使わずに PyQt4 を使うと Python が死なずに動いちゃう。
最初のあたりの use_pyside = True を use_pyside = False にすると、PyQt4 を使って動かせる。
各行をコメントアウトして動作確認してみたけど、どうも QPixmap.fromImage() を呼んだ時点で Python が死んでる感じ。QPixmap.fromImage() をコメントアウトすると Python が死なないわけで。
すると ImageQt が QImage を返せていないのか、おかしな QImage を QPixmap.fromImage() に渡してしまっているのか。と思ったけど、 _Pillow/ImageQt.py at master - python-pillow/Pillow を眺めると、ImageQt は QImage を継承してるわけで…。github上の ImageQt.py と、手元の ImageQt.py は、どちらも似たようなソースだし…。
アレ? 待てよ? 微妙に違うな。
_Pillow 2.9 causes PySide application to crash (whereas 2.8.2 does not) - Issue #1370 - python-pillow/Pillow
上記のバグ報告ページで、self.__data = im_data['data'] を追加してソレを使って QImage を生成すると落ちない、という報告が。手元の ImageQt.py はその行が入ってない。
手元の ImageQt.py にも試しに反映させてみたら、落ちなくなった。
しかし困ったな…。これでは他の環境にスクリプトを持っていった際に ImageQt が使えないではないか…。一々、「ImageQt.py をエディタで開いて該当行を修正してください」とお願いするのもアレだし…。
ちなみに、PySide 1.2.4 と PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe を両方インストールしてある環境。
例えば、以下のソースと画像を使って実験すると…。
_imageqt_test.py
u""" Pillow(PIL)のImageQtについて動作確認. 動作確認 : Windows10 x64 + Python 2.7.12 + PySide 1.2.4 + Pillow 3.4.2 """ use_pyside = True import sys if use_pyside: # use PySide # import PySide # sys.modules['PyQt4'] = PySide # ここでPyQt4を使わないようにしているはず…なのだが… from PySide import QtGui from PySide import QtCore sys.modules['PyQt4.QtGui'] = QtGui sys.modules['PyQt4.QtCore'] = QtCore from PySide.QtCore import * # NOQA from PySide.QtGui import * # NOQA else: # use PyQt4 from PyQt4.QtCore import * # NOQA from PyQt4.QtGui import * # NOQA from PIL import Image from PIL import ImageQt class MyWidget(QWidget): def __init__(self, *argv, **keywords): super(MyWidget, self).__init__(*argv, **keywords) layout = QGridLayout() lst = [ "./tmp_grayscale.png", "./tmp_indexed.png", "./tmp_rgb.png", "./tmp_rgba.png" ] for i, fpath in enumerate(lst): im = Image.open(fpath) old_im_mode = im.mode if im.mode != "RGBA": im = im.convert("RGBA") print("im.mode %s -> %s" % (old_im_mode, im.mode)) qim1 = ImageQt.ImageQt(im) qim2 = QImage(qim1) pm = QPixmap.fromImage(qim2) lbl = QLabel(old_im_mode, self) lbl.setPixmap(pm) layout.addWidget(lbl, i / 2, i % 2) self.setLayout(layout) if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.show() sys.exit(app.exec_())
_tmp_grayscale.png
_tmp_indexed.png
_tmp_rgb.png
_tmp_rgba.png
やってることは、
- グレースケール、インデックスカラー、RGB、RGBA、の4つ画像を、PIL の Image.open() で読み込んで、
- PIL の ImageQt を使って PySide の QImage に変換してから、
- 得られた QImage を QPixmap に変換して、ラベルウィジェットを使って表示。
コレを実行すると、Python が死ぬ。
これがまたよく分からんのだけど、PySide を使わずに PyQt4 を使うと Python が死なずに動いちゃう。
最初のあたりの use_pyside = True を use_pyside = False にすると、PyQt4 を使って動かせる。
各行をコメントアウトして動作確認してみたけど、どうも QPixmap.fromImage() を呼んだ時点で Python が死んでる感じ。QPixmap.fromImage() をコメントアウトすると Python が死なないわけで。
すると ImageQt が QImage を返せていないのか、おかしな QImage を QPixmap.fromImage() に渡してしまっているのか。と思ったけど、 _Pillow/ImageQt.py at master - python-pillow/Pillow を眺めると、ImageQt は QImage を継承してるわけで…。github上の ImageQt.py と、手元の ImageQt.py は、どちらも似たようなソースだし…。
アレ? 待てよ? 微妙に違うな。
_Pillow 2.9 causes PySide application to crash (whereas 2.8.2 does not) - Issue #1370 - python-pillow/Pillow
上記のバグ報告ページで、self.__data = im_data['data'] を追加してソレを使って QImage を生成すると落ちない、という報告が。手元の ImageQt.py はその行が入ってない。
手元の ImageQt.py にも試しに反映させてみたら、落ちなくなった。
しかし困ったな…。これでは他の環境にスクリプトを持っていった際に ImageQt が使えないではないか…。一々、「ImageQt.py をエディタで開いて該当行を修正してください」とお願いするのもアレだし…。
[ ツッコむ ]
以上です。