tediousdays’ blog

プログラミングに関するブログです

KV言語 (2)

以前の記事で取り上げたスクリーンマネージャのプログラムを,KV言語を用いて書き換えてみます.



まずは Pythonソースコード

from kivy.app import App
class screenmanagerkvApp(App):
    pass
screenmanagerkvApp().run()

KV側でルートウィジェットを定義するので,Python側では最低限のこと(Appクラスのサブクラスを定義し,runメソッドを走らせる)しかしていません.サブクラスの名前は


screenmanagerkvApp
ですので,KVファイルのデフォルト名は

screenmanagerkv.kv
となります.

続いてその screenmanagerkv.kv です.

ScreenManager:
   Screen:
      name: '0'
      Button:
         text: 'Button 0'
   Screen:
      name: '1'
      Button:
         text: 'Button 1'
   Screen:
      name: '2'
      Button:
         text: 'Button 2'
   Screen:
      name: '3'
      Button:
         text: 'Button 3'
   Screen:
      name: '4'
      Button:
         text: 'Button 4'

<Button>:
   font_size: 36
   on_press:
      self.parent.parent.current = self.parent.parent.next()

ScreenManager オブジェクトがルートウィジェットです.
前回も書いたとおり,この ScreenManager は Screen オブジェクトのみを子に持つことができます.上では5つの Screen オブジェクトを子に持っています.

Screen 自体は RelativeLayout というレイアウトクラスを継承していますので,さらに子を持たせて使うことになります.また Screen オブジェクトの name プロパティに名前となる文字列をわたし,画面切替のときに使えるようにしておきます.

さて5つの Screen はいずれも Button オブジェクトを子に持っていますが,KVスクリプトの一番下に Button クラスに関する記述があります.

<Button>:
   font_size: 36
   on_press:
      self.parent.parent.current = self.parent.parent.next()

これはクラスルールといって,Buttonクラスの各種プロパティのデフォルト値を定めるものです.クラスルールは < ... > のようにクラス名を不等号ではさんで書きます.このクラスルールに書いてあることは:

  • Button のデフォルトのフォントサイズは36です
  • on_press を押すと次の画面に切り替わります(前回の記事参照)

このようにメソッドをクラスルールの中に書くこともできますが,メソッドについてはなんでもかんでも書けるわけではありません.このことについては後日詳しく述べたいと思いますが,要はインデントが増えないような「簡単な」もので,on_*** で始まる特殊なメソッドしか書けません.



KV言語 (1)

今回はKV言語を取り上げます.

以前の記事で取り上げた BoxLayout に関するサンプルプログラムを,KV言語を使って書き換えてみようと思います.まずはサンプルプログラムの再掲です.

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

class layoutApp(App):
    def build(self):
        x1 = Button(text='x1')
        x2 = Button(text='x2', color=(1,0,0,1))
        x3 = Button(text='x3', font_size=32)
        box = BoxLayout(orientation='vertical')
        box.add_widget(x1)
        box.add_widget(x2)
        box.add_widget(x3)
        return box

layoutApp().run()

続いてスクリーンショットです.

f:id:tediousdays:20180506074745p:plain




上のプログラムをKV言語を使って書き換えてみます.このKV言語は Python スクリプトの中に書くことも,外部ファイルに書くこともできます.また外部ファイルに書く場合は,デフォルトのファイル名が決まっていて,そこに書くのが都合が良いと思います.

今回はデフォルト名の外部ファイルに書くことにします.デフォルト名についてはこの後ですぐ説明します.また,以後このファイルをKVファイルと呼ぶことにします.

さて,デフォルト名のKVファイルを用いる場合の Python スクリプトは以下のようになります.

from kivy.app import App
class layoutkvApp(App):
    pass
layoutkvApp().run()

たったの4行です.Appクラスのサブクラスを定義し,そのrunメソッドを実行するというだけのものです.

