PythonとKivyを使ったアプリ開発の旅② Python + kivyアプリ開発でkvファイルを徹底活用!
こんにちは、momozoです!
PythonのGUIフレームワーク「kivy」を使って、人狼ゲームのGM(ゲームマスター)アプリを作ろうと奮闘しています(前回の記事は以下)。
https://looblog.com/python%e3%81%a8kivy%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%9f%e3%82%a2%e3%83%97%e3%83%aa%e9%96%8b%e7%99%ba%e3%81%ae%e6%97%85%e2%91%a0%e3%80%80%e5%88%9d%e3%82%81%e3%81%a6%e3%81%ae%e3%82%a2%e3%83%97%e3%83%aa/kivyは、Pythonで簡単にクロスプラットフォームなGUIアプリが作れる優れもの。
その中でも、UIデザインを記述するkvファイルは、コードとデザインを分離できる便利な仕組みです。
アプリを作っているとどうしてもコードが煩雑になるので、処理とレイアウトはファイルを分けて管理したいところですよね。
今回は、kvファイルの使い方や、Pythonコードとの紐づけ方、アプリ開発中に直面した課題、そしてその解決策について共有したいと思います。
Pythonとkvファイルの連携
kivyでは、Pythonコードとkvファイルを連携させる方法がいくつかあります。
最も簡単な方法は、Pythonで設定したアプリ名と同じ名前のkvファイル名にすることです。
例えば、JinroGmApp
というアプリ名にした場合、jinrogmapp.kv
というkvファイルを作成します。
ファイル配置例
project_folder/
├── main.py
└── jinrogmapp.kv
さらにフォルダ分けすることもできると思いますが、今回は必要性を感じなかった。
ファイル名称の付け方
【main.py】
class MainWidget(Widget):
member_list = ListProperty()
member_caption_list = ListProperty(['' for _ in range(40)])
reg_list = ListProperty()
reg_caption_list = ListProperty(['' for _ in range(20)])
position_list = ListProperty()
status_list = ListProperty()
<中略>
class JinroGmApp(App):
def __init__(self, **kwargs):
super(JinroGmApp, self).__init__(**kwargs)
self.title = '人狼GMツール'
if __name__ == '__main__':
JinroGmApp().run()
main.pyで、メインのクラス名称にkvファイルで定義したWidget名を指定し、一番下には、上記のようにアプリ定義をしています。
【jinrogmapp.kv】
<MainWidget>:
BoxLayout:
orientation: 'vertical'
size: root.size
BoxLayout:
size_hint_y: 2.5
BoxLayout:
size_hint_x: 2
orientation: 'vertical'
Label:
size_hint_y: 2
font_size: 20
text: "人狼GMアプリ"
kvファイルの一番上に「MainWidget」という名前を定義しています。
こうすることで、kivyは自動的にmain.py
と
を関連付け、kvファイルで定義されたUI要素をPythonコードから利用できるようになります。jinrogmapp.kv
その他の方法
より柔軟な連携方法として、Builder.load_file()
関数を使ってkvファイルを明示的に読み込むこともできます。
こうすることで、kvファイル名をアプリ名と一致させなくてもよく、自由にファイル名を指定することができます。
from kivy.lang import Builder
# KVファイルのロード
Builder.load_file('jinrogmapp.kv')
main.pyの上部import設定に、上記のような定義をします。
この方法では、kvファイルのパスを指定することで、任意のkvファイルを読み込むことができます。
kvファイルでスッキリUIデザイン!
kivyでは、Pythonコードの中にUIデザインを直接書くこともできますが、kvファイルを使うことで、コードとデザインをキレイに分離できます。
これにより、UIデザインの変更が容易になり、コードの可読性も向上します。
例えば、人狼GMアプリでは、以下のようなUI要素をkvファイルで定義しました。
<MainWidget>:
BoxLayout:
orientation: 'vertical'
size: root.size
BoxLayout:
size_hint_y: 2.5
BoxLayout:
size_hint_x: 2
orientation: 'vertical'
Label:
size_hint_y: 2
font_size: 20
text: "人狼GMアプリ"
BoxLayout:
size_hint_y: 8
BoxLayout:
size_hint_x: 5
Image:
id: app_img
source: 'yupi_icon.png'
BoxLayout:
size_hint_x: 5
orientation: 'vertical'
Button:
font_size: 20
text: "参加者登録"
background_color: (1, 0, 1, 1)
on_press: root.menber_reg_popup_open()
Button:
text: '全てをリセット'
font_size: 20
background_color: (1, 0, 1, 1)
on_press: root.memberResetClicked()
Button:
text: 'ゲームメンバーをリセット'
font_size: 20
background_color: (1, 0, 1, 1)
on_press: root.regResetClicked()
Button:
text: 'ゲーム内容をリセット'
font_size: 20
background_color: (1, 0, 1, 1)
on_press: root.gameResetClicked()
BoxLayout:
size_hint_x: 5
orientation: 'vertical'
Label:
font_size: 20
text: "参加者リスト(※参加者名をタップするとゲームメンバーに追加できます)"
以下略
上記のように、kvファイルでは、BoxLayout
、Label
、Button、ImageなどのUI要素を組み合わせてレイアウトを定義します。
Pythonコードからは、id
を使ってこれらのUI要素にアクセスし、内容を動的に変更することができます。
kvファイル分割の落とし穴
さて、ここで一つ疑問が湧きました。
「kvファイルを分割して、UI要素ごとに管理できたらもっと便利なのでは?」
そう思って色々と調査して、include
を使ってkvファイルを分割しようと試みましたが、うまくいきませんでした。
試してみたのはこんな感じの記述です。
# MainWidget.kv
<MainWidget>:
BoxLayout:
orientation: 'vertical'
size: root.size
BoxLayout:
size_hint_y: 2.5
# ... (ヘッダー部分の定義)
BoxLayout:
size_hint_y: 7.5
BoxLayout:
size_hint_x: 5
Label:
# ... (ラベルの定義)
BoxLayout:
size_hint_y: 7
Include:
file: 'member_list.kv' # 参加者リストをインクルード
# ... (ゲームメンバーリストとボタンの定義)
Include:
file: 'game_member_list.kv' # ゲームメンバーリストをインクルード
Include:
file: 'day_result.kv' # 各日の結果をインクルード
他にもいくつかWebで見つけた書き方を試したがうまくいかなった。
今のところ、kivyではkvファイルの分割はサポートされていないように見えます。
動的なUI要素はどうする?
人狼GMアプリでは、ゲームの進行状況によって表示するUI要素が変化します。
例えば、生存者の人数に応じて参加者リストを更新したり、役職を非表示にしたりする必要があります。
このような動的なUI要素は、kvファイルだけでは実現できません。
そこで、PythonコードでUI要素を動的に生成・更新する方法を検討しました。
kivyでは、PythonコードからWidgetを生成し、UIに追加することができます。
【main.py】
target_box = self.ids[f'day{day_number}_result']
button = Button(text=f"{day_number}日目", font_size=20, background_color=COLORS['blue'])
target_box.add_widget(button)
label = Label(text='【処刑者】', size_hint_y=None, height=80)
target_box.add_widget(label)
dropdown = DropDown()
main_button = Button(text="-", size_hint=(1, 0.8), pos_hint={"y": 0.9}, disabled_color=COLORS['default'])
self.ids[f'syokei_{day_number}'] = main_button
for index, item in enumerate(self.reg_list):
if self.status_list[index] == '1':
btn_color = COLORS['red'] if self.position_list[index] == '人狼' else COLORS['green']
btn = Button(text=item, size_hint_y=None, height=80, background_color=btn_color)
btn.bind(on_release=lambda btn: dropdown.select(btn.text))
dropdown.add_widget(btn)
main_button.bind(on_release=dropdown.open)
dropdown.bind(on_select=lambda instance, x: setattr(main_button, 'text', x))
target_box.add_widget(main_button)
上記のように、add_widget
メソッドを使ってUI要素を動的に追加することができます。
これにより、ゲームの状況に合わせてUIを柔軟に変更することが可能になりました。
※詳細に関してはまた別の記事で解説したいと思います。
kvファイル活用のまとめ
kvファイルは、kivyアプリ開発において非常に便利な機能です。
UIデザインをコードから分離することで、開発効率とコードの可読性を向上させることができます。
ただし、kvファイルの分割や動的なUI要素の生成には制約があるため、注意が必要です。
今回の経験を通じて、kvファイルのメリットと注意点を知ることができました。
今後もkivyを使って、人狼GM用アプリの開発を進めていきたいと思います。
次回予告:kivyアプリ開発のさらなる深みへ!
次回は、kivyアプリ開発のさらなるステップとして、以下の内容について掘り下げていきます(あくまで予定ですw)。
- 人狼GM用アプリ要件とレイアウトの検討
- 各コンポーネントの特性を調べる
最後まで読んでいただき、ありがとうございました!
この記事が、あなたのkivyアプリ開発の参考になれば幸いです。
もしこの記事が気に入っていただけたら、SNSでシェアしていただけると嬉しいです。
また、コメントや質問もお気軽にどうぞ!