LoginSignup
1
0

Pythonで〇×ゲームのAIを一から作成する その68 ipywidgets と ipympl の使い方とイベント駆動型プログラミング

Last updated at Posted at 2024-03-31

目次と前回の記事

これまでに作成したモジュール

以下のリンクから、これまでに作成したモジュールを見ることができます。

ルールベースの AI の一覧

ルールベースの AI の一覧については、下記の記事を参照して下さい。

〇×ゲームの GUI の入力機能

前回の記事では、〇×ゲームの ゲーム盤画像で表示 するという、GUI出力機能実装 しました。今回の記事では、GUI入力機能 を実装しますが、そのためには、〇×ゲームの 具体的入力機能 について 検討 する 必要あります。まず、どのような入力機能が必要になるかについて少し考えてみて下さい。

〇×ゲームの GUI の入力機能の検討

〇×ゲームの 入力機能 について 最低限必要 になるのは、以下の機能 です。

  • 着手 を行う 座標を入力 する 機能

他にも、必須ではありませんが、下記 のような 機能 があると 便利 でしょう。

  • ゲーム を最初から やり直す 機能
  • 直前の着手取り消す待った を行う機能
  • 決着がついたゲームの 対戦経過表示 する リプレイ の機能

本記事 では、上記の機能実装 することにしますが、他にも、あると良さそうだと思った機能があれば列挙して実装してみて下さい。また、本記事でも上記以外の機能を思いついた場合は、実装することにします。

具体的な入力機能の検討

次に、それぞれ機能 について、具体的 にどのような方法で入力を行うについて 検討 を行う 必要あります。それぞれについて、少し考えてみて下さい。

着手を行う座標を入力する機能

CUI では、着手 を行う 座標 を、テキストボックス に座標を表す 文字列で入力 しました。GUI では、ゲーム盤画像で表示 されるので、着手 を行いたい マス直接マウスクリック するのが最も 直観的わかりやすく入力の手間簡単 だと思います。

他にも、メニュー から 座標を選択 するなどの 方法考えられます。本記事よりも良い方法が思いついた方は、その方法で実装してみると良いでしょう。

この機能実装 するためには、下記 のようなプログラムを 記述 する 必要あります

  • ゲーム盤画像の上クリック した時に、以下の処理行う
    • クリック した ゲーム盤マスの座標知る
    • その座標着手行う
  • ゲーム盤画像更新 する

matplotlib は、上記 のような 処理行うための機能備えています が、残念ながら、JupyterLab 上で matplotlib を使って 画像を描画 した場合は、そのままでは その機能利用 することは できません具体的 には、JupyterLabmatplotlibplt.show()描画した画像 は、後から その 描画内容変更 したり、クリック などの 操作 によって、特定の処理 を行うプログラムを 記述 することは できません。実際に、前回の記事play メソッドに〇×ゲームの 画像を描画 する 機能を実装 しましたが、その際には、着手行うたび に、新しいゲーム盤の画像描画 しました。

上記 のような 処理を行う ためには、今回の記事で紹介する、ipywidgetsipympl などの モジュールインストール して 利用 する 必要あります

本記事では取り扱いませんが、JupyterLab 以外 の、Pythonプログラムを実行 する 環境matplotlib実行 した場合や、JupyterLab で上記の ipywidgetsipympl 以外モジュールを利用 した場合は、本記事 で紹介するプログラムとは 異なる記述行う必要あります

その他の機能

ゲームを最初からやり直す」、「待ったを行う」、「リプレイを表示する」などの 機能 は、画面上 にそれらの 機能を呼び出す ための ボタン などを 描画 し、マウスで ボタンをクリック した際に その機能を実行 するという 方法実装 するのが 一般的 です。

Python には、そういった GUIアプリケーション作成 するための 様々なモジュールあります が、本記事では、JupyterLab 上 で、ボタンやテキストボックスなどの GUI による 入力処理 を行うことができる ipywidgets という モジュール利用 することにします。

ipywidgets による GUI

ipywidgets は、IPython 上widgets利用 できるようにするための モジュール です。

IPython とは、Pythonプログラム を、対話型に実行 するための シェル(プログラム)のことで、JupyterLabウェブブラウザで動作 するように IPython を拡張 したものです。

なお、Python のプログラムを 実行 するための シェル は、IPython 以外 にも あります が、IPythonそれらの中 でも、便利な機能豊富に用意 されているため、 良く使われます

IPython のように、ユーザ と、コンピュータOSPython などの プログラム言語つなぐ役割 を持つ プログラム の事を シェル と呼びます。

Windows の場合は、コマンドプロンプト や、Windows Power ShellユーザーWindows の OSつなぐ役割 を持つ シェル です。

JupyterLab は、セル入力 した プログラムPython に伝え、その 実行結果表示 するという形で ユーザPythonつなぐ処理行います

ウィジェット(widget)1とは、ボタンテキストボックス などの、GUI構成する部品 のことを 表す用語 です。ipywidgets は、その名前が示すように、IPython 上で、ウィジェット扱う ことが できるようにする ための モジュール です。

ipywidget の詳細については、下記のリンク先を参照して下さい。

なお、ipywidget は非常に 多彩な機能 を持つ モジュール ですが、本記事 ではその中で、〇×ゲームの GUI作るために必要な機能主に紹介 します。ipywidget有名なモジュール で、使い方説明 する ウェブページ多数ある ので、他の機能について知りたい方は調べてみると良いでしょう。

ipywidgets のインストール