KVファイルのデフォルト名は,Appクラスのサブクラスの名前から決定されます.その決定ルールは次の通りです.

  • Appクラスのサブクラスの名前をすべて小文字にした文字列とする
  • ただし末尾のappは取り除く
  • 最後に .kv を付ける

今回の場合,サブクラスの名前は lauoutkvApp なので,すべてを小文字にすると

layoutkvapp
ですが,末尾の app を取り除いて .kv を付けた,

layoutkv.kv
がデフォルト名となります.

さてそのKVファイル layoutkv.kv ですが,中身は以下の通りです.

BoxLayout:
   orientation: 'vertical'
   Button:
      text: 'x1'
   Button:
      text: 'x2'
      color: 1,0,0,1
   Button:
      text: 'x3'
      font_size: 32

Python だけで書いた場合と比べて,きわめて簡潔に書くことができます.また各クラスのインポートも必要ありません.(正確には kivy.uix 以下の基本的なウィジェットクラスについてはインポートは不要です.その他のクラスはインポートの必要があるのですが,ここでは省略.)

Python ではインデントが文法の一部となっており,ネスト構造がインデントによって示されます.これと同じように,KV言語でもインデントが文法の一部となっています.

さて上記のKVファイルには以下のことが書かれています.

  • BoxLayoutがルートウィジェットである
  • その orientation プロパティの値は 'vertical' である(子どもは縦に並ぶ)
  • 3つのButtonを子どもに持つ
  • 1つ目のButtonの文字列は 'x1' である
  • 2つ目のButtonの文字列は 'x2' で,色は赤 (1,0,0,1) である
  • 3つ目のButtonの文字列は 'x3' で,フォントサイズは 32 である

こういったことが直感的に書けて,後で見直したときにもわかりやすいように(少なくとも私は)思います.

なお BoxLayout がルートウィジェットであることが指定されているので,Pythonスクリプトにおいてルートウィジェットを指定する必要がないことに注意してください.これまでは Appクラスのサブクラスにおいて build メソッドをオーバーライドし,ルートウィジェットを返すようにしてきましたが,KVファイルでルートウィジェットを指定する場合はこれが不要です.

KV言語にはまだまだ便利な機能があります.次回以降はそれらについて触れたいと思います.

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Creating Apps in Kivy Mobile with Python
価格:4259円(税込、送料無料) (2018/5/7時点)



スクリーンマネージャ

前回の記事を承けて、スクリーンマネージャのことを書きます。

スクリーンマネージャは、複数の画面 (スクリーン) を保持し、切り替えて表示したいときに使います。複数画面を持つアプリならば、スクリーンマネージャ系ウィジェットをルートウィジェットにするのが都合がよいでしょう。

今回は最も代表的なスクリーンマネージャ系ウィジェットである、ScreenManagerを紹介します。スクリーンマネージャ系ウィジェットには、このほかAndroid OS 風の ActionBar や紙芝居風のCarousel があります。このうち Carousel はシンプルでわかりやすいので、慣れないうちはこれを用いるのもよいかもしれません。

Kivyプログラミング Pythonで作るマルチタッチアプリ [ 久保 幹雄 ]

価格:3,456円
(2018/5/6 16:31時点)
感想(0件)

次のソースコードは、5つの画面を保持します。
それぞれの画面はボタン1つのみから成り、ボタンを押すと次の画面に切り替わります。

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen

class MyButton(Button):
    def on_press(self):
        self.parent.parent.current = self.parent.parent.next()
        # self.parent: Screen object
        # self.parent.parent: ScreenManager object

class screenmanagerApp(App):
    def build(self):
        sm = ScreenManager()
        for i in range(5):
            btn = MyButton(text='Button '+str(i), font_size=36)
            sc = Screen(name=str(i))
            sc.add_widget(btn)
            sm.add_widget(sc)
        return sm

screenmanagerApp().run()

