Kivyでpopup画面を表示させる方法を説明します。Kivyのpopupは別ウィンドウに表示させるものではなく同一ウィンドウ内に表示するモーダルウィンドウになります。
popupの構造
Kivyのpopupは別ウィンドウに表示させるものではなく、同一ウィンドウ内に疑似的に画面を作り出しています。
popupの構造は下記のようにいくつかの部品に分かれています。
- popupウィンドウ:黒い領域がpopupウィンドウです。
- タイトル:上部の文字列が書かれている部分です。
- セパレーター:水色のラインです。
- Content領域:popupウィンドウに表示するウィジェットを配置する領域
popupの設定
popupを作る方法はいくつかありますが、ここでは一番シンプルに書けてコードが見やすい動的クラスルールを使用した書き方を説明します。公式サイトでも紹介されている方法になります。kv言語の動的にクラスについては下記の記事をご覧ください。
主なpopupの設定を書いたサンプルになります。
popup1.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class RootWidget(BoxLayout):
pass
class Popup1(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
Popup1().run()
popup1.kv
#:import Factory kivy.factory.Factory
<CustomPopup@Popup>:
# Popupウィンドウの設定
title: "Popup Title"
title_align: "center"
title_color: 100/255, 100/255, 100/255, 1
#title_font: "./font_file.ttf"
title_size: sp(15)
separator_color: 180/255, 164/255, 176/255, 1
separator_height: dp(4)
auto_dismiss: False
size_hint: 0.6, 0.4
background: ''
background_color: 228/255, 216/255, 225/255, 1
# Content領域の設定
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgba: 207/255, 190/255, 202/255, 1
Rectangle:
size: self.size
pos: self.pos
Label:
text: "ポップアップだよ"
color: 100/255, 100/255, 100/255, 1
Button:
text: "Close"
background_color: 215/255, 145/255, 197/255, 1
on_release: root.dismiss()
<RootWidget>:
Button:
text: "Open popup"
on_release: Factory.CustomPopup().open()
コード解説
kvファイルではpopupウィンドウの設定→コンテントの設定の順に書きます。
動的クラスルールをインスタンス生成するためにFactoryをインポートします。
#:import <エイリアス名> <モジュール名>
#:import Factory kivy.factory.Factory
インポートはkvファイルでもインポートすることができます。’#’はコメントではありません。’Factory’の部分はエイリアスで任意の名前が付けられます。
<CustomPopup@Popup>:
Popupクラスを継承した動的クラスルールを定義し、次の行でPopupクラスのプロパティを定義します。
タイトルを設定する
下記はタイトル関連の設定になります。
title: "Popup Title"
title_align: "center"
title_color: 100/255, 100/255, 100/255, 1
#title_font: "./font_file.ttf"
title_size: sp(15)
title: "Popup Title"
titleはタイトルの文字列を設定します。デフォルトは[No title]です。
title_align: "center"
title_alignは水平方向の位置を設定します。プロパティの値には[left,center,right,justify]のいずれかを指定します。デフォルトはleftです。justufyは複数行を使用する場合の行揃えです。
title_color: 100/255, 100/255, 100/255, 1
title_colorはタイトルの文字色を設定します。デフォルトは[1, 1, 1, 1]です。16進数指定もできます。色の設定については下記の記事をご覧ください。
title_font: "./font_file.ttf"
title_fontはタイトルのフォントファイルを指定します。絶対パスで記述することをお勧めします。
title_size: sp(15)
title_sizeはタイトルのフォントサイズを設定します。デフォルトは14spです。上記の書き方は他の単位に変換する方法で書いています。
セパレーターを設定する
下記はセパレーター関連の設定になります。
separator_color: 180/255, 164/255, 176/255, 1
separator_height: dp(4)
separator_color: 180/255, 164/255, 176/255, 1
separator_colorはセパレーターのラインの色を設定します。デフォルトは[47 / 255, 167 / 255, 212 / 255, 1]になります。
separator_height: dp(4)
separator_heightはセパレーターの高さを設定します。デフォルトは2dpです。
popupの外側をクリックした時の動作を設定する
auto_dismiss: False
auto_dismissはpopupウィンドウの領域外をクリックした場合にpopupウィンドウを閉じるかを設定します。デフォルトはTrueです。Falseはこれを行わないのでボタンを実装する必要があります。
popupのサイズを設定する
Kivyにはポップアップのサイズを設定するプロパティは存在しないので、size_hintを使用します。
size_hint: 0.6, 0.4
size_hintの詳細は下記の記事をご覧ください。
popupウィンドウの背景色を設定する
popupウィンドウの背景色を設定するプロパティは存在しないのでbackground_colorを使います。
background: ''
background_color: 228/255, 216/255, 225/255, 1
ここで重要なのが一度デフォルトのbackground情報をクリアする必要があります。backgroundに空文字を指定し、その次の行にbackground_colorで背景色を設定します。
Content領域を設定する
この領域、クラスルールの記述と同じように、ルートウィジェットにレイアウトを定義し、ウィジェットをツリーにして配置します。
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgba: 207/255, 190/255, 202/255, 1
Rectangle:
size: self.size
pos: self.pos
Label:
text: "ポップアップだよ"
color: 100/255, 100/255, 100/255, 1
Content領域の背景色を設定したい場合は、レイアウトの背景色を設定するのと同じでcanvasを使用します。レイアウトの背景色は下記の記事をご覧ください。
popupの閉じるボタンを実装する
popupのボタン用のイベント関数はないので自作で関数を作る必要があります。閉じるだけの動作ならdismiss()メソッドが使えます。
Button:
text: "Close"
background_color: 215/255, 145/255, 197/255, 1
on_release: root.dismiss()
on_release: root.dismiss()
on_releaseイベントかon_pressイベントを使用してdismiss()を呼び出します。
popupウィンドウを実行する
popupウィンドウを実行するには何らかのウィジェットでon_releaseイベントかon_pressイベントを使用してopen()メソッドを呼び出します。
<RootWidget>:
Button:
text: "Open popup"
on_release: Factory.CustomPopup().open()
名前空間のFactoryはインポート文で定義したエイリアス名で、その次のCustomPopup()は動的クラスルールの定義で記述したサブクラス名になります。Factoryの登録はここではkvファイルに記述していますが、pyファイルで書いても良いです。
popupにOKボタンとキャンセルボタンを実装する
ボタンの処理を実装するサンプルコードも紹介しておきます。動的クラスルールにこだわったせいで何気に難しかったです。筆者もいい勉強になりました。せっかくなのでFactoryもpyファイルに書く方法で記述しました。
popup2.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory
class RootWidget(BoxLayout):
popup_instance = None # ポップアップインスタンスを管理する変数
def open_popup(self):
# ポップアップを開く
self.popup_instance = Factory.CustomPopup()
self.popup_instance.open()
def on_ok(self):
self.ids.label.text = "OKが押されたよ"
self.dismiss_popup() # ポップアップを閉じる
def on_cancel(self):
self.ids.label.text = "Cancelが押されたよ"
self.dismiss_popup() # ポップアップを閉じる
def dismiss_popup(self):
# ポップアップが開いている場合のみ閉じる
if self.popup_instance:
self.popup_instance.dismiss()
self.popup_instance = None
class Popup2(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
Popup2().run()
popup2.kv
<CustomPopup@Popup>:
# Popupウィンドウの設定
title: "Popup Title"
title_align: "center"
title_color: 100/255, 100/255, 100/255, 1
#title_font: "./font_file.ttf"
title_size: sp(15)
separator_color: 180/255, 164/255, 176/255, 1
separator_height: dp(4)
auto_dismiss: False
size_hint: 0.6, 0.4
background: ''
background_color: 228/255, 216/255, 225/255, 1
# Content領域の設定
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgba: 207/255, 190/255, 202/255, 1
Rectangle:
size: self.size
pos: self.pos
Label:
text: "ポップアップだよ"
color: 100/255, 100/255, 100/255, 1
BoxLayout:
padding: 30, 30
spacing: 10
Button:
text: "OK"
on_press: app.root.on_ok()
Button:
text: "Cancel"
on_press: app.root.on_cancel()
<RootWidget>:
Label:
id: label
text: "popupが表示されるよ"
font_size: 30
Button:
text: "Open Popup"
font_size: 30
on_release: root.open_popup()
コード解説
下記はPythonコードで定義している関数です。
open_popup() # Factoryに登録しpopupを実行する関数
on_ok() # 「OK」ボタンを押すと呼び出せれる関数
on_cancel() # 「キャンセル」ボタンを押すと呼び出される関数
dismiss_popup() # ポップアップが閉じられると呼ばれる関数
通常閉じるにはdismiss()を呼び出せばいいのですが、ここではFactoryで登録している関数と他の関数が分かれているためスコープの関係でポップアップのインスタンスを参照できません。例えばon_ok()で閉じるためにポップアップのインスタンスが必要ですがopen_popup()でインスタンスの生成を行っているため参照できません。そこでポップアップインスタンスを参照するためのクラス変数を定義します。そんな理由からdismiss_popup()を作成し、on_ok()やon_cancel()でdismiss_popup()を呼び出してpopupを閉じています。
Pythonコード
popup_instance = None
他の関数からpopupのインスタンスを参照するために定義しています。初期値はNoneを代入します。
def open_popup(self):
# ポップアップを開く
self.popup_instance = Factory.CustomPopup()
self.popup_instance.open()
open_popup()はFactoryに登録し、ポップアップを実行しています。動的クラスルールは定義しているだけなのでFactoryに登録しインスタンスを生成する必要があります。
from kivy.factory import Factory
self.popup_instance = Factory.CustomPopup()
popup_instanceにCustomPopup()のインスタンスを代入しています。
self.popup_instance.open()
open()でポップアップを起動します。
def on_ok(self):
self.ids.label.text = "OKが押されたよ"
self.dismiss_popup() # ポップアップを閉じる
on_ok()では’OK’ボタンや’キャンセル’ボタンを押すとLabelに表示されるようにしています。またポップアップの閉じる処理をしています。
self.ids.label.text = "Cancelが押されたよ"
Labelのid ‘label’ のtextプロパティを参照し変更しています。
self.dismiss_popup()
dismiss_popup()を呼び出して、ポップアップを閉じる処理をしています。
def on_cancel(self):
self.ids.label.text = "Cancelが押されたよ"
self.dismiss_popup() # ポップアップを閉じる
on_cancel()でもon_ok()と同じ処理をしています。
def dismiss_popup(self):
# ポップアップが開いている場合のみ閉じる
if self.popup_instance:
self.popup_instance.dismiss()
self.popup_instance = None
dismiss_popup()ではポップアップの閉じる処理をしています。
if self.popup_instance:
popup_instanceにインスタンスが代入されているかを評価し、ポップアップが起動されている場合にだけ次の処理を実行します。
self.popup_instance.dismiss()
dismiss()でポップアップを閉じます。
self.popup_instance = None
2回目以降ににポップアップが起動された時の為にpopup_instanceに代入されているインスタンスを破棄しています。
kvファイル
Button:
text: "OK"
on_press: app.root.on_ok()
Button:
text: "Cancel"
on_press: app.root.on_cancel()
on_pressイベントでクリックされた時にon_ok()またはon_cancel()を呼び出しています。通常rootキーワードだけで呼び出せますが、今回は動的クラスルールを使用しているためCustomPopup()はルートウィジェットのツリーに存在しないのでrootでは参照できません。
app.rootについて説明する前に、クラスの構造について確認しておきましょう。ダイナミック・クラスをPythonコードで書いた場合は次のようになります。
class CustomPopup(Popup):
pass
class RootWidget(BoxLayout):
pass
動的クラスルールはクラスを継承したサブクラスなのでルートウィジェットとはスコープが異なります。rootキーワードで参照できるのはルートウィジェットになるため、今回はapp.rootになります。
appキーワードはAppクラスのインスタンスを参照できます。つまり、現在実行しているアプリケーションのインスタンスです。そして、rootキーワードはルートウィジェットを参照できます。この2つのキーワードを繋げてapp.rootにすることで、appキーワードを経由してrootが持っているメソッドにアクセスしています。
rootなどのキーワードとidについての詳細は下記の記事をご覧ください。
Comment