为什么PyQt4在Jupyter和IPython笔记本之间表现不同?

时间:2017-05-29 21:33:51

标签: python pyqt4 jupyter-notebook ipython-notebook qeventloop

我已经使用基于PyQt4的GUI创建了一个大型python程序。我希望这个包能够在IPython笔记本(在Windows上安装Python 2.7的旧安装),Jupyter笔记本(最近安装了Anaconda的Python 3.5)以及作为在命令行上传递的python程序中运行。我在Jupyter笔记本中运行代码时遇到问题(直接在底部看到)。

我的模块mymodule.py看起来像这样(非常简化,在许多其他python文件显示之前大约10k行):

from PyQt4 import QtCore, QtGui

class MyModule(object):
    def __init__(self):
        self.window = QtGui.QMainWindow()
        self.window.show()

命令行的典型用法是

python myscript.py

使用以下文件myscript.py

from PyQt4 import QtCore, QtGui
import mymodule

m = mymodule.MyModule()

APP = QtGui.QApplication.instance()
APP.exec_()

这很好用。我知道APP.exec_()需要启动一些通过Gui交互事件工作的EventLoop。

在IPython笔记本中,用户通常会

import mymodule
m = mymodule.MyModule() # opens the gui
# this still leaves the console active to allow things like this:
m.change_color("red")

我可以毫无问题地运行它,我知道IPython会以某种方式处理场景背后的EventLoop。

现在,在Jupyter笔记本中运行相同的命令,会打开一个窗口,但在允许任何用户交互之前会冻结。因此我认为Jupyter笔记本不能正确处理事件,因为我没有告诉它这样做。我找到的一种方法是在运行代码之前执行命令%pylab。但是,我经常遇到与此相关的问题,例如在开始我的程序之前直接连续运行%pylab%matplotlib inline时,一旦我加载代码,这会导致再次冻结(好奇地,颠倒顺序)这两个神奇的命令再次起作用)。另外,如果可以避免,我不想强​​迫我的程序用户在每个新笔记本中执行%pylab(也因为我认为这需要安装matlab,这不是我的程序的要求)。

我必须在mymodule.py中添加哪些代码才能使Jupyter笔记本中描述的用户代码兼容?任何人都可以更清楚地解释IPython笔记本和Jupyter笔记本如何管理QEventLoop / QApplication(或者这里的重要概念)不同,以及魔术命令如何搞乱这个?我害怕我的程序中存在隐藏的错误,并且希望尽可能地使其变得不那么挫败用户。

1 个答案:

答案 0 :(得分:0)

这是一个工作解决方案,允许运行代码而不区分不同的IPython / Jupyter版本和' raw'蟒蛇。我的__init__.py文件在开头包含此部分:

# enable IPython QtGui support if needed
try:
    from IPython import get_ipython
    get_ipython().magic('gui qt')
except BaseException as e:
    # issued if code runs in bare Python
    print('Could not enable IPython gui support: %s.' % e)

# get QApplication instance
from PyQt4 import QtCore, QtGui
APP = QtGui.QApplication.instance()
if APP is None:
    print('Creating new QApplication instance "mymodule"')
    APP = QtGui.QApplication(['mymodule'])

在原始Python上运行的脚本只需要这个:

import mymodule  # imports the above code
from PyQt4 import QtCore, QtGui
if __name__ == '__main__':
    QtGui.QApplication.instance().exec_()

我发现这不起作用的用例。