ipywidgets は、matplotlib と同様 の、下記の手順 で、Anaconda Navigator から インストール することが できます

  1. Anaconda Navigator を立ち上げる
  2. 左の Environmentsクリック して表示される 仮想環境の一覧 から marubatsuクリック すると、右に marubatsu仮想環境インストール されている モジュールの一覧表示 される
  3. 上のメニュー から「Not installed」を 選択 し、その右の「Search packages」と表示された テキストボックス に「ipywidget」を 入力 する
  4. 下に ipywidget が表示 されるので、その 左のチェックボックスチェック し、下の「Applyボタンクリック する。
  5. Install packages」という パネルが表示 され、しばらく待つと「Applyボタン がクリックできるようになるので クリック する。

ipywidgets のインポート

ipywidgets利用 するためには、インポート行う必要あります。本記事では、下記のプログラムのように、widgets という 名前インポート することにします。

import ipywidgets as widgets

モジュールのバージョン

本記事 では ipywidgetsバージョン 8.1.2インストール して 利用 します。モジュールバージョンが異なる と、モジュールの機能若干異なる 場合があります。本記事プログラムを実行 した際に、エラーが発生 したり、表示が異なる 場合は、ipywidgetsバージョンを確認 して下さい。例えば、7 以前バージョン では 利用できない機能 を使ってプログラムを 記述する可能性あります

8.1.2 以降 のバージョンであれば、おそらく問題なく動作する と思います。

モジュールのバージョンの確認方法

モジュールバージョン確認する方法 をいくつか 紹介 します。

ほとんどのモジュール では、__version__ という 名前特殊属性 に、モジュールバージョン表す文字列代入 されています。従って、下記 のプログラムのように、モジュールの __version__ 属性表示 することで バージョン確認 することが できます

print(widgets.__version__)

実行結果

8.1.2

Anaconda Navigator から、下記の手順 で、仮想環境インストール された モジュールバージョン確認 することが できます

  1. Anaconda Navigator を立ち上げる
  2. 左の Environmentsクリック して表示される 仮想環境の一覧 から marubatsuクリック すると、右に marubatsu仮想環境インストール されている モジュールの一覧表示 される
  3. 上のメニュー から「Installed」を 選択 し、右の「Search packages」と表示された テキストボックス にモジュール名を 入力 すると、下図のように バージョンが表示 される

モジュールのバージョンの変更方法

何らかの理由で、インストールされている モジュールバージョン変更 したい場合は、Anaconda Navigator を使って 下記の手順変更 することが できます

1. 上記の画面で、左にある 緑色チェックマーククリック すると、下図 のような メニューが表示 される

2. 「Mark for specific version installation」を クリック すると、下図 のような、バージョン選択 する メニューが表示 される

3. バージョンを選択 し、下記の Apply ボタンクリック する。「Install packages」という パネルが表示 され、しばらく待つと「Applyボタン がクリックできるようになるので クリック する

本記事では説明しませんが、condapip などの コマンド を使って、異なるバージョンインストールし直す ことが できます

現在よりも 新しいバージョンインストール することを「バージョンアップ」、古いバージョンインストール することを「バージョンダウン」と呼びます。

イベント駆動型プログラミング

これまで本記事記述 してきた プログラム は、実行 すると 記述 した プログラムの処理順番に行われすべての処理終了 した時点で、プログラムの実行終了 します。そのような プログラミングの手法 の事を、処理の流れ(flow)を 記述 して 実行 することから、フロー駆動型プログラミング と呼びます。

それに対して、ボタンメニュー などによって 操作 を行う GUIアプリケーション では、一般的プログラムの実行後 に、ボタン などの GUI を操作するまで の間は 待機しボタン などの GUI に対するの 操作行われた時処理が行われる ようになっています。そのようなプログラムは、フロー駆動型プログラミング手法で記述 することは 困難 なので、GUIアプリケーション は、イベント駆動型プログラミング という 手法良く使われます。また、本記事利用 する、ipywidgetsその手法用います

イベント駆動型プログラミングの用語

イベント駆動型プログラミング で使われる 主な用語 について最初に 説明 します。

用語 意味
ウィジェット ボタンやテキストボックスなどの、GUI構成する部品
コントロール などと 呼ぶ場合 もある
イベント ウィジェットに 対して行われる 操作
イベントの発生 ウィジェットに 対して 操作が行われたこと を表す
イベントハンドラ イベントの発生応じた処理 を行う プログラム
イベントループ イベントの発生応じて適切イベントハンドラ実行する処理行うプログラム

イベント駆動型プログラミングによる GUI アプリの記述方法

イベント駆動型プログラミング手法利用 した GUIアプリケーション は、下記 のような 手順プログラムを記述 します。

  1. 画面内 の適切な位置に ウィジェットを配置 する 処理を記述 する
  2. それぞれウィジェット に対して、下記の処理記述 する
    1. ウィジェット操作 した際に 行う処理記述 する、イベントハンドラ というプログラムを 記述 する
    2. ウィジェットイベントハンドラ結び付ける

具体例 として、ipywidgets で、ボタンをクリック すると、画面メッセージを表示 する GUI のプログラムを 記述 する 方法紹介 します。

ボタンのウィジェットを作成して配置する

ipywidgets には 様々な種類ウィジェット作成 し、JupterLab 上表示 する 機能 があり、ウィジェット種類ごと に、ウィジェットを作成 する 関数が用意 されています。例えば、Button という 関数 によって、ボタンウィジェット作成 することが できますButton返り値 は、作成 された ウィジェット を表す オブジェクト です。

