WPP
KivyMD の NavigationDrawer に RecycleView を追加するサンプル

引き続き、Kivy です。

今回は、KivyMD の NavigationDrawer の中に RecycleView を突っ込んでみます。Kivy / KivyMD はここのコンポーネントを動作確認する分には特別難しいことはありませんが、複数組み合わせるとトタンになかなかいい感じに配置することができないのが難点だと思う。(自分の理解の浅さのせいかもしれないが、HTML/CSS の方がまだましに感じます。)

提示するサンプルを実行するとこんな感じになります。前回と同じように Appbar が画面上部にあり、 Appbar のアイコンを押すと、NavigationDrawer が表示されます。その NavigationDrawer の中に 果物の名前のリストを表示しています。

Appbar のアイコンが左で、ドロワーが右から出てくるのは違和感があるかもしれませんが anchor の確認のためにあえて行っています。

環境

  • Kivy 2.3.0
  • KivyMD 2.0.1.dev

コード

from kivy.lang import Builder

from kivy.properties import StringProperty

from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.recycleview import MDRecycleView


KV = '''
MDScreen:

    MDNavigationLayout:
        # size_hint_x: 1

        ScreenManager:
            MDScreen:

                MDBoxLayout:
                    md_bg_color: self.theme_cls.secondaryContainerColor
                    orientation:'vertical'
                    
                    MDTopAppBar:                        
                        MDTopAppBarLeadingButtonContainer:
                            MDActionTopAppBarButton:
                                icon: 'menu'
                                on_release: nav_drawer.set_state('toggle')
                            
                        MDTopAppBarTitle:
                            text: 'this is AppBar'
                            pos_hint: {'center_x': .5}
                            
                    MDLabel:
                        text: "I'm MDLabel"

        MDNavigationDrawer:
            anchor: 'right'

            MDBoxLayout:
                orientation: 'vertical'

                MDNavigationDrawerLabel:
                    text: 'drawer items!!'
                    icon: 'format-list-bulleted'

                MDNavigationDrawerDivider:

                NvDrawerRecycleItem:                    
                    id: rv
                    padding: 0, nav_drawer.height, 0, 0
                    viewclass: 'Food'
                    RecycleBoxLayout:
                        size_hint: (1, None)
                        height: self.minimum_height
                        orientation:'vertical'

<NvDrawerRecycleItem>:

<Food>:
    text: 'name: {}'.format(self.item)
    adaptive_size: True
    padding: 0, dp(16), 0, dp(16)

'''

class Food(MDLabel):
    item = StringProperty('')

class NvDrawerRecycleItem(MDRecycleView):
    pass


class TestNavigationDrawer(MDApp):
    def build(self):
        self.theme_cls.theme_style = 'Dark'  # or 'Light'
        return Builder.load_string(KV)
    
    def on_start(self):
        rv = self.root.ids.rv
        rv.data = (
            { 'item': it }
            for it in [
                'Apple', 'Banana', 'Cherry', 'Grape', 'Lemon',
                'Mikan', 'Orange', 'Pear', 'Lychee', 'Strawberry',
                'Peach', 'Fig'
            ]
        )


TestNavigationDrawer().run()

公式のサンプルでは MDNavigationDrawerMenu の中に Drawer の中身を入れてるのですが、そうすると RecycleView と MDNavigationDrawerMenu の間に大きな隙間ができてしまい。どうしても制御することができませんでした。

また、MDBoxLayout: orientation: ‘vertical’ で囲ってあげないと ドロワー系のアイテムと RecycleView が横並びになってしまいました。

そのため、上のようなサンプルコードになりました。


        MDNavigationDrawer:
            id: nav_drawer
            anchor: 'right'

            MDBoxLayout:
                orientation: 'vertical'

                MDNavigationDrawerMenul:
                    MDNavigationDrawerLabel:
                       text: 'drawer items!!'
                       icon: 'format-list-bulleted'

                      MDNavigationDrawerDivider:

                NvDrawerRecycleItem:                    
                    id: rv
                    padding: 0, nav_drawer.height, 0, 0
                    viewclass: 'Food'
                    RecycleBoxLayout:
                        size_hint: (1, None)
                        height: self.minimum_height
                        orientation:'vertical'

今作りたいものをやるのにドロワーの中にリスト入れればいけるんじゃね?と安直に考えてやってみたがなかなかしんどかった。Kivy の道はきびしい。

KivyMD の Appbar 画面上部にピッタリに配置する方法

さあ、今日も Kivy の話です。

KivyMD にはあたかも Android アプリのような Appbar というコンポーネントがあります。これを画面のトップに配置して下部分に大きさピッタリのコンテンツを配置する方法のメモです。

やりたいことは、2つあります。

  1. Appbar を画面トップに配置する。
  2. 空いた下の領域いっぱいにコンテンツを表示する。

結果はこんな感じになります。

画像を入れる

環境

  • Kivy 2.3.0
  • KivyMD 2.0.1.dev

最終的なサンプル

from kivy.lang import Builder

from kivymd.app import MDApp

