CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: Kivy と Matplotlib でデータセットの確認ツールを書いてみる

以前、このブログで Kivy で作った GUI に Matplotlib のグラフを埋め込む方法について書いた。

blog.amedama.jp

今回は、これを応用したツール作りをしてみる。 といっても、やっていることは単純で先の例にボタンを付けてインタラクティブにした程度にすぎない。

使った環境は次の通り。

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.14.5
BuildVersion:   18F132
$ python -V          
Python 3.7.4

下準備

下準備として必要なパッケージをインストールしておく。

$ pip install kivy matplotlib scikit-learn

Digit データセットの内容を表示してみる

今回書いてみたサンプルコードが次の通り。 内容としては scikit-learn に同梱されている Digit データセットの内容を表示させてみることにした。 ボタンを使って表示するデータを前後に進めたり戻したりできる。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from matplotlib import pyplot as plt
from matplotlib import cm
from sklearn import datasets
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg


kv_def = '''
<RootWidget>:
    orientation: 'vertical'

    GraphView:
        id: graph_view
        size_hint_y: 0.8

    BoxLayout:
        size_hint_y: 0.2

        Button:
            id: prev_button
            text: '< Prev'
            on_press: root.ids.graph_view.prev()

        Button:
            id: next_button
            text: 'Next >'
            on_press: root.ids.graph_view.next()

<GraphView>:
'''
Builder.load_string(kv_def)


class GraphView(BoxLayout):
    """Matplotlib のグラフを表示するウィジェット"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # データセットを読み込んでおく
        self.dataset = datasets.load_digits()
        # 表示するデータのインデックス
        self.cursor = 0

        # 描画領域を用意する
        self.fig, self.ax = plt.subplots()

        # 描画を初期化する
        self._update_view()

        # グラフをウィジェットとして追加する
        widget = FigureCanvasKivyAgg(self.fig)
        self.add_widget(widget)

    def _update_view(self):
        """描画を更新するメソッド"""
        # 以前の内容を消去する
        self.ax.clear()
        self.ax.axis('off')

        # データを取得する
        img_data = self.dataset.data[self.cursor]
        label = self.dataset.target[self.cursor]

        # データを描画する
        self.ax.imshow(img_data.reshape(8, 8),
                       cmap=cm.gray_r,
                       interpolation='nearest')
        title_msg = 'index={idx}, label={label}'.format(idx=self.cursor,
                                                        label=label)
        self.ax.set_title(title_msg, color='red')

        # 再描画する
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()

    def next(self):
        """次へボタンを押したときのコールバック"""
        if self.cursor < len(self.dataset.data) - 1:
            self.cursor += 1
        self._update_view()

    def prev(self):
        """戻るボタンを押したときのコールバック"""
        if self.cursor > 0:
            self.cursor -= 1
        self._update_view()


class RootWidget(BoxLayout):
    pass


class ViewerApp(App):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.title = 'Digit dataset viewer'

    def build(self):
        root_widget = RootWidget()
        return root_widget


def main():
    # アプリケーションを開始する
    app = ViewerApp()
    # ここでスレッドがブロックする
    app.run()


if __name__ == '__main__':
    main()

上記を実行してみる。

$ python digitviewer.py

すると、次のような GUI が表示される。

f:id:momijiame:20190725060813g:plain

応用すればアノテーションに使うツールなんかも作れるだろうね。 いじょう。