作成 した ウィジェット は、display という 関数 を使って JupyterLab描画 することが できますこれまでに利用 してきた print は、文字列を表示 する 機能 を持ちますが、display は、画像ウィジェットなどJupyterLab描画 する 機能 を持つ 関数 です。

下記 は、ボタンウィジェット作成 して 描画 するプログラムです。なお、キーワード引数 description は、ボタン表示 する 文字列設定 する 実引数 です。

button = widgets.Button(description='Click me')
display(button)

実行結果(下図は、画像なので操作することはできません)

Quiita記事内Python のプログラムを 実行 することは できない ので、本記事内実行結果 には、ウィジェット画像 を表示します。実際のウィジェット は、JupyterLab 上で 確認して下さい

display は、IPython という モジュール定義 された 関数 ですが、VSCodeJupyterLab では、IPython モジュールインポートしなくても利用できる ようです。なお、display を実行 して エラーが発生 する場合は、下記 のプログラムを 記述 して displayインポート して下さい。

from IPython.display import display

表示 された ボタン は、一般的 なアプリケーションの ボタンと同様 に、マウスクリック することで ボタンの色一時的に変化 しますが、ボタンクリックした時処理 をまだ 記述していない ので、クリック しても 色が変わるだけ他の処理 はまだ 行われません

Button詳細 については、下記のリンク先を参照して下さい。

ipywidgetsウィジェットの種類 については、下記のリンク先を参照して下さい。

イベントハンドラの定義

ipywidgets では、イベントハンドライベント(event)を 処理 する(handle)機能を持つ関数 として 定義 します。今回のプログラムでは、ボタンクリック するという 操作行った時メッセージを表示 するという 処理を行う ので、その処理行う ための イベントハンドラ定義 する 必要あります

ipywidgets では、ウィジェットの種類 によって、イベントハンドラ定義の方法若干異なりますボタンウィジェット に対する イベントハンドラ は、仮引数1 つ持つ関数 として 定義 します。その仮引数 には、イベントハンドラ呼び出された際 に、ボタンウィジェット を表す オブジェクトが代入 されます。

この後で紹介する例を含め、ボタン に対する イベントハンドラ仮引数の情報必要になる ことは あまりない かもしれませんが、仮引数記述せずイベントハンドラを定義 すると エラーが発生 するので、必ず記述 する 必要あります

ボタン以外ウィジェットイベントハンドラ仮引数 には、別の情報代入 されます。具体例は今後の記事で紹介する予定です。

下記 は、画面"ボタンがおされたよ!" という メッセージを表示 する イベントハンドラ定義 の一例です。イベントハンドラ関数名前 は、ボタン(button)が クリックされた時(on clicked)に 行う処理 なので on_button_clicked に、仮引数名前 は、ボタンウィジェットの情報代入 されるので button頭文字 をとって b としました。なお、これらの名前決まりはない ので 好きな名前変更 しても かまいません

def on_button_clicked(b):
    print("ボタンがおされたよ!")

ウィジェットへのイベントハンドラの結び付け

イベントハンドラ定義しただけ では、ボタンクリック しても 何も起きませんボタンクリック した際に イベントハンドラを実行する ようにするためには、ウィジェットイベントハンドラ結び付ける という 処理を記述 する 必要あります

ボタンウィジェット を表す オブジェクト には、on_click という、ウィジェットイベントハンドラ結び付ける機能 を持つ メソッド が用意されており、下記 のプログラムのように、実引数イベントハンドラ記述 して呼び出すことで、ボタンクリックした際 に、そのイベントハンドラ呼び出される ようになります。

button.on_click(on_button_clicked)

上記 のプログラムを 実行後 に、先程表示 した ボタンクリック すると、クリックするたび に、ボタンの下下図 のように メッセージが表示 されるようになります。なお、下図3 回クリック を行った 場合の図 です。

on_click メソッドは、ボタンウィジェットのみ利用 できます。他のウィジェットイベントハンドラ結び付ける 場合は observe という メソッド利用 します。具体的な方法については、今後の記事で紹介する予定です。

上記 のプログラムでは ボタンの下メッセージが表示 されますが、メッセージ別の場所表示 することも できます。具体的な方法は今後の記事で紹介します。

ipywidgetsボタンwidgets利用 することで、〇×ゲームの リセットなど機能実装 することが できます。具体的な実装は次回の記事で行います。

イベント駆動型プログラミングの仕組み

下記は、これまで の、フロー駆動型プログラミング と、イベント駆動型プログラミング違い を表す表です。フロー駆動型 の場合は、プログラムの実行終了した時点 で、プログラムの処理すべて完了 します。ここでいう プログラム実行の終了 とは、JupyterLab の セルに入力 した プログラムの実行終了 し、次のプログラムセルに入力 して 実行できる状態になる という 意味 です。

駆動型 性質
フロー プログラムの実行終了した時点 で、プログラムの処理完了する
イベント プログラムの実行終了した後 でも、プログラムの処理完了しない

イベント駆動型 の場合は、プログラムの実行終了した後 も、この後で説明する、イベントループ という 処理引き続き実行 されるため、プログラムの処理完了しません

イベントループ は、下記 のような アルゴリズム処理 を行う プログラム のことです。イベントループ は、「イベント発生するまで待ち発生したイベント対する処理行う」という処理を 無限に繰り返すループ する)ことが、名前の由来 になっています。

  1. ipywidgets登録 された イベントハンドラ対応するイベント発生しているか どうかを 調べる
  2. イベント発生していれば対応 する イベントハンドラ呼び出す発生していなけれ何もしない
  3. 手順 1戻る

