はじめに
Jupyter NotebookやJupyter QtConsoleでQtのQWidget
やQMainwindow
などをshow()
で動かそうとするとイベントループの関係でフリーズするか"Kernel died"になります。マジックコマンド%gui qt
でこれは解決しますが、毎回これを実行するのもだるいし、同じコードブロックで実行すると意味がないとか結構謎な挙動を示します。
ここでは、%gui qt
の実行を、Qtウィジェットの__init__
内で勝手にやってくれる便利な方法を紹介します。
ポイント
-
QWidget.__init__
の前にQApplication
を走らせる。 -
QApplication
を走らせる前に%gui qt
を走らせる。 - Garbage collectionで
QApplication
が持っていかれないようにする。
方法
まずは%gui qt
を走らせてからQApplication
を立ち上げる関数です。
from qtpy.QtWidgets import QApplication
APPLICATION = None
def gui_qt():
try:
from IPython import get_ipython
except ImportError:
get_ipython = lambda: False
shell = get_ipython()
if shell and shell.active_eventloop != "qt":
shell.enable_gui("qt")
return None
def get_app():
gui_qt()
app = QApplication.instance()
if app is None:
app = QApplication([])
global APPLICATION
APPLICATION = app
return app
関数gui_qt
内でipythonのシェルを取得し、enable_gui
で%gui qt
を実行します。Jupyter以外の環境でも問題ないように、try/exceptや場合分けを慎重に行います。
関数get_app
では、gui_qt
を呼んでからQApplicationを走らせます。すでに走っている場合、クラスメソッドQApplication.instance
でQApplication
のインスタンスが取得できるので、これがNoneの場合のみQApplication([])
で新しく用意します。
最後に、グローバル変数APPLICATION
に残しておくことで、garbage collectionを回避します。
このget_app
関数をコンストラクタ内で実行します。ここでまた、app = get_app()
のようにQApplication
インスタンスを取っておかないとなぜかフリーズします。
from qtpy.QtWidgets import QMainWindow
class MyWidget(QMainWindow):
def __init__(self):
app = get_app()
super().__init__()
以上で問題なく起動します。例えばmywidget
モジュールにこれがあれば
from mywidget import MyWidget
widget = MyWidget()
widget.show()
だけでOKです。起動したアプリケーションとJupyterが同時に動かせます。