kivyのSpinnerをカスタマイズ

kivyのSpinner(ドロップダウンリスト)をカスタマイズする

概要

Pythonのマルチプラットフォーム向けGUIライブラリkivyで Spinner(ドロップダウンリスト)の項目の表示がすべて同じ色で現在選択されている項目がどれか一目で分からなかったので 選択されている項目の色が変わるようにカスタマイズしてみた。

kivyのButtonの色を変更するではButtonをカスタマイズして 簡単に背景色を変更できるようにしたので、それを使えばわりとお手軽にできそうな感じ。

カスタマイズ内容

ソース

ソースは↓の「開く」をクリックすると表示されます。
ダウンロードしたいときは こちら からどうぞ。

解説

kivyではドロップダウンリスト(クリックすると選択項目がぺろっと出てくるやつ)を表示するのにSpinnerウィジェットを使う。
で、クリックすると設定された項目が表示されるのだけれど、すべて同じ色(通常時、クリックした時、無効化した時の色はそれぞれ画像で指定できる) で表示され、現在どれを選択しているのかが分かり難い。
そこで、現在選択されている項目の色(画像でなく)を変更できるようにカスタマイズする。

色指定に関して、Spinnerクラスと項目を表示するためのSpinnerOptionクラスはどちらもButtonクラスを継承しているので、 CustomButtonクラスを同時に継承することでButtonクラスに関連する処理を置き換えられる。

まず、SpinnerOptionクラスとCustomButtonクラスを継承したCustomSpinnerOptionクラスを作成する。
CustomSpinnerOptionクラスは特に追加する処理などはない。

class CustomSpinnerOption(SpinnerOption, CustomButton):
    pass

次に SpinnerクラスとCustomButtonクラスを継承したCustomSpinnerクラスを生成。

class CustomSpinner(Spinner, CustomButton):

追加するプロパティ

    item_selected_bg     = ColorProperty([1.0, 0.5, 0.5, 1.0])   # 選択項目の背景色
    item_selected_fg     = ColorProperty([1.0, 1.0, 1.0, 1.0])   # 選択項目の文字色
    item_unselected_bg   = ColorProperty([0.2, 0.2, 0.2, 1.0])   # 非選択項目の背景色
    item_unselected_fg   = ColorProperty([1.0, 1.0, 1.0, 1.0])   # 選択項目の文字色
    item_height          = NumericProperty('48dp')               # 項目の高さ(デフォルトは48dp)

コンストラクタではoption_clsのデフォルト値をCustomSpinnerOptionに設定し、基底クラスのコンストラクタを実行。

    def __init__(self, **kwargs):
        if 'option_cls' not in kwargs:
            # 項目表示用クラスが指定されていなければCustomSpinnerOptionを指定
            kwargs['option_cls'] = CustomSpinnerOption
        
        # 基底クラスの初期化
        super(CustomSpinner, self).__init__(**kwargs)

その後、追加した各プロパティとtextプロパティの変更時の処理をバインドする。

        self.bind(
                    text                = self.update_dropdown_background,  # 選択変更時に背景色を更新
                    item_selected_bg    = self.update_dropdown_background,  # 選択項目の背景色変更時に背景色を更新
                    item_unselected_bg  = self.update_dropdown_background,  # 非選択項目の背景色変更時に背景色を更新
                    item_selected_fg    = self.update_dropdown_background,  # 選択項目の背景色変更時に背景色を更新
                    item_unselected_fg  = self.update_dropdown_background,  # 非選択項目の背景色変更時に背景色を更新
                    item_height         = self.update_dropdown_background,  # 項目高さ
                )

ドロップダウンの項目の色などを変更する処理を追加。
この処理が各プロパティの変更時の処理としてバインドされる。

項目の一覧はself._dropdown.container.childrenself._dropdown.childrenにあるので判断して取得。

選択されている項目か否かは各項目のtextself.textが一致しているかどうかで判断できるので、 この条件で設定する色を変更。
また、項目すべてをループするので、ついでに項目高さ(height)も変更しておく。

    # ドロップダウン内の項目の高さ/背景色/文字色を更新
    def update_dropdown_background(self, *largs):
        text = self.text
        # 項目のリストを取得
        if self._dropdown.container :
            items = self._dropdown.container.children
        else :
            items = self._dropdown.children
        
        # 各項目の背景色/文字色を変更
        for item in items:
            item.height = self.item_height                      # 項目高さ
            if item.text == text:
                item.bg_color_normal    = self.item_selected_bg    # 選択中の背景色
                item.color              = self.item_selected_fg    # 選択中の文字色
            else:
                item.bg_color_normal    = self.item_unselected_bg  # 非選択の背景色
                item.color              = self.item_unselected_fg  # 非選択中の文字色

ドロップダウンの更新処理をオーバーライド。
この処理はドロップダウンの新規作成時や項目追加時にコールされる。
ここに上のドロップダウンの項目の色などを変更する処理のコールを追加。

    def _update_dropdown(self, *largs):
        super()._update_dropdown(*largs)
        
        # 項目の背景色等を更新
        self.update_dropdown_background()

ついでに項目の追加処理を追加。

    # 項目の追加
    def add_item(self, text) :
        self.values.append(text)

以上でカスタマイズは終了。

動作確認プログラム

このファイルを単体で実行すれば動作確認プログラムが動作する。
動作確認ではAddボタンをクリックする度にドロップダウンリストの項目が追加されるようになっている。
Spinnerのボタンをクリックするとドロップダウンリストが表示され、現在選択されている項目が他の色で表示されている。