【 Python + Kivy 】仕事の合間企画! Python で Kivy を使ってみる! 今回は RecycleView を使ってみた。データ更新時も自動で反映してくれるのでお気楽です。 投稿一覧へ戻る
Published 2020年10月18日20:36 by T.Tsuyoshi
kivy 1.10 で ListView が廃止され、RecycleView を使ってね、ということらしいので使ってみました。
何の変哲も面白味もないけど、こんな感じ。
TextInput に入力して (このプログラムでは日本語未対応) 3 つのうちのいずれかのボタンを押すと、ボタンテキストの「...」の部分を TextInput の内容に置き換えた文字列を text とした Button widget を item として RecycleView にリスト表示します。
RecycleView 内のアイテムは交互に背景色を変えて追加していきます。
RecycleView の item をクリックすると、本来であれば何らかの処理を行うんでしょうが、今回はただ標準出力にプリントアウトします。
内容は、Button 自身の text と、RecycleView 内での自分の位置を示すインデックス番号 (上から 0, 1, ...)。
ドンドン押すとこんな感じに出力されます。
RecycleView を使ってみた感想は「いい感じ」。
データの変更や追加をした場合、on_data_changed イベントが投げられ、それに反応して refresh_from_data() が自動的に実行されるので、「ただ表示内容が更新されればいいや」ということであればこちら側でやることは何一つありません。
まずは python ファイル。
(main.py)
import re
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleboxlayout import RecycleBoxLayout
class AppRoot(BoxLayout):
pat = re.compile(r'\.{3}')
my_rv = ObjectProperty()
def choose_data(self, btn):
print(f"Index {btn.index}: {btn.text}")
def create_data(self, btn):
data_txt = self.pat.sub(self.ids.input.text.capitalize(), btn.text)
self.my_rv.data.append({'text': data_txt}) # 2:
class MyRecycleBoxLayout(RecycleBoxLayout):
def add_widget(self, widget, index=0, canvas=None): # 4:
widget.index = self.view_indices[widget] # 6:
if widget.index % 2:
widget.background_color = 0, 0, 1, 0.5
else:
widget.background_color = 1, 0, 0, 0.5
super().add_widget(widget, index, canvas) # 5:
class RVApp(App):
pass
if __name__ == '__main__':
RVApp().run()
import re
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleboxlayout import RecycleBoxLayout
class AppRoot(BoxLayout):
pat = re.compile(r'\.{3}')
my_rv = ObjectProperty()
def choose_data(self, btn):
print(f"Index {btn.index}: {btn.text}")
def create_data(self, btn):
data_txt = self.pat.sub(self.ids.input.text.capitalize(), btn.text)
self.my_rv.data.append({'text': data_txt}) # 2:
class MyRecycleBoxLayout(RecycleBoxLayout):
def add_widget(self, widget, index=0, canvas=None): # 4:
widget.index = self.view_indices[widget] # 6:
if widget.index % 2:
widget.background_color = 0, 0, 1, 0.5
else:
widget.background_color = 1, 0, 0, 0.5
super().add_widget(widget, index, canvas) # 5:
class RVApp(App):
pass
if __name__ == '__main__':
RVApp().run()
続いて kivy ファイル。
(rv.kv)
AppRoot:
<AppRoot@BoxLayout>:
orientation: 'vertical'
my_rv: MyRv
BoxLayout:
height: dp(56)
size_hint_y: None
TextInput:
id: input
size_hint_x: 2
padding_y: dp(20)
text: 'Nana'
CreateDataButton:
text: '... is cute!'
CreateDataButton:
text: '... is smart!'
CreateDataButton:
text: 'I love ...!'
RV:
id: MyRv
<CreateDataButton@Button>:
size_hint_x: 1
font_size: dp(20)
on_press: app.root.create_data(self)
<BackgroundColor@Widget>:
background_color: 1, 1, 1, 1
canvas.before:
Color:
rgba: root.background_color
Rectangle:
size: self.size
pos: self.pos
<BackgroundButton@Button+BackgroundColor>:
index: -1
font_size: dp(20)
on_press: app.root.choose_data(self)
<RV@RecycleView>:
viewclass: 'BackgroundButton' # 1:
MyRecycleBoxLayout: # 3:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
AppRoot:
<AppRoot@BoxLayout>:
orientation: 'vertical'
my_rv: MyRv
BoxLayout:
height: dp(56)
size_hint_y: None
TextInput:
id: input
size_hint_x: 2
padding_y: dp(20)
text: 'Nana'
CreateDataButton:
text: '... is cute!'
CreateDataButton:
text: '... is smart!'
CreateDataButton:
text: 'I love ...!'
RV:
id: MyRv
<CreateDataButton@Button>:
size_hint_x: 1
font_size: dp(20)
on_press: app.root.create_data(self)
<BackgroundColor@Widget>:
background_color: 1, 1, 1, 1
canvas.before:
Color:
rgba: root.background_color
Rectangle:
size: self.size
pos: self.pos
<BackgroundButton@Button+BackgroundColor>:
index: -1
font_size: dp(20)
on_press: app.root.choose_data(self)
<RV@RecycleView>:
viewclass: 'BackgroundButton' # 1:
MyRecycleBoxLayout: # 3:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
1: RecycleView の定義も難しいものではなく、リストの 1 つ 1 つの item として表示・利用したい widget を viewclass プロパティの値として設定するだけ。
2: あとは RecycleView の data プロパティにデータを追加してやれば表示されます。
この data プロパティの実体は list ですから、append() で追加したり、extend() で拡張できます。
また、この list の 1 つ 1 つの要素は辞書 ( dictionary ) です。
この辞書の key は、viewclass で指定した widget のプロパティ名。
value は言うまでもなく設定する値。
3: 今回の RecycleView では、それぞれの item をレイアウトする widget として BoxLayout から派生させたサブクラスを利用。
4: python ファイルのクラス定義内で add_widget() をオーバーライドして情報を横取りしてます。
5: もちろんご利用は計画的に、使用後は super() に返済します。
6: RecycleBoxLayout widget の view_indices プロパティは、{ viewclass オブジェクト: レイアウト内のインデックス番号} から成る dict なので、それを利用して各 item の背景色を交互に設定しています (この方法が正しいのかどうなのかは不明、自己責任でお願いします)。
今回のプログラムの欠点は、一度追加した item の削除に対応していないことです。
是非ご自分で実装してみてください。
1 つ 1 つの item を layout としてもち、「削除」ボタンを配置して、それが押されたらその item を RecycleView の data リストから削除して再描画、で出来るような気がしますが、そのとき各 item で保持しているインデックス番号を更新する必要が生じそうですね。
まだまだ色々と楽しめそうです。
こちらの投稿にも興味があるかもしれません...
- 【 Python + Kivy で Android 】仕事の合間企画! Python で Kivy を使って GUI ストップウォッチを作って、python-for-android と Buildozer を使って Android APK にしてスマホで動かしてみた、もちろん Windows 上でも動きます、Mac? 多分、でも試してないから分からない、の巻
- 【 Fluent Python + coding challenge 】List の List を作るときには要注意!!
- Django の forms は使わずに jQuery で POST / GET リクエスト、レスポンスで template を返してページの一部だけを入れ替えて四択クイズを作ってみた!
- 【 Using Asyncio in Python 】Python における asyncio を利用した非同期プログラム ( asynchronous programming ) の勉強を継続する前に、今一度スレッド ( thread ) についてのちょっとした復習ノート、まとめてみた
0 comments
コメントはまだありません。
コメントを追加する(不適切と思われるコメントは削除する場合があります)