起動直後の画面。
f:id:tediousdays:20180506102449p:plain

ボタンを押すと、アニメーション効果とともに画面が切り替わる。
f:id:tediousdays:20180506102556p:plain

ScreenManager は、Screenオブジェクトのみを子に持つことができます。このScreenは RelativeLayout というレイアウト系クラスのサブクラスですが、詳しくは省略します。このScreen に更に子を持たせて使うとよいでしょう。上の例では MyButton のみを子として持たせていますが、実際には BoxLayout など他のレイアウトを子として持たせ、GUIを構築していくことになるでしょう。

ですので、表示したいウィジェットを直接ScreenManager の子にしてはいけません。必ず Screen をはさんでください。 そして Screen には名前をつけておくと (nameプロパティ)、画面を切り替えるときに役立つかもしれません。(お茶を濁した書き方ですが後で書きます。)

画面を切り替える

ScreenManager の current プロパティに、切り替え先画面の name プロパティの値を渡します。

sm.current = '2'

のようにします。

上の例では、現在の画面の「次」の画面の name プロパティを返す next メソッドを用いています。

self.parent.parent.current = self.parent.parent.next()

なおここでの self は MyButton オブジェクト、その親は Screen オブジェクト、さらにその親が ScreenManager オブジェクトであることに注意しましょう。

切り替えのアニメーション効果を設定する

全部で8通りのアニメーション効果が準備されていて、それぞれについて細部 (スライドの方向など) を設定することができます。たとえば WipeTransition という効果を用いるには次のようにします。

from kivy.uix.screenmanager import WipeTransition
sm = ScreenManager(transition=WipeTransition())

詳しくは公式ドキュメンテーション Screen Manager — Kivy 1.10.1.dev0 documentation の Changing transitions をごらんください。

しかし...

GUI構築の基本的な考え方というのは、おおむねここまで述べてきた通りです。ウィジェットツリーを頭の中に描き、それを構築すること。GUIのおおまかなデザインを作れるようになったら、次はタッチなど各種イベントに対する処理をどのようにバインドするかを学んでいく必要があるのですが、その前に、こう思った人もいるのではないでしょうか?

ウィジェットツリー作るの面倒くさくね!? コードも見づらいし!

ウィジェットツリーを作るには、ウィジェットを生成し、それらを親ウィジェットに add_widget でつなげ、必要があれば初期値を渡す。またウィジェットクラスのインポートもしなければならない。確かに面倒です。

しかしKV言語という独自言語を使えば、より直感的にウィジェットツリーを構築し、コードを大幅に簡略化することも可能です。

次回はこのKV言語について取り上げたいと思います。

Kivy Blueprints【電子書籍】[ Mark Vasilkov ]

価格:2,760円
(2018/5/6 16:30時点)
感想(0件)


Kivy: Interactive Applications in Python【電子書籍】[ Roberto Ulloa ]

価格:2,146円
(2018/5/6 16:32時点)
感想(0件)


Kivy ー Interactive Applications and Games in Python - Second Edition【電子書籍】[ Roberto Ulloa ]

価格:3,681円
(2018/5/6 16:32時点)
感想(0件)


Kivy Cookbook【電子書籍】[ Hugo Solis ]

価格:4,090円
(2018/5/6 16:33時点)
感想(0件)

レイアウト

Kivy では、ウィジェットツリーを構築することによってGUIを構成することを、前回の記事で述べました。最小単位の部品としてのウィジェットには、

  • Label (文字列の表示)
  • Button (「押す」操作を受け付けるためのボタン)
  • CheckBox (チェックボックス, ラジオボタンとしての使用も可能)
  • Slider (スライド操作による数値入力が可能)
  • TextInput (文字列入力)

などがあります。詳しくは公式サイトのドキュメンテーション Widgets — Kivy 1.10.1.dev0 documentation などをご覧ください。

本稿では、このウィジェットを画面に配置するためのレイアウトについて述べます。