KV = '''
MDScreen:
    MDBoxLayout:
        orientation: 'vertical'
        padding: 0,app_bar.height, 0,0      # L T R B

        MDBoxLayout:
            md_bg_color: 0.2,0.2,0.4, 1
            MDLabel:
                text: 'text label'
                valign: 'bottom'

    MDTopAppBar:
        id: app_bar
        pos_hint: {'top': 1}
        
        MDTopAppBarLeadingButtonContainer:
            MDActionTopAppBarButton:
                icon: 'menu'
                
        MDTopAppBarTitle:
            text: 'top appbar'
        
        MDTopAppBarTrailingButtonContainer:
            MDActionTopAppBarButton:
                icon: "github"
'''

class MainApp(MDApp):
    def build(self):
        self.theme_cls.theme_style = 'Dark'  # or 'Light'
        return Builder.load_string(KV)
    
if __name__ == '__main__':
    MainApp().run()

画面トップに配置

自分が試した限りでは、 TopAppBar を公式サンプルのまま配置しただけでは画面の上部に表示することはできませんでした。(これバグ何じゃないだろうか ?)

なので pos_hint に ‘top’: 1 を設定してやります。kivy では画面左下が原点になるので ‘top’: 1 は画面の最上部を意味します。(0 – 1.0 が入り割合を示すらしい。)

    MDTopAppBar:
        id: app_bar
        pos_hint: {'top': 1}
        

コンテンツを Appbar の下ギリギリまで配置する

次に、コンテンツ領域の制御です。ここはなにも制限しないと親の大きさいっぱいまで使ってしまいます。つまりコンテンツ領域が Appbar で隠れるか、 Appbar を隠すかしてしまいます。(これは .KV 内の定義の順番によるっぽいです。)

Stack overflow で見つけた解決策は padding を設定して下の領域の大きさを明示的に制限するというものでした。こんな感じです。

padding に設定してやるのは Appbar の高さなので id: 属性を追加します。

追加した id (Appbar) の高さ分 padding でコンテンツ領域を下げてやります。

    MDBoxLayout:
        orientation: 'vertical'
        padding: 0,app_bar.height, 0,0      # Left Top Right Bottom

   # いろいろ省略

    MDTopAppBar:
        id: app_bar
        pos_hint: {'top': 1}
        # size_hint_x: 1

Kivy / KivyMD は配置周りの挙動がよくわからないのが辛い。

参考

python – How do i make an MDBottomAppBar appear on top of a list view in KivyMD – Stack Overflow

Appbar — KivyMD 2.0.1.dev0 documentation

Kivy のログレベル変更

さて、今日も Kivy です。 Kivy 戦記ってブログがあるんですが、Kivy をやってる時はまさに「戦い」って感じでいちいち面倒くさいです。

Kivy をコンソールから実行するとログが流れてきます。そのログレベルを変更しようと思います。
というのも、.kv ファイルの記述どおりにどうも動かない現象にぶち当たりましたがデフォルトのログレベルではなにも異常はなさそうだったのでもう少し詳しく見る必要がありました。

どうやら Kivy.config にコンフィグ設定値があるようです。
ログレベルに限らず、Config は環境変数で渡せるようですが、設定ファイルを使ってみます。

自分の環境ではそもそも設定ファイルは、存在しませんでした。おそらくそれが普通のようです。Config のドキュメントから設定値読み取っていちいち手入力するのはだるいなと思っていたら、ファイルに書き出す機能がありました。

$ python -m kivy
>>> from kivy.config import Config as cfg
>>> cfg.write()

こうすると ~/.kivy/config.ini が生成されているはずです。Win/Mac もそれっぽいところにあります。

それはそうと config.ini を書き換えていきます。log 周りの設定だけでなくデフォルトフォントの設定など色々ありそうですが、今回はつかわないので log_level を debug に変更します。

info や debug 以外にもドキュメントにある通り warn, error, trace なんかも設定できるようです。

[kivy]
..... いろいと並んでいる
log_dir =
log_enable =1
log_level = debug
....

にしても Kivy の API (クラス) リファレンスは、キーワードと説明の区別がつけにくくて読み取りにくいのがどうも好きになれない。

参考

Configure Kivy — Kivy 2.3.0 documentation

Configuration object — Kivy 2.3.0 documentation

pipenv で setup.py のパッケージをインストールする方法

pipenv の仮想環境で setup.py をインストールする方法です。

KivyMD の最新を git から持ってくると setup.py でのインストールを求められますが、やり方を知らなかったのでメモします。

pipenv の公式に載っているのですが run pip install すればいいらしい。

$ pipenv run pip install -e .

ちなみに、git で取ってきた KivyMD の example を動作させるには、プロジェクトのルートで python example/xxx.py のように実行するか、PYTHONPATH に追加する必要がある。インストールしても仮想環境にコピーされないので注意。

ついでに requirements.txt でインストールする場合は以下のようになる

$ pipenv install -r ./requirements.txt

参考

pipenv · PyPI

kivymd/KivyMD: KivyMD is a collection of Material Design compliant widgets for use with Kivy