Kivyでラベルや画像などのウィジェットをドラッグして移動するサンプルコードです。このサンプルコードは主にkv言語で書かれています。ドラッグの方法はいくつかありますが、ここで紹介するものは、Kivyのmixin機能であるBehaviorモジュールのDragBehaviorクラスと、ScatterLayoutを使ってドラッグの動作を実現しています。
ウィジェットをドラッグする方法
KivyのBehaviorモジュールには、ボタンやラベルなどの基本的なウィジェットに何らかの動作を組み合わせるmixinクラスがいくつか用意されています。その中でもDragBehaviorクラスはウィジェットをドラッグする機能を提供します。
Buttonをドラッグする方法
今回紹介するサンプルコードはKivy公式サイトで紹介されているサンプルをkv言語で書き直したものになります。
example_behavior1.py
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
class RootWidget(FloatLayout):
pass
class ExampleBehavior1(App):
def build(self):
return RootWidget()
if __name__ == "__main__":
ExampleBehavior1().run()
examplebehavior1.kv
<DraggableButton@DragBehavior+Button>
font_size: 30
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 100000000
drag_distance: 0
<RootWidget>:
DraggableButton:
text: 'ドラッグできるよ'
size_hint: 0.2, 0.2
center_x: 500
center_y: 500
コード解説
kv言語の動的クラスの多重継承を使用して記述しています。動的クラスの書き方については下記の記事をご覧ください。
DragBehaviorクラスを使ってドラッグさせる場合は下記の工程を記述します。
- ドラッグ可能領域を設定する。
- ドラッグのタイムアウト時間を設定する。
- ドラッグの移動距離を設定する。
from kivy.uix.floatlayout import FloatLayout
class RootWidget(FloatLayout):
pass
今回のような場合レイアウトはBoxLayoutよりはFloatLayoutの方が適しています。FloatLayoutはウィジェットを自由な位置に配置することができますが、BoxLayoutの場合は配置位置を設定するposやpos_hintが使えないので使い勝手が悪くなってしまいます。FloatLayoutに似たレイアウトでScatterLayoutもあります。
<DraggableButton@DragBehavior+Button>
これはDragBehaviorクラスとButtonクラスを多重継承しています。DragBehaviorを先に書く必要があります。
drag_rectangle: self.x, self.y, self.width, self.height
drag_rectangleでドラッグ可能領域を設定しています。上記のように書くと画面サイズの全ての領域がドラッグ可能な範囲になります。
drag_timeout: 100000000
drag_timeoutはドラッグのタイムアウト時間を設定します。ここに大きな値を設定する事でドラッグがキャンセルされないようにしています。0.1秒は100、1秒は1000になります。
drag_distance: 0
drag_distanceではドラッグを開始するのに必要な最小の移動距離を設定します。0にすることでウィジェットがすぐにドラッグ可能な状態になります。
ここまでの設定でもウィジェットをドラッグすることができます。しかし、ドラッグ可能領域を画面サイズにしているので、画面内のどの場所でもクリック→ドラッグできてしまいます。つまり、ウィジェットの周りの領域だけクリックできる方が好ましいということです。例えばウィジェットが1つなら問題ないように思えますが、複数のウィジェットがあった場合、クリックする領域が重なってしまい同時にウィジェットがドラッグされてしまいます。
これを解消するには、size_hintでウィジェットサイズの調整を行います。ウィジェットのテクスチャ領域(描画領域)を取得してドラッグ可能領域に設定しようと思ったのですがこれは上手くいかなかったのでこの方法を提示しています。もっと良い方法があるかもしれません。
size_hintの詳細は下記の記事をご覧ください。
DraggableButton:
動的クラスで定義したカスタムウィジェットをツリーに配置しています。
size_hint: 0.2, 0.2
ウィジェットのサイズを設定しています。
center_x: 500
center_y: 500
初期の配置位置を設定しています。pos、pos_hintや他の配置位置を設定するプロパティを使用して配置場所を決まます。
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
上記のように[center,center]で固定した場合ドラッグできなくなってしまうので注意してください。
画像をドラッグする
DragBehaviorクラスは画像をドラッグすることもできます。方法は前章と同じです。このサンプルでは複数の画像を配置し、ドラッグ領域が重ならないようにsize_hintで調整しています。
examplebehavior2.kv
pyファイルは最初のコードと同じです。
<DraggableIcon@DragBehavior+Image>
source: 'icon.png'
size: self.texture_size
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 100000000
drag_distance: 0
<RootWidget>:
DraggableIcon:
pos: 600, 300
size_hint: 0.1, 0.1
DraggableIcon:
pos: 500, 600
size_hint: 0.1, 0.1
DraggableIcon:
pos: 300, 400
size_hint: 0.1, 0.1
コード解説
<DraggableIcon@DragBehavior+Image>
ここではDragBehaviorクラスとImageウィジェットを多重継承しています。DragBehaviorクラスを先に書く必要があります。
source: 'icon.png'
画像を表示させるにはsourceプロパティに画像のパスを指定します。この場合はスクリプトファイルが置いてある場所に画像ファイルが置いてあります。モバイルアプリを作成している場合は絶対パスで指定することをお勧めします。
size: self.texture_size
画像のサイズを指定します。このように書くと元の画像サイズになります。
pos: 600, 300
posプロパティで最初に表示される画像の位置を指定しています。X(横方向)、y(縦方向)の位置の座標を指定します。
size_hint: 0.1, 0.1
size_hintで画像の大きさを親ウィジェットに対する割合で指定します。ここで画像のドラッグ領域のサイズを調整しています。
Labelをドラッグする方法➀
LabelをドラッグするにはLabelを継承するだけです。
<DraggableButton@DragBehavior+Label>
Labelをドラッグする方法➁
もう一つLabelをドラッグする方法で縦方向または横方向の移動を制御する方法を紹介します。このサンプルはStack Overflowのサイトにサンプルが紹介されていたのをそのまま使わせて頂いています。
このサンプルコードはScatterLayoutの機能を使用してドラッグしています。使用できるウィジェットに制限はありますが、DragBehaviorクラスを使う必要がなくウィジェットの多重継承を使用しないでドラッグを実装できるので用途に合わせて使い分けると良いと思います。
example_behavior3.py
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
class RootWidget(FloatLayout):
pass
class ExampleBehavior3(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
ExampleBehavior3().run()
examplebehavior3.kv
<RootWidget>
Label:
text: "Labelは移動できるよ"
ScatterLayout:
size_hint: 1.0, 0.2
do_translation_x: False
Label:
text: 'Drag up and down'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
コード解説
from kivy.uix.floatlayout import FloatLayout
class RootWidget(FloatLayout):
FloatLayoutとScatterLayoutは入れ子になっていますが、ScatterLayoutだけということも可能です。この場合、文字列を表示するLabelとドラッグできるLabelの2つがあり、文字列を表示するLabelはドラッグできないようするためにレイアウトの入れ子をしています。
Label:
text: "Labelは移動できるよ"
このLabelはFloatLayoutの直下に配置されているのでドラッグはできないようにしています。
ScatterLayout:
size_hint: 1.0, 0.2
do_translation_x: False
ScatterLayoutは、ウィジェットを任意の位置に配置できるレイアウトです。FloatLayoutと似ていますが、ウィジェットを移動、回転、リサイズするメソッドがあります。
size_hint: 1.0, 0.2
ここでLabelのサイズを指定しています。
do_translation_x: False
do_translation_xは水平方向の移動を許可するかを設定します。do_translation_yは垂直方向の移動を制御します。デフォルトはTrueで移動を許可するになります。今回は水平方向の移動を許可していないので垂直方向にのみドラッグできます。
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
これはLabelに背景色を設定しています。LabelにはButtonの様にbackground_colorがないのでcanvasを使用して色を塗っています。詳細は下記の記事をご覧ください。
Comment