PR
Python+Kivy入門

Labelの使い方: KivyのWidgets1【kv言語】

この記事は約14分で読めます。

Kivyの基本ウィジェットであるLabelの使い方について説明します。また、Labelにロールオーバー機能を実装しマウスホバーするサンプルや、text文字を円や湾曲に変形させるサンプルコードも紹介しています。

Labelの使い方

Labelは文字を表示するためのウィジェットです。表示する文字をtextプロパティで指定します。

<RootWidget>:
    Label:
        text: "Labelに文字を設定する"
        font_size: 20
        #font_name: "fonts/meiryo.ttc"
        color: 1, 0, 0, 1
        halign: "justify"
        valign: "bottom"
        text_size: 500, 500

        bold: True
        italic: False
        underline: True
        max_lines: 10
        shorten: True
        shorten_from: "left"
        line_height: 1.2
        outline_color: 1, 1, 1, 1
        outline_width: 5
        padding: 10, 10, 10, 10
        font_hinting: "mono"
        markup: True

textを装飾するためのfontサイズや、文字色を変える、太字にするなど色々なプロパティが用意されています。また、HTMLタグのように文字の一部分だけ装飾を変えることができるマークアップと呼ばれるタグがあります。

Label:
    text: "[size=40][b]マークアップ[/b]も使えるよ[/font]"
    markup: True

これらの詳細については下記の記事で説明しています。

Labelで使えるイベント

Labelで使えるKivy標準イベントについて説明します。よく使われるのは下記のイベントになります。

  1. on_touch_down: タッチされたときに呼び出されます。
  2. on_touch_up: タッチが離されたときに呼び出されます。
  3. on_touch_move: ドラッグされたときに呼び出されます。

on_touch系のイベントは他のイベントとは異なり、伝播の仕組みを持っているため扱いが厄介です。on_touch_*を使うとイベントがツリーのウィジェットに伝播します。これらの使い方については下記の記事で説明しています。

Labelを使ったサンプルコード

Labelを使ったいくつかのサンプルコードを紹介します。

Labelにロールオーバー機能を実装する

Labelにマウスポインタが乗るとLabelの背景色を変えるマウスホバーのサンプルを紹介します。

Kivy マウスホバーでLabelの背景色をけるアプリ

label1.py

from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.label import Label
from kivy.properties import ListProperty
from kivy.core.window import Window

class HoverLabel(Label):
    bg_color = ListProperty([1, 1, 1, 1])  # 初期の背景色(白)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Window.bind(mouse_pos=self.on_mouse_move)

    def on_mouse_move(self, window, pos):
        x, y = self.to_widget(*pos)
        if self.collide_point(x, y):  # マウスがLabelの範囲内の場合
            self.bg_color = [0.2, 0.6, 1, 1]  # 青
        else:
            self.bg_color = [1, 1, 1, 1]  # 白


class RootWidget(RelativeLayout):
    pass

class Label1(App):
    def build(self):
        return RootWidget()

if __name__ == "__main__":
    Label1().run()

label1.kv

<HoverLabel>:
    canvas.before:
        Color:
            rgba: self.bg_color
        Rectangle:
            pos: self.pos
            size: self.size
    
    size_hint: None, None
    size: 300,50
    color: 0,0,0,1
    font_size: 40

<RootWidget>:
    HoverLabel:
        text: "Hover over 1!"
        pos_hint: {'center_x': 0.5, 'center_y': 0.6}

    HoverLabel:
        text: "Hover over 2!"
        pos_hint: {'center_x': 0.5, 'center_y': 0.4}

コード解説

このコードのポイントは4つです。

  1. Labelのカスタムウィジェット定義する
  2. Propertyクラスで色情報を管理する
  3. on_mouse_move()をオーバーライドする
  4. mouse_poson_mouse_move()をバインドする
Labelのカスタムウィジェット定義する

何故わざわざカスタムウィジェットを定義するのかというと、今回のケースではLabelの位置が取得できないからです。後ほど説明しますが、ウィンドウはWindowオブジェクトの座標を持っており、ウィジェットはWidgetオブジェクトの座標を持っています。

カスタムウィジェットを作らずにRootWidgetの方でon_mouse_move()を実装するとウィンドウの座標が取られ、画面領域全体が位置を取得する範囲になってしまいます。この例では、Labelの描画範囲の位置を取得し、その範囲にポインタが乗った時にだけ背景色を変えるので、Labelのカスタムウィジェットを定義しています。

class HoverLabel(Label):

Labelを継承したサブクラスを定義します。

kvコードの方ではLabelのスタイルを定義しています。

<HoverLabel>:
    canvas.before:
        Color:
            rgba: self.bg_color
        Rectangle:
            pos: self.pos
            size: self.size
    
    size_hint: None, None
    size: 200,50
    color: 0,0,0,1
    font_size: 24

Labelの背景色はレイアウトと同じ方法です。背景色を作る方法については下記の記事で説明しています。

canvasColorには、後述するPropertyクラスを使ってカラー値を代入した変数を指定します。

Color:
    rgba: self.bg_color

このようにすると動的にカラーを変更した時に、ここに変更された値が入るようになります。

<RootWidget>:
    HoverLabel:

    HoverLabel:

上記ではRootWidgetのツリーにカスタムウィジェットを配置しています。これをしないとカスタムウィジェットは表示されません。

Propertyクラスで色情報を管理する

Propertyクラスはデータを管理し、Observer設計によりデータの更新があった場合に通知し、イベントをディスパッチ(イベントの発生)します。PropertyクラスはStringPropertyNumericPropertyなど型ごとに用意されているので、データの型で使い分けます

このコードではカラーリストを扱うのでlist型のListPropertyを使います。