ipywidgetsインポート すると、自動的上記イベントループ処理 を行うプログラムが 実行 されます。その際 に、JupyterLabセルプログラム入力 して 実行できる ので、イベントループ処理行われている ことが 実感できない かもしれませんが、実際 に、見えない所イベントループの処理行われています

最初 は、ipywidgetsイベントハンドラ は一つも 登録されていない ので、イベントループ は、実質的何の処理行いません

先程のように、ボタンなどの ウィジェットを作成 し、イベントハンドラ結びつける ことで、ipywidgetsイベントハンドラが登録 され、以後 は、イベントハンドラ対応するイベント発生するたび に、イベントループの処理 によって、イベントハンドラが実行される ようになります。また、対応するイベント発生しない間 は、イベントループ実質的何の処理行わない ので、イベント発生するまでの間待機状態 になります。

これが、イベント駆動型プログラミング大まかな仕組み です。

実際イベントループ は、上記以外処理行っています が、イベント駆動型プログラミングおおまかな仕組み把握するだけ であれば、上記の処理理解 するだけで 十分 でしょう。

イベントループと play メソッドのループの類似点

イベントループ行う処理ピンとこない人多いのではないか と思いますので、Marubatsu クラスの play メソッドで行う 処理 が、イベントループ で行われる 処理似ている ことを 説明 します。

イベントループ を、Python 風プログラム記述 すると 以下 のようになります。

while True
    if ipywidgets に登録されたイベントハンドラに対応するイベントが発生している:
        対応するイベントハンドラを呼び出す

下記play メソッドの中で、人間どうし対戦 を行った際に、イベントループ似た処理 を行う 部分抜粋 したプログラムです。

# ゲームの決着がつくまで無限に繰り返す
while self.status == Marubatsu.PLAYING:
   # キーボードからの座標の入力
   coord = input("x,y の形式で座標を入力して下さい。exit を入力すると終了します")
   # この後で、coord に着手を行うプログラムが記述されている

上記 のプログラムは、下記 の点で イベントループ似ています

  • input という 組み込み関数行う処理 は、テキストボックス への 文字列の入力 という イベントが発生 するまで 待機 する
  • 文字列入力された場合 は、入力された 文字列対応する処理行う
  • 処理完了後 は、再び 次の入力を待つ という 処理繰り返す

ただし、下記 の点では、イベントループ とは 異なります

  • play メソッドの場合は、ゲームの 決着がついた場合繰り返しを終了 するので、イベントループと異なり、無限ループではない
  • イベントループ では、複数イベントハンドラ登録 することで、複数イベント対応する処理を行う ことが できるがplay メソッドの場合は、テキストボックス への 文字列の入力 という、一つのイベント にしか 対応できない

このように、play メソッドが 行う処理 は、イベントループが行う処理と 異なる部分もありますが、「イベント発生するまで待ち発生した場合対応する処理を行う」という では、同様の処理行っています

初心者の方 には 理解しづらい かもしれないので、上記の違い と、下記の説明 については、現時点 では よくわからなくても構わない と思います。

play メソッドが、イベントループ異なり一つのイベント にしか 対応できない理由 は、input実行 すると、テキストボックス文字列入力されるまで は、他の処理一切行わない からです。このように、実行 すると、特定の条件満たされるまで 処理が 終了しない ことを、処理ブロックされる と呼びます。

イベントループ では、特定のイベント発生 するまで、処理ブロックする といことは 行っていない ため、複数の種類イベント対応 することが できます

なお、play メソッド は、そもそも 文字列を入力 する 以外 のイベントに 対応する必要はない ので、そのまま でも 問題はありません

ipympl

matplotlib には、描画 した 画像 に対して、下記のような インタラクティブ2(interactive)な 処理を行う プログラムを 記述するため機能 があります。しかし、JupyterLab では、そのままでは matplotlib下記の機能利用 することは できません

  • 描画した画像 を、後から更新 する
  • 描画した画像 に対する、マウスを押す などの 操作対応する処理 を表す イベントハンドラー を定義し、結び付ける ことが できる

ipympl3 は、JupyterLab 上で、matplotlibインタラクティブな機能利用できるよう にする モジュール です。

ipympl の詳細については、下記のリンク先を参照して下さい。

ipympl のインストール

ipympl も、Anaconda Navigator を使って、下記 の方法で インストール できます。

  1. Anaconda Navigator を立ち上げる
  2. 左の Environmentsクリック して表示される 仮想環境の一覧 から marubatsuクリック すると、右に marubatsu仮想環境インストール されている モジュールの一覧表示 される
  3. 上のメニュー から「Not installed」を 選択 し、その右の「Search packages」と表示された テキストボックス に「ipympl」を 入力 する
  4. 下に ipympl が表示 されるので、その 左のチェックボックスチェック し、下の「Applyボタンクリック する。
  5. Install packages」という パネルが表示 され、しばらく待つと「Applyボタン がクリックできるようになるので クリック する。

ipympl のバージョン

ipympl利用する際 は、matplotlibバージョン組み合わせの制限 があり、制限を満たさない 場合は エラーが発生 したりしてプログラムが うまく動作しない 場合が あります

具体的組み合わせ については、下記のリンク先参照 して下さい。

筆者の PC では、下記のバージョン動作を確認 しています。プログラムが うまく動作しない場合 は、先程説明 した方法で モジュールバージョン確認 し、必要 であれば バージョンあわせてみて 下さい。

