How can i make ipythons RichIPythonWidget use pysi

2019-08-14 19:59发布

问题:

I have an application that i build using pyinstaller, and it uses PySide for its Qt Gui. I included an interactive prompt by embedding an ipython qtconsole. This breaks the builds created by pyinstaller.

Here is a minimal (non-)working example:

from PySide.QtGui import *

from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager
from IPython.lib import guisupport

class IPythonWidget(RichIPythonWidget):
    def __init__(self, parent=None, **kwargs):
        super(self.__class__, self).__init__(parent)
        self.app = app = guisupport.get_app_qt4()
        self.kernel_manager = kernel_manager = QtInProcessKernelManager()
        kernel_manager.start_kernel()

        self.kernel = kernel = kernel_manager.kernel
        kernel.gui = 'qt4'
        self.kernel_client = kernel_client = kernel_manager.client()
        kernel_client.start_channels()

if __name__ == '__main__':
    app = QApplication([])
    i = IPythonWidget()
    i.show()
    app.exec_()

When run directly from source (python mwe.py), it pops up an ipython qt console window. When i bundle this with pyinstaller in one directory and run the exe, i get this:

Traceback (most recent call last):
  File "<string>", line 3, in <module>
  File "C:\Python27\Lib\site-packages\PyInstaller\loader\pyi_importers.py", line 270, in load_module
    exec(bytecode, module.__dict__)
  File "H:\Home\pydd2swid\build\mwe\out00-PYZ.pyz\IPython.qt.console.rich_ipython_widget", line 8, in <module>
  File "C:\Python27\Lib\site-packages\PyInstaller\loader\pyi_importers.py", line 270, in load_module
    exec(bytecode, module.__dict__)
  File "H:\Home\pydd2swid\build\mwe\out00-PYZ.pyz\IPython.external.qt", line 23, in <module>
  File "H:\Home\pydd2swid\build\mwe\out00-PYZ.pyz\IPython.external.qt_loaders", line 296, in load_qt
ImportError:
    Could not load requested Qt binding. Please ensure that
    PyQt4 >= 4.7, PyQt5 or PySide >= 1.0.3 is available,
    and only one is imported per session.

    Currently-imported Qt library:   'pyqtv1'
    PyQt4 installed:                 False
    PyQt5 installed:                 False
    PySide >= 1.0.3 installed:       False
    Tried to load:                   ['pyside', 'pyqt', 'pyqt5']

and when i build a single executable (pyinstaller -F mwe.py) and run it, i get this:

WARNING: file already exists but should not: C:\Users\SARNOW4\AppData\Local\Temp\_MEI62362\Include\pyconfig.h
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python27\Lib\site-packages\PyInstaller\loader\pyi_importers.py", line 270, in load_module
    exec(bytecode, module.__dict__)
  File "H:\Home\pydd2swid\build\mwe\out00-PYZ.pyz\PySide", line 41, in <module>
  File "H:\Home\pydd2swid\build\mwe\out00-PYZ.pyz\PySide", line 11, in _setupQtDirectories
  File "H:\Home\pydd2swid\build\mwe\out00-PYZ.pyz\PySide._utils", line 93, in get_pyside_dir
  File "C:\Python27\Lib\site-packages\PyInstaller\loader\pyi_importers.py", line 409, in load_module
    module = imp.load_module(fullname, fp, filename, self._c_ext_tuple)
RuntimeError: the sip module has already registered a module called PyQt4.QtCore

It seems that the way pyinstaller hooks the import mechanism does not work with ipythons qt_loaders. How can i fix this?
I am using pyinstaller 2.1, ipython 3.0, python 2.7 (32-bit) on Windows 7.

回答1:

You can fix it in two ways:

1) Overwrite the function load_qt included in IPython.external.qt_loaders, e.g.:

def load_qt(api_options):
    from PySide import QtCore, QtGui, QtSvg
    return QtCore, QtGui, QtSvg, 'pyside'

so, you will force to choose the PySide Module.

2) Another solution without overwriting the IPython Module installed, would be passing the function as reference before importing the IPython widget, e.g.

def new_load_qt(api_options):
    from PySide import QtCore, QtGui, QtSvg
    return QtCore, QtGui, QtSvg, 'pyside'

from IPython.external import  qt_loaders
qt_loaders.load_qt = new_load_qt

from IPython.qt.console.rich_ipython_widget import RichIPythonWidget

Now it should work.

It was tested using the PyInstaller Develop Version