LoginSignup
0
0

More than 1 year has passed since last update.

〈Kivy〉シングルタップ・ダブルタップで別の動作をするボタンを作る

Posted at

目標

こんなふうに, シングルタップとダブルタップで別の動作をするボタンを作成する.
具体的には, シングルタップで「+1」, ダブルタップで「数字を 0 に戻す」動作を行うボタンを実装する.

サンプルコード(ダブルタップ実装前)

左のボタンを押すと表示された数値に +1, 右のボタンを押すと表示された数値に +2 するシンプルなボタン.
これを修正してダブルタップ処理を追加する.
以下にコードを記載する.

1. double_tap_sample.py

プログラムを動かすメインファイル.

from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder

from layout import DoubleTapSample

Window.size = (500, 500)
Builder.load_file('double_tap_sample.kv')


class DoubleTapSampleApp(App):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def build(self):
        return DoubleTapSample()

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

2. double_tap_sample.kv

Label に数字を表示, PlusOneButton と PlusTwoButton の on_press: で, それぞれ root.plus_one(), root.plus_two() を呼び出している.

#:kivy 1.2.0
#:import PlueOneButton layout.PlusOneButton
#:import PlueTwoButton layout.PlusTwoButton
#:import DoubleTapSample layout.DoubleTapSample

<DoubleTapSample>:
    id: double_tap_samle
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            size_hint: (1., 0.5)
            Label:
                text: str(root.number)
                font_size: 100
        BoxLayout:
            size_hint: (1., 0.5)
            PlusOneButton:
                text: '+1'
                font_size: 100
                on_press: root.plus_one()
            PlusTwoButton:
                text: '+2'
                font_size: 100
                on_press: root.plus_two()

3. layout.py

各ボタンから呼び出された処理の具体的な内容を記載する python ファイル.
PlusOneButton, PlusTwoButton は Kivy に最初からあるボタンクラスを継承している.

from kivy.uix.button import Button
from kivy.properties import NumericProperty
from kivy.uix.screenmanager import Screen


class DoubleTapSample(Screen):
    number = NumericProperty(0)

    def plus_one(self):
        self.number += 1

    def plus_two(self):
        self.number += 2

class PlusOneButton(Button):
    pass 

class PlusTwoButton(Button):
    pass

サンプルコード(実装後)

改良後のサンプルコードがこちら.
左のボタンをシングルタップすると表示された数字に+1, ダブルタップで数字を 0 に戻せている.

1. double_tap_sample.py

前回と変化がないので省略

2. double_tap_sample.kv

#:kivy 1.2.0
#:import DoubleClickButton layout.DoubleClickButton
#:import PlusOneButton layout.PlusOneButton
#:import PlueTwoButton layout.PlusTwoButton
#:import DoubleTapSample layout.DoubleTapSample

<DoubleTapSample>:
    id: double_tap_sample
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            size_hint: (1., 0.5)
            Label:
                text: str(root.number)
                font_size: 100
        BoxLayout:
            size_hint: (1., 0.5)
            PlusOneButton:
                id: plus_one_button
                text: '+1'
                font_size: 100
            PlusTwoButton:
                text: '+2'
                font_size: 100
                on_press: root.plus_two()

<DoubleClickButton>:
    background_color:(0, 0, 0, 0)
    background_normal:''
    canvas.before:
        Color: 
            rgba: (0/256, 175/256, 216/256, 1) if self.touched else (88/256,88/256,88/256,1)
        Rectangle: 
            pos: self.pos
            size: self.size

ダブルタップを実装したので on_press: root.plus_one() がなくなっている.
タップ時にボタンの色を変えるために

<DoubleClickButton>:
    background_color:(0, 0, 0, 0)
    background_normal:''
    canvas.before:
        Color: 
            rgba: (0/256, 175/256, 216/256, 1) if self.touched else (88/256,88/256,88/256,1)
        Rectangle: 
            pos: self.pos
            size: self.size

を追加したのが大きな変化である.

3. layout.py

from kivy.app import App
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.properties import NumericProperty, BooleanProperty
from kivy.uix.screenmanager import Screen

class DoubleTapSample(Screen):
    number = NumericProperty(0)

    def plus_one(self, *args):
        self.number += 1

    def plus_two(self):
        self.number += 2

    def reset_num(self):
        self.number = 0

class DoubleClickButton(Button):
    pass

class PlusOneButton(DoubleClickButton):

    scheduled_event = None
    touched = BooleanProperty(False)
    def on_touch_down(self, touch):
        if self.scheduled_event is not None:
            self.scheduled_event.cancel()
            self.scheduled_event = None
        if self.collide_point(*touch.pos):
            self.touched = True
            if touch.is_double_tap:
                App.get_running_app().root.reset_num()
            else:
                double_tap_wait_s = 0.2
                self.scheduled_event = Clock.schedule_once(App.get_running_app().root.plus_one, double_tap_wait_s)
    
    def on_touch_up(self, touch):
        if self.collide_point(*touch.pos):
            self.touched = False

class PlusTwoButton(Button):
    pass

まずは, 数値を 0 に戻すための def reset_num() を追加.
on_touch_down() でタッチを検知し, ダブルタップかどうかを判別している(詳細は後述).
touched = BooleanProperty(False) はタッチ中にボタンの色を変えるために使う. タッチされた時に, on_touch_down() 内で True になり, タッチ終了時に呼ばれる, on_touch_up()False に戻す.

個人的にハマったところ

1. ダブルタップ時にシングルタップの処理が呼ばれないようにする

ダブルタップの判定には, kivyのドキュメンテーションのサンプルコードを参考にダブルタップを実装し, else 文でシングルタップ時の処理を書けば良いように思えるが...

def on_touch_down(self, touch):
    if self.collide_point(*touch.pos):
        if touch.is_double_tap:
            App.get_running_app().root.reset_num()
           else:
            App.get_running_app().root.plus_one()

上のコードを実行するとシングルタップ処理の後でダブルタップ処理が呼ばれてしまう
参考にしたのはPython3 & Kivy: React only to double tap "not" single tapのコード

def on_touch_down(self, touch):
    if self.scheduled_event is not None:
        self.scheduled_event.cancel()
        self.scheduled_event = None
        if self.collide_point(*touch.pos):
            self.touched = True
            if touch.is_double_tap:
                App.get_running_app().root.reset_num()
            else:
                double_tap_wait_s = 0.2
                self.scheduled_event = Clock.schedule_once(App.get_running_app().root.plus_one, double_tap_wait_s)

kivy の Clock 機能を使って, 0.2 秒後にシングルタップ時の処理が行われるように登録を行う.
もし, 0.2 秒以内にもう一度タップされたら登録されたイベントは削除, ダブルタップ処理が行われる.
また, if self.collide_point(*touch.pos):の判定がないと, 画面のどこを押してもシングルタップ or ダブルタップ処理が行われてしまう.

2. 別クラスに記載された処理を呼び出す.

初めは, on_press: root.plus_one()で root(DoubleTapSample クラス)に書かれた plus_one 処理を呼び出していた. 同じ処理を .py ファイルで書くためにApp.get_running_app().root.reset_num()を使っている.
def plus_one(self, *args):は *args がないと clock takes 1 positional argument but 2 were given というエラーになる.

まとめ

ダブルタップとシングルタップで処理を変えたいというときの実装方法がわかった.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0