モジュール バージョン
ipympl 0.9.3
matplotlib 3.8.0

上記リンク先jupyter-matplotlibJupyterLabバージョン についてですが、VSCodeJupyterLab利用 する場合は、VSCode拡張機能Jupyter が関係します。下図 は、筆者VSCode拡張機能Jupyter の表示 です。

実はこの記事を最初に執筆した時に、ipympl利用 すると、matplotlib画像うまく表示できない という 問題発生 しました。試行錯誤した結果、筆者VSCodeJupyter下図 のように、上部pre-release表示 される プレリリースバージョン になっており、下図 の「リリースバージョンへの切り替え」をクリックして、VSCode再起動 した結果、画像うまく表示される ようになりました。

ただし、筆者PCうまく画像描画できなかった理由 が、プレリリースバージョンせいか どうかは よくわかりません。もしかすると、単にVSCode にインストールされていた Jupyter の拡張機能のバージョンが古く、リリースバージョンへの切り替えをクリックしたことでバージョンアップされたことで問題が解決できただけかもしれません。いずれにせよ、この後のプログラムを実行した際に、matplotlib画像うまく描画できない 場合は、VSCode拡張機能Jupyter をみて、バージョンアップできる のであれば、しておくことお勧めしますうまくいかない方 や、この件 について ご存じの方コメント書いていただけると嬉しい です。

ipympl の利用方法

ipympl利用する際 は、これまでのように モジュールインポート するの ではなく、下記の マジックコマンド記述 して 実行する必要 があります。

%matplotlib widget

上記の %始まるもの は、JupyterLab機能を呼び出すマジックコマンド と呼ばれるもので、Pythonプログラム では ありませんマジックコマンド は、JupyterLabセルの中のみ実行 できるため、marubatsu.py などの、モジュールの中記述 すると エラーが発生 する点に 注意 して下さい。

本記事では、他のマジックコマンドについては、必要に応じて紹介する予定ですが、興味がある方は、「Jupyter マジックコマンド」をキーワードに検索してみると良いでしょう。

%matplotlib widget の効果

%matplotlib widget実行 することで、matplotlib対する処理 が、大きく変化 します。その 変化の内容 について 説明 します。

インタラクティブモード

matplotlib には、matplotlibインタラクティブな機能利用できるインタラクティブモード(interactive mode)」と、利用できない非インタラクティブモード(non interactive mode)」があります。JupyterLab では、matplotlib初期設定 では、非インタラクティブモード になっていますが、%matplotlib widget実行 することで、matplotlibインタラクティブモードなります

インタラクティブモード では、matplotlib での 画像の描画処理 が、以下 のように、これまでの 非インタラクティブモード大きく異なる処理 が行われるようになります。

  • 下記のような、Figure対する変更行われるたび に、画像の描画更新 される
    • Figure の作成
    • plot メソッドなどによる Artist の登録
    • set_xlim メソッドなどによる Artist設定の変更

それ以外の効果

%matplotlib widget実行 すると、上記に加え下記 のような matplotlib描画した画像インタラクティブ にする 効果得られます

  • 描画 した 画像の付近 に、Figureタイトルマウスの座標画像に対する操作 を行うための ツールバー などが表示されるようになる
  • 描画 した 画像対する マウスのクリックなどの イベント と、イベントハンドラ結び付ける ことが できる ようになる
  • JupyterLabセルを実行 しても、plt.show() が自動的に 実行されなくなる
  • plt.show()実行 しても、pyplot管理 する Figure削除されない

上記 は、matplotlibインタラクティブモード とは 関係ありません

例えば、pyplot には、インタラクティブモード有効 にする ion というメソッドがありますが、plt.ion()実行 して インタラクティブモード にしても、JupyterLabmatplotlib利用する場合 は、それだけ では matplotlibインタラクティブな機能利用 することは できません

上記の変化 について、具体例 をいくつか挙げて 説明 します。

画像の描画

%matplotlib widgets実行後 は、下記 のプログラムのように、Figure作成するだけ で、画像が描画される ようになります。

import matplotlib.pyplot as plt
import japanize_matplotlib

fig = plt.figure(figsize=[3, 3], facecolor="lightblue")

実行結果(下図は、画像なので操作することはできません)

以前の記事で説明したように、%matplotlib widgets実行しない 場合は、上記 のプログラムを 実行 しても 画像描画されません