詳しくは触れませんが、Kivyでは日本語を表示することはできるものの (フォントの指定が必要です)、入力することは現状では難しいようです。それでも何とかして入力できるようにすることは可能なようですが、Windowsの例はこのページなどでご覧ください。





レイアウト

複数のウィジェットを規則的に配置するには、レイアウトを使います。
以下の例は、3つのボタン x1, x2, x3 を縦に並べるコードです。

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

class layoutApp(App):
    def build(self):
        x1 = Button(text='x1')
        x2 = Button(text='x2', color=(1,0,0,1))
        x3 = Button(text='x3', font_size=32)
        box = BoxLayout(orientation='vertical')
        box.add_widget(x1)
        box.add_widget(x2)
        box.add_widget(x3)
        return box

layoutApp().run()

f:id:tediousdays:20180506074745p:plain

ボタンの文字の色やフォントサイズを設定できることも合わせて確認してください。

このコードにおけるレイアウトとは、BoxLayoutオブジェクトである box です。BoxLayoutはウィジェットを縦もしくは横一列に並べるためのレイアウトです。上記では縦に並べる (orientation='vertical') 設定となっていますが、

        box = BoxLayout(orientation='horizontal')

とすれば横に並べることができます。

この box に、

        box.add_widget(x1)

のようにすることで、x1 を box の子とすることができます。

したがって box は x1,x2, x3 の3つの子を持ち、ルートウィジェットとして return されていることになります。

なおレイアウトには

などがあります。詳細は公式サイトのドキュメンテーション Layout — Kivy 1.10.1.dev0 documentation をご覧ください。

また、あるレイアウトを別のレイアウトの子にすることで (入れ子にする)、複雑な配置を実現することもできます。


しかしレイアウトだけではもの足りません。これでは単一画面のアプリしか作れないからです。次回の記事では、複数画面を保持し、切り替えるためのスクリーンマネージャを紹介します。*1



*1:ただしレイアウトだけでも複数画面のアプリをつくろうと思えば作れます。面倒くさいですが。

Hello, world.

今日からプログラミングに関する記事を書いていきます。
対象言語は C, C++, Python を予定していますが、 当面は PythonKivyライブラリを中心に据えようと思います。




Kivy は Pythonオープンソースライブラリで、マルチタッチGUIを作るためのものです。
Linux, MacOS, Windows などの主なデスクトップOSで開発が可能です。
またこれらのOSはもちろんのこと、Android OS や iOS 向けにアプリをビルドすることもできます。

公式サイトはここです。


早速 'Hello, world.' から。

from kivy.app import App
from kivy.uix.button import Button

class helloApp(App):
    def build(self):
        return Button(text='Hello, world.')

helloApp().run()

f:id:tediousdays:20180506064948p:plain

このように基本クラスである App クラスのサブクラスを定義し (ここでは helloApp)、そのオブジェクトを生成して run メソッドを走らせるとアプリが起動します。


Kivy のGUI構成のポイントは、ウィジェットツリーを構築することにあります。


ウィジェットツリーとは、ウィジェット (ボタンやラベルなどGUIの部品です) 同士の親子関係から定められる根付き木 (rooted tree, 詳しくはググってください) です。その根に相当するウィジェットルートウィジェットといい、Appクラス (のサブクラス) の build メソッドではルートウィジェットを返すことが求められます。

上記のコードでは、

    def build(self):
        return Button(text='Hello, world.')

となっていますので、Button オブジェクトがルートウィジェットとなるのです。(かつ、ウィジェットツリーはこの Button オブジェクトのみから成る。)

当然、ウィジェット1つでは面白くも何ともありません。
そこで複数のウィジェットを規則的に配置するレイアウトや、
複数のスクリーンを切り替えるためのスクリーンマネージャ
ウィジェットツリーに組み込んで使っていくことになるのですが、
これはまた次回の記事で書きたいと思います。