import time from threading import Thread from ..tracker import GestureTracker from ..geometry import Positionable from utils import PointGesture class TapTracker(GestureTracker): __gesture_types__ = ['tap', 'single_tap', 'double_tap'] __configurable__ = ['tap_distance', 'tap_time', 'double_tap_time', 'double_tap_distance', 'update_rate'] def __init__(self, window=None): super(TapTracker, self).__init__(window) # Map of TUIO session id to tuple (timestamp, position) of point down self.reg = {} # Maximum radius in which a touch point can move in order to be a tap # event in pixels self.tap_distance = 20 # Maximum time between 'down' and 'up' of a tap event in seconds self.tap_time = .2 # Maximum time in seconds and distance in pixels between two taps to # count as double tap self.double_tap_time = .3 self.double_tap_distance = 30 # Times per second to detect single taps self.update_rate = 30 self.reset_last_tap() self.single_tap_thread = Thread(target=self.detect_single_tap) self.single_tap_thread.daemon = True self.single_tap_thread.start() def detect_single_tap(self): """ Iteration function for single-tap detection thread. """ while True: time_diff = time.time() - self.last_tap_time if self.last_tap and time_diff > self.double_tap_time: # Last tap is too long ago to be a double tap, so trigger a # single tap self.trigger(SingleTapGesture(self.last_tap)) self.reset_last_tap() time.sleep(1. / self.update_rate) def reset_last_tap(self): self.last_tap_time = 0 self.last_tap = None def on_point_down(self, point): x, y = point.get_position() self.reg[point.sid] = time.time(), Positionable(x, y) def on_point_move(self, point): if point.sid not in self.reg: return # If a stationary point moves beyond a threshold, delete it so that the # 'up' event will not trigger a 'tap' t, initial_position = self.reg[point.sid] if point.distance_to(initial_position) > self.tap_distance: del self.reg[point.sid] def on_point_up(self, point): # Assert that the point has not been deleted by a 'move' event yet if point.sid not in self.reg: return down_time = self.reg[point.sid][0] del self.reg[point.sid] # Only trigger a tap event if the 'up' is triggered within a certain # time afer the 'down' current_time = time.time() if current_time - down_time > self.tap_time: return tap = TapGesture(point) self.trigger(tap) # Trigger double tap if the threshold has not not expired yet if self.last_tap: if self.last_tap.distance_to(tap) <= self.double_tap_distance: # Close enough to be a double tap self.trigger(DoubleTapGesture(self.last_tap)) self.reset_last_tap() return # Generate a seperate single tap gesture for the last tap, # because the lat tap variable is overwritten now self.trigger(SingleTapGesture(self.last_tap)) self.last_tap_time = current_time self.last_tap = tap class TapGesture(PointGesture): """ A tap gesture is triggered """ __type__ = 'tap' class SingleTapGesture(TapGesture): """ A single tap gesture is triggered after a regular tap gesture, if no double tap is triggered for that gesture. """ __type__ = 'single_tap' class DoubleTapGesture(TapGesture): """ A double tap gesture is triggered if two sequential taps are triggered within a certain time and distance of eachother. """ __type__ = 'double_tap'