また、描画される画像 は、%matplotlib widgets実行しない 場合と 比べて下記 のような 違い があります。それぞれについて説明します。

  • 画像の上部 に、Figureタイトル が表示される
  • 画像の下部 に、Figure登録 された Axes に対する マウスポインタ座標が表示 される(上記 の例では Axes登録されていない ので 表示されない
  • 画像の右下 に、画像大きさドラッグ して 変更できる ことを表す 三角形のマーク表示 される
  • 画像の上マウスを移動 すると、画像操作 するための ツールバー表示 される

Figure のタイトルとマウスの座標

先程の 画像の上部表示 される Figure 1 は、Figureタイトル です。Figureタイトル は、Figure作成した順番 で、Figure 1 のように 自動的付けられます

FigureAxes登録されている 場合は、マウスAxesplot の上移動 することで、画像の下部Axes に対する マウスポインタ座標が表示 されるようになります。なお、Figure の外 や、Figure の中で、Axes の範囲外マウスを移動 した場合は、何も表示されなくなります

下記は、subplots メソッドで、Axes登録 された Figure作成 するプログラムです。先程の図区別 できるように、Figure の 背景色黄色 にしました。実行結果 は、この 画像の上マウスを移動 した場合の図で、下部の表示Axes(0.490, 0.371)A座標の上マウスポインタ存在する ことを 表します。なお、2 つ目作成 した 画像 なので、FigureタイトルFigure 2 になります。

fig2, ax2 = plt.subplots(figsize=[3, 3], facecolor="yellow")

実行結果(下図は、画像なので操作することはできません)

なお、画像の左表示 される ボタン は、この後で説明する ツールバー です。

Figure のタイトルの設定方法

Figureタイトル自分で設定 したい場合は、下記 のプログラムのように、figure メソッドや、subplots メソッドで Figure を作成 する際に、キーワード引数 numタイトルを表す文字列代入 して 呼び出す ことで行えます。実行結果 から、画像の上部〇×ゲーム表示 されることが 確認 できます。

fig3, ax3 = plt.subplots(num="〇×ゲーム", figsize=[3, 3], facecolor="lightgreen")

実行結果(下図は、画像なので操作することはできません)

figure メソッドや、subplots メソッドの キーワード引数 num は、画像の上部のタイトル以外にも、作成した Figure識別子 としても 利用 されます。

詳細は長くなるので、下記のリンク先を参照して下さい。

画像の大きさの変更と、ツールバー

画像右下三角形のマークマウスでドラッグ することで、画像の大きさ変更 することが できます。また、画像の上マウスを移動 することで、下図(先ほどの fig2 の画像です)のような ツールバー画像の内部 に表示されます。

この ツールバー は、画像表示範囲の変更 や、ファイルへの保存 などを行うことができる ボタン ですが、〇×ゲーム では 利用しない ので本記事では 詳細説明しません。興味がある方は、下記のリンク先を参照して下さい。

タイトルやツールバーなどに関する設定

Figure には、JupyterLab 上に 描画 される 画像 を表す オブジェクト代入 されている canvas 属性 があり、canvas 属性代入 された オブジェクト には、画像の描画設定 に関する 下記 のような 属性が存在 します。これらの属性の値変更 することで、タイトルツールバー表示の有無 などの 設定変更 することが できます

属性名 意味
header_visible4 True の場合に上部(header)の タイトルを表示 する
footer_visible5 True の場合に下部(footer)の マウスの座標を表示 する
toolbar_visible True の場合に ツールバーを表示 する
resizable6 True の場合に、画像のサイズ変形できる

下記 は、先程 最後に作成 した fig3 に対して、上記すべてFalse設定 するプログラムです。下記 のプログラムを実行すると、実行結果 のように、先程作成 した 画像タイトルなど表示されなく なります。また、マウスのドラッグ による 画像サイズの変更 を行うことも できなくなりますJupterLab 上で、実際に確認 してみて下さい。

fig3.canvas.toolbar_visible = False
fig3.canvas.footer_visible = False
fig3.canvas.header_visible = False
fig3.canvas.resizable = False

実行結果(下図は、画像なので操作することはできません)

plt.show() に関する注意点

%matplotlib widget実行 した場合は、以下 のような理由で、plt.show() は基本的に 実行しません2 つ目現象 に関しては、この後説明 します。

  • plt.show()実行しなくてもFigure作成 したり、Figure画像の内容変化 するような 処理を行う と、自動的画像の描画更新される
  • plt.show()実行 すると、同じ画像同時複数描画 されてしまう

plt.show() の処理の変化

上記で説明したように、%matplotlib widget実行 した場合は、基本的に plt.show()実行しない ので、下記の説明 は、現時点 では 理解する必要ありません。意味が分からない方や、あまり興味がない方は、この後の 画像の描画の更新進んで下さい

%matplotlib widget実行 している 場合 と、そうでない場合 は、plt.show() メソッドを 実行 した際の 処理下記 のように 変化します

%matplotlib widget plt.show() に関する処理
実行していない pyplot管理 する すべての Figure描画 する
pyplot管理 する Figure をすべて 削除 する
JupyterLabセルを実行 すると、plt.show()実行される
実行した current Figure のみ新しく描画 する
pyplot管理 する Figure変化しない
JupyterLabセルを実行 しても、plt.show()実行されない

pyplot が管理する Figure と current Figure

上記 のようなことが起きることを 確認するため には、pyplotFigure管理する仕組み について 理解 する 必要あります

pyplot は、figure メソッドや subplots メソッドで 作成 した Figure管理 する 機能 を持っています。pyplot管理 している Figure一覧 は、下記 のプログラムのように、pyplotget_figures メソッドで 知る ことが できますこのメソッド返り値 は、Figure割り当てられた ID番号要素 とする list で、先程 3 つFigure を作成 したので、実行結果 には 3 つFigure対応 する ID が表示 されます。

plt.get_fignums()

実行結果

[1, 2, 3]

pyplot が、管理 する Figure の中で、pyplot現在(current)処理の対象 とする Figurecurrent Figure と呼びます7pyplot では、新しい Figure作成 すると、その Figurecurrent Figure になります。

本記事では利用しませんが、pyplot には current Figurecurrent Axes何であるか調べたり別のもの変更 する メソッド があります。

%matplotlib widget を実行した場合の plt.show() の処理の確認

下記 のプログラムを実行すると、実行 した セルの下 に、current Figure である、先程作成した、3 つ目画像のみ描画 されます。

plt.show()

実行結果(下図は、画像なので操作することはできません)

また、下記 のプログラムを実行することで、plt.show()実行 しても、pyplot管理 する Figure変化していない ことが 確認 できます。

plt.get_fignums()

実行結果

[1, 2, 3]

上記 のプログラムを 実行 した際に、実行結果画像が描画されない点注目 して下さい。もし、JupyterLabセルを実行 した際に、自動的plt.show() が実行される のであれば、先程 plt.show()実行した場合同様 に、セルの下画像が描画 されるはずです。また、下記 のプログラムのように、もう一度 plt.show()実行 した場合は、fig3 の 画像新しく描画 されます。

このことから、%matplotlib widget実行 している 場合 は、セルを実行 しても 自動的plt.show()実行されなくなっている ことが 確認 できました。

plt.show()

実行結果(下図は、画像なので操作することはできません)

%matplotlib widget を実行していない場合の plt.show() の処理の確認

次に、下記 のプログラムを 実行 して下さい。%matplotlib inline は、%matplotlib widget実行する前状態に戻すマジックコマンド です。

%matplotlib inline

上記を実行 した で、下記 のプログラムのように、plt.show()実行 すると、前回までの記事のように、pyplot管理 する 3 つの Figureすべて セルの下に 描画されます。また、描画 される 画像 は、下記の点先程異なります

  • 1 つ目Figure には、Axes登録されていない ので、画像描画されない
  • いずれの画像 にも、タイトルツールバー表示されず変形もできない
plt.show()

実行結果

<Figure size 450x450 with 0 Axes>



また、下記 のプログラムを実行することで、plt.show() によって、pyplot管理 する Figure削除 されたことが 確認 できます。

plt.get_fignums()

実行結果

[]

画像の描画の更新

この前 の「plt.show() の処理の変化」で 記述 されている プログラム実行した人 と、この前の説明を 読み飛ばして来た人 では、JupyterLab状態が異なる ので、JupyterLab再起動 してから、下記 のプログラムを 実行 して 新しい Figure作成 して下さい。

%matplotlib widget
import matplotlib.pyplot as plt
import japanize_matplotlib

fig, ax = plt.subplots(figsize=[3, 3])

実行結果(下図は、画像なので操作することはできません)

下記は、上記描画 した FigureAxes に対して、plot メソッドで 線を描画 するプログラムです。下記実行 すると、これまでと異なり下記 のプログラムの セルの直後 には 画像描画されず上記描画 した 画像更新されて直線が描画されます

ax.plot([1, 2], [1, 2])

実行結果(先程描画した画像が下記のように更新されます)

このように、%matplotlib widget実行 した場合に、既に作成した画像 に対して 描画などの処理 を行うと、新しい画像が作成 されるの ではなく既に描画した画像更新される ことになります。また、先程 fig3タイトルツールバー表示の設定修正 した際に、既に描画 した fig3画像表示の設定変化 したのは、そのため です。

本記事最初で決めた〇×ゲーム以下の処理 は、%matplotlib widget実行 することで 記述できる ようになります。

  • 着手を行う ことで、ゲーム盤画像の内容更新 する

plot.show() を実行すべきでない理由

先程、%matplotlib widget実行 した場合は、基本的plt.show()実行しない と説明しましたが、その 理由の一つ紹介 します。

下記 は、plt.show()実行 し、セルの下先程の画像もう一つ描画 するプログラムです。実行結果 から、先程と同じ画像描画 されることが 確認 できます。

実行結果(下図は、画像なので操作することはできません)

plt.show()

続けて下記 のプログラムを 実行 して、画像もう一本の線描画 すると、先程描画した 2 つの画像両方線が描画 されることになります。

ax.plot([1, 2], [2, 1])

実行結果(下図は、画像なので操作することはできません)

画像の内容更新 する際に、このように 同じ画像2 つ描画 して 同時に更新 する 必要一般的 には ありません。これが、plt.show()実行しない理由 の一つです。

matplotlib の Figure に対するイベントハンドラの登録

今回の記事最初 で、〇×ゲーム着手を入力 する GUI実装 するためには、下記 のようなプログラムを 記述 する 必要がある と説明しました。

  • ゲーム盤画像の上クリック した時に、以下の処理 を行う
    • クリック した ゲーム盤マスの座標知る
    • その座標着手を行う

上記の処理実現 するためには、matplotlib描画 した 画像の上マウスがクリック された際に 実行 される、イベントハンドラ定義 し、その イベントハンドラ を、画像結び付ける必要あります

mpl_connect メソッド

先程、Figure には、JupyterLab 上に 描画 される 画像 を表す オブジェクトが代入 されている canvas 属性 があると説明し、そのオブジェクトいくつかの属性紹介 しました。

canvas 属性 に代入された オブジェクト には、mpl_connect という、JupyterLab 上に 描画 された matplotlib画像 に対する イベントイベントハンドラ結び付け(connect)、ipywidgets登録 することで、イベントループ処理を行う ことができるようする メソッド があります。mpl_connect実行する際 には、イベントの種類 を表す 文字列 と、イベントハンドラ2 つ実引数記述 します。

イベントハンドラの定義

mpl_connect で結びつける イベントハンドラ は、仮引数1 つ持つ関数 として 定義 する 必要があります。また、その仮引数 には、発生 した イベントの情報 を表す オブジェクトが代入 されます。なお、仮引数名前どのような名前 を付けても 構いません が、一般的 には、eventevte などが 良く使われます

下記は、仮引数代入 された オブジェクト主な属性一覧 です。

属性名 意味
inaxes マウスAxes の上にあるか どうかを表す bool 型 のデータ
xdataydata マウスAxes の上ある場合 は、その Axes 上x 座標y 座標
マウスAxes の上ない場合 はどちらも None が代入 される
xy Figure左上の座標原点 とした場合の マウスx 座標y 座標
button マウスボタン押された場合 の、ボタンの種類
key キー押された場合 の、キーの種類

イベントの属性の詳細は、下記のリンク先を参照して下さい。

下記 は、イベント発生した際 の、Axes 上マウスの座標表示 する イベントハンドラ定義 を行うプログラムです。マウス(mouse)が押された(down)際(on)の イベントハンドラ想定 して、名前on_mouse_down としました。

def on_mouse_down(event):
    print(event.xdata, event.ydata)

mpl_connect の記述方法

matplotlib画像 には、マウスを押すキーを押す などの、様々な種類イベント に対して、それぞれ個別イベントハンドラ定義 して 結びつける ことが できます

先程紹介した ipywidgetsボタン などの ウィジェット に対して イベントハンドラ結び付ける方法 は、matplotlib画像 に対して イベントハンドラ結び付ける方法 とは 異なる ので、混同しない ように 注意 して下さい。

mpl_connect は、実引数 に、イベントの種類 と、イベントハンドラ記述 することで、指定 した イベントが発生 した際に、指定 した イベントハンドラ呼び出され実行される ようになります。主なイベントの種類 には、以下 のようなものがあります。

イベントを表す文字列 イベントの意味
"button_press_event" マウスボタン押した
"button_release_event" マウスボタン離した
"key_press_event" キー押した
"key_release_event" キー離した
"figure_enter_event" Figure の中マウスが移動 した
"figure_leave_event" Figure の外マウスが移動 した

イベントの種類の一覧と詳細については、下記のリンク先を参照して下さい。

上記の表から わかるように、マウスクリックに対応 する イベント存在しませんマウスクリック は、マウスボタン押して同じ場所離す という 操作 なので、マウスクリック対応する処理記述 したい場合は、以下 のようなプログラムを 記述する必要あります

  • "button_press_event" に対する イベントハンドラ で、マウス押された座標記録 しておく
  • "button_release_event" に対する イベントハンドラ で、上記記録した座標 と、マウス離された座標比較 し、一致していた場合クリック に対応する 処理を行う

上記の処理記述面倒 なので、先程はマウスをクリックした際に着手を行うと記述しましたが、マウス押された時着手行う ことにします。

下記 のプログラムは、マウスが押された という イベント に、on_mouse_down という イベントハンドラ結び付ける プログラムです。

fig.canvas.mpl_connect("button_press_event", on_mouse_down)

上記 のプログラムを 実行後 に、先程作成した 画像Axes範囲内マウス押すたび に、下記の 実行結果 のように、画像の下クリック した Axes の座標表示 されます。また、3 行目 のように、Axes範囲の外マウスを押すNone表示 されます。

実行結果(下図は、画像なので操作することはできません)

なお、先程 plt.show()実行 するとで、2 つの画像描画 されていますが、どちらの画像 の上で マウスを押してもon_mouse_down呼び出されAxes の座標表示 されます。ただし、表示 されるのは、最初の画像下だけ で、2 つ目画像の下 には 表示されません。これは、print で行われる 文字列の表示 が、matplotlib画像 とは 関係のない場所行われる からです。

上記の方法利用 することで、〇×ゲームゲーム盤の画像 の上で マウスを押した 際の、ゲーム盤の座標知ることできます

今回の記事のまとめ

今回の記事 では、ipywidgets をインポートし、ボタン などの ウィジェット配置する方法 と、ウィジェット操作 した際に 実行 する イベントハンドラの定義方法 を説明しました。また、ipywidgets仕組 みとして、イベントループ について 説明 しました。

次に、matplotlibインタラクティブな機能利用 するために 必要ipympl使い方説明 し、実際matplotlib描画 した 画像の更新 や、その上マウス押した際 に処理を行う イベントハンドラ定義の方法説明 しました。

次回の記事 では、これらの機能 を使って、〇×ゲームGUI で遊べる ようにします。

本記事で入力したプログラム

以下のリンクから、本記事で入力して実行した JupyterLab のファイルを見ることができます。

なお、下記のリンクをクリックして、github 上の JupyterLabファイル見た場合 は、ボタンなどウィジェット表示されません。また、%matplotlib widget実行した後セルmatplotlib画像 は、インタラクティブな画像 には なりません実際プログラムの挙動確認したい人 は、下記のファイルダウンロード後 に、自分のパソコン実行する必要あります

今回の記事では marubatsu.py は変更していません。

次回の記事

  1. widget は、window と、小道具 を表す gadget合成 してできた 用語 だと 言われています

  2. インタラクティブ(interactive)とは、双方向の という意味を表す英語です

  3. ipymplmplmatplotlib を表します

  4. ヘッダ(header)とは、先頭データ のことで、人間上から見た 時に、(head)が 最初 にあることが 由来 です。visible は、目に見える という意味を表す英語です

  5. フッタ(footer)とは、末尾データ のことで、人間上から見た 時に、(foot)が 最後 にあることが 由来 です。なお、ヘッダフッタ にある、本体のデータ の事を、 を表す ボディ (body)と呼びます

  6. resize とは 大きさ(size)を 変更 する(re)という意味の英語で、可能 を表す able をつけた resizable は、大きさを変更可能 という意味を表します

  7. 同様 に、Axes に対しても、pyplot現在処理の対象 とする current Axes があります

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0