bg_color = ListProperty([1, 1, 1, 1])

このようにカラー値をPropertyで管理することで、色の変更があった場合に自動的に関連するイベントを呼び出して更新してくれます。

因みにkvコードの場合は以下の様に書くことで既にPropertyで管理されています。

<Property>:<value>
size: 300,50
color: 0,0,0,1
font_size: 40
on_mouse_move()をオーバーライドする

on_mouse_move()はKivyのビルトインイベントでマウスを動かすと呼ばれるイベントです。

on_mouse_move(xymodifiers)

    def on_mouse_move(self, window, pos):
        x, y = self.to_widget(*pos)
        if self.collide_point(x, y):  # マウスがLabelの範囲内の場合
            self.bg_color = [0.2, 0.6, 1, 1]  # 青
        else:
            self.bg_color = [1, 1, 1, 1]  # 白

ここでは3つの処理をしています。

  1. 引数でWindowのオブジェクトとウィンドウ内のマウスポインタの位置を受け取る。
  2. Windowウィジェットの座標をWidgetの座標に変換。
  3. マウスポインタの位置がLabelの範囲なら背景色を変更する。

引数で位置を受け取る

def on_mouse_move(self, window, pos):

引数のwindowWindowオブジェクト受け取り、引数のposはマウスポインタのウィンドウ内の座標(タプル)を受け取ります。

座標系を変換

Kivyでは座標系という概念があり位置やサイズを決定するための考え方になります。座標系は、ウィンドウ座標、親座標、ローカル座標またはウィジェット座標があります。

  1. ウィンドウ座標Windowウィジェットが持つ座標。
  2. 親座標:親(通常はレイアウト)が持つ座標。
  3. ローカル座標/ウィジェット座標:Widgetが持つ座標。ローカル座標は現在のウィジェット。

これらは、位置を取得するとたいていの場合、同じ位置になりますが、内部的にはどのウィジェットが持っている位置情報なのかを分けています。

このコードでは、引数でWindowオブジェクトの位置情報を受け取っています。受け取った位置情報はウィンドウ座標です。そして、このコードはLabelの範囲内の位置が知りたいのです。Labelはウィジェットなのでウィジェット座標になります。Window座標とウィジェット座標の間で位置に差が生じるのを防ぐために変換をします。

to_widget()はウィンドウ座標からローカル座標に変換するメソッドです。

to_widget(x, y, relative=False)

x, y = self.to_widget(*pos)

引数*posは、タプルのposを個別の引数xyに分解して渡します。つまり、ウィンドウの座標をウィジェットのローカル座標に変換し、それをxyに代入しています。

最終的に、ウィジェット座標でマウスポインタの位置を表すことになり、ウィジェットの範囲内でマウスの位置を検出しています。

背景色を変える

collide_point()でマウスポインタがLabelの範囲内かを判定し、背景色を変更します。

        if self.collide_point(x, y):  # マウスがLabelの範囲内の場合
            self.bg_color = [0.2, 0.6, 1, 1]  # 青
        else:
            self.bg_color = [1, 1, 1, 1]  # 白

collide_point()はマウスポインタ (x, y) がウィジェットの領域内にあるかどうかをチェックするメソッドです。

collide_point(x, y)

if self.collide_point(x, y):  # マウスがLabelの範囲内の場合

マウスポインタがLabelの範囲内の場合は、背景色を青にします

self.bg_color = [0.2, 0.6, 1, 1]  # 青

この処理が実行されると変数bg_colorに入っているPropertyの値が自動で更新され、Labelの背景色が変わります。

self.bg_color = [1, 1, 1, 1]  # 白

マウスポインタがLabelの範囲外の場合は背景色を白にします。

Labelのテキストを変形させる

Kivyでは文字を変形させるようなメソッドはありませんが、canvasを使ってLabelの文字を変形させることができます。もしくはStencilViewウィジェットを使う方法もあります。どちらの方法も少し面倒かもしれません。これらの詳細は別の記事で書くつもりなのでcanvasを使う方法のコードだけ載せておきます。

Kivy Labelの文字を変形させるアプリ

label2.py

from kivy.app import App
from kivy.graphics import Color, Rectangle
from kivy.uix.widget import Widget
from kivy.core.text import Label as CoreLabel
from math import radians, cos, sin

class CurvedText(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.text = "1 2 3 4 5 6 7 8 9 10 11 12"
        self.radius = 300 # 円の大きさ
        self.angle_step = -13.2  # 文字の方向。マイナス値で右から左

        self.bind(pos=self.update_canvas, size=self.update_canvas)
        self.update_canvas()

    def update_canvas(self, *args):
        self.canvas.clear()
        with self.canvas:
            Color(1, 1, 1, 1)
            angle = 55 # 文字の角度
            for char in self.text:
                label = CoreLabel(text=char, font_size=50)
                label.refresh()
                texture = label.texture
                text_width, text_height = texture.size

                x = self.center_x + self.radius * cos(radians(angle)) - text_width / 2
                y = self.center_y + self.radius * sin(radians(angle)) - text_height / 2

                Rectangle(texture=texture, pos=(x, y), size=texture.size)

                angle += self.angle_step

class Label2(App):
    def build(self):
        return CurvedText()

if __name__ == '__main__':
    Label2().run()

Labelウィジェットではなく低レベルのラベルクラスのCoreLabelを使用しています。通常のLabelとは異なり、CoreLabelは表示テキストやフォント、スタイルなどの属性を設定しつつ、テクスチャを生成するために使用されます。1文字ずつテキストの位置を計算し、四角形の中に文字を配置している形になります。

Comment

タイトルとURLをコピーしました