はじめに

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が同時に動かせます。

广告
将在 10 秒后关闭
bannerAds