为什么 PyQt4 在 Jupyter 和 IPython 笔记本之间表现不同?
Why does PyQt4 behave differently between Jupyter and IPython notebook?
我已经基于 PyQt4 创建了一个带有 GUI 的大型 python 程序。我想在 IPython 笔记本(Python 2.7 在 windows 上的旧安装)、Jupyter 笔记本(Python 3.5 最近安装了 Python 3.5 Anaconda),并作为在命令行上传递的 python 程序。我在 运行 Jupyter notebook 中的代码中遇到问题(直接在底部查看)。
我的模块 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 notebook 中使用相同的命令,window 打开,但在允许任何用户交互之前冻结。所以我认为 Jupyter notebook 没有正确处理事件,因为我没有告诉它这样做。我发现的一种方法是在 运行 之前执行命令 %pylab
我的代码。但是,我经常运行陷入与此相关的问题,例如当运行宁%pylab
和%matplotlib inline
在开始我的程序之前直接连续,这导致我再次冻结加载我的代码(奇怪的是,颠倒这两个神奇命令的顺序再次起作用)。另外,如果可以避免的话,我不想强迫我的程序的用户在每个新笔记本中执行 %pylab(也是因为我相信这需要安装 matlab,这不是我的程序的要求)。
我必须在 mymodule.py 中添加什么代码才能与 Jupyter notebook 中描述的用户代码兼容?谁能更清楚地解释 IPython notebook 和 Jupyter notebook 如何以不同方式管理 QEventLoop/QApplication (或这里的重要概念),以及魔术命令如何与此混淆?因此,我害怕我的程序中存在隐藏的错误,并希望使其尽可能健壮,以免让用户感到沮丧。
这是一个可行的解决方案,它允许 运行 代码而不区分不同的 IPython / Jupyter 版本和 'raw' Python。我的 __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 上的脚本 运行ning 然后只需要这个:
import mymodule # imports the above code
from PyQt4 import QtCore, QtGui
if __name__ == '__main__':
QtGui.QApplication.instance().exec_()
我没有发现这不起作用的用例。
我已经基于 PyQt4 创建了一个带有 GUI 的大型 python 程序。我想在 IPython 笔记本(Python 2.7 在 windows 上的旧安装)、Jupyter 笔记本(Python 3.5 最近安装了 Python 3.5 Anaconda),并作为在命令行上传递的 python 程序。我在 运行 Jupyter notebook 中的代码中遇到问题(直接在底部查看)。
我的模块 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 notebook 中使用相同的命令,window 打开,但在允许任何用户交互之前冻结。所以我认为 Jupyter notebook 没有正确处理事件,因为我没有告诉它这样做。我发现的一种方法是在 运行 之前执行命令 %pylab
我的代码。但是,我经常运行陷入与此相关的问题,例如当运行宁%pylab
和%matplotlib inline
在开始我的程序之前直接连续,这导致我再次冻结加载我的代码(奇怪的是,颠倒这两个神奇命令的顺序再次起作用)。另外,如果可以避免的话,我不想强迫我的程序的用户在每个新笔记本中执行 %pylab(也是因为我相信这需要安装 matlab,这不是我的程序的要求)。
我必须在 mymodule.py 中添加什么代码才能与 Jupyter notebook 中描述的用户代码兼容?谁能更清楚地解释 IPython notebook 和 Jupyter notebook 如何以不同方式管理 QEventLoop/QApplication (或这里的重要概念),以及魔术命令如何与此混淆?因此,我害怕我的程序中存在隐藏的错误,并希望使其尽可能健壮,以免让用户感到沮丧。
这是一个可行的解决方案,它允许 运行 代码而不区分不同的 IPython / Jupyter 版本和 'raw' Python。我的 __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 上的脚本 运行ning 然后只需要这个:
import mymodule # imports the above code
from PyQt4 import QtCore, QtGui
if __name__ == '__main__':
QtGui.QApplication.instance().exec_()
我没有发现这不起作用的用例。