Background:
I am writing a Scene application (a somewhat complex card game) and to do that I need gesture control for. I looked at the Gestures code which can be found on the pythonista forum and github but it seems to be geared toward UI gesture handling and not Scene gesture handling.
I suppose I could stick my Scene module inside a sceneview UI module, that’s an option.
I could try to subclass or adapt the Gestures code to work with the Scene module.
But as an exercise in learning python better and playing around, I took inspiration from the Gestures code and tried to write my own version of gesture handling for use with the Scene module which leverages the touches objects which are available in the Scene module.
The trouble is that I haven’t gotten the hang of debugging inside the pythonista app. For simple things I get it to work okay, but for things which require interaction with the Scene at the same time as debugging, or which require conditional breakpoints. I’m currently having trouble figuring out why, given my state machine working (I think) as expected, why my calculations for touch translation and rotation are not functioning correctly.
I have included the code below for reference (it should be in a working state as posted). And if anyone has suggestions on how to accomplish conditional breakpoints it has fixes or suggestions for the code please chime in!
import scene
import console
import math
from collections import OrderedDict
#from touch_plus import TouchPlus
from colorsys import hsv_to_rgb
from random import random
'''<><><><><><><><><><><><><><><><><><><><><><><><>
touch_plus.py file:
<><><><><><><><><><><><><><><><><><><><><><><><>'''
#import scene
#import console
#from colorsys import hsv_to_rgb
#from random import random
class TouchPlus():
def __init__(self, parent, **kwargs):
if not isinstance(parent, scene.Scene):
#raise TypeError('parent must be a Scene object!')
self.visible = False
self.parentScene = None
else:
self.parentScene = parent
# Extra kwargs handling
if 'position' in kwargs:
self.startPosition = kwargs.get('position')
else:
self.startPosition = scene.Point(0,0)
if 'startTime' in kwargs:
self.startTime = kwargs.get('startTime')
else:
self.startTime = 0
if 'visible' in kwargs:
self.visible = kwargs.get('visible')
else:
self.visible = False
if 'touch_id' in kwargs:
self.touch_id = kwargs.get('touch_id')
else:
self.touch_id = None
# Other
self.A = scene.Action
self.lastPosition = self.position = self.startPosition
self.location = self.position
self.lastTime = self.time = self.startTime
self.totalMovement = self.lastMovement = self.movement = scene.Vector2(0,0)
self.totalVelocity = self.lastVelocity = self.velocity = scene.Vector2(0,0)
self.area = scene.Rect(self.position.x, self.position.y, 1, 1)
self.sprite = None
if self.visible:
self.show()
else:
self.hide()
def duration(self, time=0):
if time > 0:
self.lastTime = self.time
self.time = time
return self.time - self.startTime
def hide(self):
if self.sprite is not None:
self.sprite.remove_from_parent()
del self.sprite
self.sprite = None
self.visble = False
def show(self, parent=None):
if parent is not None:
if self.parentScene is not None and self.sprite is not None:
self.sprite.remove_from_parent()
self.parentScene = parent
if self.parentScene is not None:
self.visible = True
if self.sprite is None:
self.sprite = scene.SpriteNode(
texture='shp:wavering',
position=self.position,
color=hsv_to_rgb(random(), 1, 1))
self.parentScene.add_child(self.sprite)
else:
self.visble = False
self.sprite = None
def update(self, pos, time=0):
self.duration(time)
self.lastPosition = self.position
self.lastMovement = self.movement
self.lastVelocity = self.velocity
self.position = pos
self.location = self.position
self.movement = self.position - self.lastPosition
self.totalMovement = self.position - self.startPosition
wh = self.totalMovement
self.area = scene.Rect(self.startPosition.x,
self.startPosition.y,
wh.x, wh.y)
if self.time == self.lastTime:
self.totalVelocity = self.velocity = scene.Vector2(0,0)
else:
self.velocity = self.movement / (self.time - self.lastTime)
self.totalVelocity = self.totalMovement / (self.time - self.startTime)
if self.sprite is not None:
self.sprite.run_action(self.A.move_to(pos.x, pos.y, 0.01))
'''<><><><><><><><><><><><><><><><><><><><><><><><>
touch_plus.py file END
<><><><><><><><><><><><><><><><><><><><><><><><>'''
'''<><><><><><><><><><><><><><><><><><><><><><><><>
Free Functions BEGIN
<><><><><><><><><><><><><><><><><><><><><><><><>'''
def angle(point0, point1):
diff = point1 - point0
return math.atan2(diff.y, diff.x)
def area(touches):
min_x = min_y = 50000
max_x = max_y = 0
for touch in touches.values():
min_x = min(min_x, touch.location.x)
min_y = min(min_y, touch.location.y)
max_x = max(max_x, touch.location.x)
max_y = max(max_y, touch.location.y)
return scene.Rect(min_x, min_y, (max_x-min_x),(max_y-min_y))
def centroid(touches):
x_cent = y_cent = 0
for touch in touches.values():
x_cent += touch.location.x
y_cent += touch.location.y
if len(touches) > 0:
n = len(touches)
x_cent /= n
y_cent /= n
return scene.Point(x_cent, y_cent)
def direction(point0, point1):
const = Consts()
diff = point1 - point0
if diff.x == 0:
diff.x = 0.0001
gradient = diff.y / diff.x
if diff.x > 0 and diff.y > 0 and gradient > 1:
return const.UP
elif diff.x > 0 and diff.y > 0 and gradient < 1:
return const.RIGHT
elif diff.x > 0 and diff.y < 0 and gradient < 1:
return const.RIGHT
elif diff.x > 0 and diff.y < 0 and gradient > 1:
return const.DOWN
elif diff.x < 0 and diff.y < 0 and gradient > 1:
return const.DOWN
elif diff.x < 0 and diff.y < 0 and gradient < 1:
return const.LEFT
elif diff.x < 0 and diff.y > 0 and gradient < 1:
return const.LEFT
elif diff.x < 0 and diff.y > 0 and gradient > 1:
return const.UP
else:
return None
def rotation(touches0, touches1):
cent0 = centroid(touches0)
cent1 = centroid(touches1)
angles0 = {}
angles1 = {}
for touch in touches0.values():
angles0[touch.touch_id] = angle(cent0, touch.location)
for touch in touches1.values():
angles1[touch.touch_id] = angle(cent1, touch.location)
ang_sum = ang_num = 0
for key in angles0.keys():
if key in angles1:
ang_num += 1
ang_sum += (angles1[key] - angles0[key])
if ang_num == 0:
return 0
else:
return (ang_sum / ang_num)
def scale(touches0, touches1):
area0 = area(touches0)
area1 = area(touches1)
size0 = area0.width * area0.height
size1 = area1.width * area1.height
if size0 == 0 or size1 == 0:
return 0
else:
return (size1 / size0)
def translation(touches0, touches1):
cent0 = centroid(touches0)
cent1 = centroid(touches1)
return (cent1 - cent0)
'''<><><><><><><><><><><><><><><><><><><><><><><><>
Free Functions END
<><><><><><><><><><><><><><><><><><><><><><><><>'''
'''<><><><><><><><><><><><><><><><><><><><><><><><>
timer.py file BEGIN
<><><><><><><><><><><><><><><><><><><><><><><><>'''
class Timer():
def __init__(self):
self.reset()
def tick(self, time, caller=None):
if caller != self.last_called_by:
self.stopwatch = 0
if time > 0:
if self.start_time == 0:
self.last_time = self.start_time = time
dt = time - self.last_time
self.last_time = time
self.stopwatch += dt
def tock(self):
return self.stopwatch
def reset(self):
self.start_time = 0
self.last_time = 0
self.stopwatch = 0
self.last_called_by = None
'''<><><><><><><><><><><><><><><><><><><><><><><><>
timer.py file END
<><><><><><><><><><><><><><><><><><><><><><><><>'''
'''<><><><><><><><><><><><><><><><><><><><><><><><>
gesture_handler.py file BEGIN
<><><><><><><><><><><><><><><><><><><><><><><><>'''
class Consts():
IDLE = 'State: Idle'
ADDING = 'State: Adding'
BEGAN = 'State: Began'
CHANGED = 'State: Changed'
REMOVING = 'State: Removing'
ENDED = 'State: Ended'
CANCELLED = 'State: Cancelled'
HANDLED = 'State: Handled'
NO_GESTURE = 'Gesture: None'
TAP = 'Gesture: Tap'
LONG_PRESS = 'Gesture: Long Press'
LONG_PRESS_N_DRAG = 'Gesture: Long Press N Drag'
SWIPE = 'Gesture: Swipe'
PAN = 'Gesture: Pan'
PINCH = 'Gesture: Pinch'
ROTATION = 'Gesture: Rotation'
SCREEN_EDGE_PAN = 'Gesture: Screen Edge Pan'
LEFT = 'Left'
RIGHT = 'Right'
UP = 'Up'
DOWN = 'Down'
EDGE_RIGHT = 'Edge Right'
EDGE_LEFT = 'Edge Left'
EDGE_TOP = 'Edge Top'
EDGE_BOTTOM = 'Edge Bottom'
LONG_TOUCH_TIME = 0.4 #seconds
ADD_REMOVE_TIME = 0.05 #seconds
TAP_MOVEMENT = 10 #pixels
class State(Consts):
def __init__(self, **kwargs):
#Defaults
self.touches = OrderedDict()
self.state = None
self.time = 0
#Handle KWARGS:
if 'touches' in kwargs:
self.touches = kwargs.get('touches')
if 'state' in kwargs:
self.state = kwargs.get('state')
if 'time' in kwargs:
self.time = kwargs.get('time')
class Data(Consts):
def __init__(self, state_0, state_1):
self.state = state_1.state
self.gesture = None
self.number_of_touches = len(state_1.touches)
self.duration = state_1.time - state_0.time
self.translation = translation(state_0.touches, state_1.touches)
self.area = area(state_1.touches)
self.starting_location = centroid(state_0.touches)
self.location = centroid(state_1.touches)
self.direction = direction(self.starting_location, self.location)
self.scale = scale(state_0.touches, state_1.touches)
self.rotation = rotation(state_0.touches, state_1.touches)
if self.duration != 0:
self.velocity_t = self.translation / self.duration
self.velocity_r = self.rotation / self.duration
else:
self.velocity_t = None
self.velocity_r = None
class Recognizer(Consts):
def __init__(self, action, **kwargs):
self.minimum_press_duration = self.maximum_press_duration = self.minimum_number_of_touches = self.maximum_number_of_touches = self.minimum_movement = self.maximum_movement = self.direction = self.gesture = self.notify_on = None
self.callback = action
if 'minimum_press_duration' in kwargs:
self.minimum_press_duration = kwargs.get('minimum_press_duration')
if 'maximum_press_duration' in kwargs:
self.maximum_press_duration = kwargs.get('maximum_press_duration')
if 'minimum_number_of_touches' in kwargs:
self.minimum_number_of_touches = kwargs.get('minimum_number_of_touches')
if 'maximum_number_of_touches' in kwargs:
self.maximum_number_of_touches = kwargs.get('maximum_number_of_touches')
if 'minimum_movement' in kwargs:
self.minimum_movement = kwargs.get('minimum_movement')
if 'maximum_movement' in kwargs:
self.maximum_movement = kwargs.get('maximum_movement')
if 'direction' in kwargs:
self.direction = kwargs.get('direction')
if 'notify_on' in kwargs:
self.notify_on = kwargs.get('notify_on')
if 'gesture' in kwargs:
self.gesture = kwargs.get('gesture')
def is_recognized(self, data):
recognized = True
attributes = 0
if self.notify_on is not None:
attributes += 1
if data.state == self.IDLE or data.state == self.CANCELLED:
return False
elif (self.notify_on == self.ENDED or self.notify_on == self.BEGAN) and data.state != self.notify_on:
return False
if self.maximum_number_of_touches is not None:
attributes += 1
recognized &= (data.number_of_touches <= self.maximum_number_of_touches)
if self.minimum_number_of_touches is not None:
attributes += 1
recognized &= (data.number_of_touches >= self.minimum_number_of_touches)
if self.maximum_movement is not None:
attributes += 1
recognized &= (abs(data.translation) <= self.maximum_movement)
if self.minimum_movement is not None:
attributes += 1
recognized &= (abs(data.number_of_touches) >= self.minimum_movement)
if self.maximum_press_duration is not None:
attributes += 1
recognized &= (data.duration <= self.maximum_press_duration)
if self.minimum_press_duration is not None:
attributes += 1
recognized &= (data.duration >= self.minimum_press_duration)
if self.direction is not None:
attributes += 1
recognized &= (data.direction == self.direction)
if attributes < 1:
return False
else:
return recognized
def run_action(self, data):
self.callback(data)
class GestureHandler(Consts):
INIT_STATE = 0
LAST_STATE = 1
THIS_STATE = 2
FINAL_STATE = 3
def __init__(self, parent, **kwargs):
self.parentScene = parent
if not isinstance(parent, scene.Scene):
raise TypeError('parent must be a Scene object!')
self.A = scene.Action
if 'visible' in kwargs:
self.visible = kwargs.get('visible')
else:
self.visible = True #False
self.timer = Timer()
self.recognizers = []
self.reset()
#Debugging
if self.visible:
self.setup_messages()
def reset(self):
# <><><><><><><><><><><><><><><><><><><><><><><><>
self.touches = OrderedDict()
self.timer.reset()
t = self.parentScene.t
self.states = [
State(time=t),
State(time=t),
State(time=t),
State(time=t)]
self.gesture_area = None
# <><><><><><><><><><><><><><><><><><><><><><><><>
def add_touch(self, touch, time=0):
# <><><><><><><><><><><><><><><><><><><><><><><><>
#Add a new touch object to the collection
self.touches[touch.touch_id] = TouchPlus(self.parentScene,
position=(touch.location),
color=hsv_to_rgb(random(), 1, 1),
startTime=time,
touch_id = touch.touch_id,
visible = self.visible)
# <><><><><><><><><><><><><><><><><><><><><><><><>
#Handle the gesture state change - The first touch indicates a tap gesture
new_state = None
self.timer.tick(time, 'add_touch')
if (len(self.touches) == 1):
new_state = self.BEGAN
else:
if (self.timer.tock() > self.ADD_REMOVE_TIME):
new_state = self.CANCELLED
else:
new_state = self.ADDING
self.change_state(new_state)
# <><><><><><><><><><><><><><><><><><><><><><><><>
def move_touch(self, touch, time=0):
#Update the touch with the move
if touch.touch_id in self.touches:
self.touches[touch.touch_id].update(touch.location, time)
self.change_state(self.CHANGED)
# <><><><><><><><><><><><><><><><><><><><><><><><
def update_touches(self, touches, time=0):
#return early because touches should only legitimately change on movement, add, or remove
return
updated = False
for touch_key in touches.keys():
if touch_key in self.touches:
updated = True
self.touches[touch_key].update(touches[touch_key].location, time)
if updated:
self.change_state(self.CHANGED)
# <><><><><><><><><><><><><><><><><><><><><><><><>
def remove_touch(self, touch, time=0):
#Handle the gesture state change - The last touch indicates a gesture is complete
#new_state = self.states[self.THIS_STATE].state
new_state=None
self.timer.tick(time,'remove_touch')
if (len(self.touches) == 0):
new_state = self.ENDED
else:
if (self.timer.tock() > self.ADD_REMOVE_TIME):
new_state = self.CANCELLED
else:
new_state = self.REMOVING
#Remove a touch object from the collection
if touch.touch_id in self.touches:
self.touches[touch.touch_id].hide()
del self.touches[touch.touch_id]
self.change_state(new_state)
# <><><><><><><><><><><><><><><><><><><><><><><><>
def change_state(self, new_state=None):
#The main state machine for detecting and handling gestures, this is where all the smarts (should) happen.
if new_state is None:
new_state = self.IDLE
# .........................................
self.states[self.LAST_STATE] = self.states[self.THIS_STATE]
self.states[self.THIS_STATE] = State(touches=self.touches,
state=new_state,
time=self.parentScene.t)
self.msg = [new_state]
clean_on_exit = False
if new_state == self.BEGAN or new_state == self.ADDING:
self.states[self.INIT_STATE] = self.states[self.THIS_STATE]
elif new_state == self.CHANGED:
clean_on_exit = self.check_recognizers(self.states[self.INIT_STATE],self.states[self.THIS_STATE])
elif new_state == self.REMOVING:
if self.states[self.LAST_STATE].state != self.REMOVING:
self.states[self.FINAL_STATE] = self.states[self.THIS_STATE]
elif new_state == self.ENDED:
clean_on_exit = self.check_recognizers(self.states[self.INIT_STATE],self.states[self.FINAL_STATE])
elif new_state == self.CANCELLED:
clean_on_exit = True
self.print_messages(self.msg)
if clean_on_exit:
self.cleanup()
'''
#old state machine:
if new_state == self.BEGAN:
pass
elif new_state == self.CHANGED:
if len(self.touches) > 0:
first_touch = next(iter(self.touches.values()))
if this_gesture == self.TAP and abs(first_touch.totalMovement) <= self.TAP_MOVEMENT and first_touch.duration() > self.LONG_TOUCH_TIME:
self.change_state(self.CHANGED, self.LONG_PRESS)
elif this_gesture == self.LONG_PRESS and abs(first_touch.totalMovement) > self.TAP_MOVEMENT:
self.highlight(first_touch.startPosition, first_touch.position)
self.change_state(self.CHANGED, self.LONG_PRESS_N_DRAG)
elif this_gesture == self.LONG_PRESS_N_DRAG:
self.highlight(first_touch.startPosition, first_touch.position)
elif new_state == self.ENDED:
#Call handlers here!
new_state = self.HANDLED
'''
#<><><><><><><><><><><><><><><><><><><><><><><><>
def check_recognizers(self, state0, state1):
recognized = False
if len(state0.touches) > 0 and len(state1.touches) > 0:
gesture_data = Data(state0, state1)
self.msg.append(gesture_data.state + ' Touches={0}'.format(gesture_data.number_of_touches))
self.msg.append('Rotation={0} Translation={1}'.format(gesture_data.rotation,gesture_data.translation))
self.msg.append('Duration={0:.2f} Scale={1}'.format(gesture_data.duration,gesture_data.scale))
for recog in self.recognizers:
if recog.is_recognized(gesture_data):
gesture_data.gesture = recog.gesture
self.msg.append(recog.gesture)
recog.run_action(gesture_data)
recognized = True
# If the new_state indicates that the gesture was cancelled or handled, reset everything and wait for something meaningful to happen
return recognized
def cleanup(self):
#Cleanup any touch tracking objects on screen, highlights, and call the reset function for all other relevant resetting
for touch in self.touches.values():
touch.hide()
self.remove_highlight()
self.reset()
#<><><><><><><><><><><><><><><><><><><><><><><><>
def register_tap(self, callback, number_of_touches_required = None):
''' Call `callback` when a tap gesture is recognized.
Additional parameters:
* `number_of_touches_required` - Set if more than one finger is required for the gesture to be recognized.
'''
recog = Recognizer(callback, minimum_press_duration=None,
maximum_press_duration=self.LONG_TOUCH_TIME, minimum_number_of_touches=number_of_touches_required, maximum_number_of_touches=number_of_touches_required,
minimum_movement=None,
maximum_movement=self.TAP_MOVEMENT,
direction=None,
gesture=self.TAP,
notify_on=self.ENDED)
self.recognizers.append(recog)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def register_long_press(self, callback, number_of_touches_required = None, minimum_press_duration = None, allowable_movement = None):
''' Call `callback` when a long press gesture is recognized. Note that this is a continuous gesture; you might want to check for `data.state == Gestures.CHANGED` or `ENDED` to get the desired results.
Additional parameters:
* `number_of_touches_required` - Set if more than one finger is required for the gesture to be recognized.
* `minimum_press_duration` - Set to change the default 0.5 second recognition treshold.
* `allowable_movement` - Set to change the default 10 point maximum distance allowed for the gesture to be recognized.
'''
if minimum_press_duration is None:
minimum_press_duration=self.LONG_TOUCH_TIME
if allowable_movement is None:
allowable_movement=self.TAP_MOVEMENT
recog = Recognizer(callback, minimum_press_duration=minimum_press_duration,
maximum_press_duration=None, minimum_number_of_touches=number_of_touches_required, maximum_number_of_touches=number_of_touches_required,
minimum_movement=None,
maximum_movement=allowable_movement,
direction=None,
gesture=self.LONG_PRESS,
notify_on=self.ENDED)
self.recognizers.append(recog)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def register_long_press_n_drag(self, callback, number_of_touches_required = None, minimum_press_duration = None, allowable_movement = None):
''' Call `callback` when a long press gesture is recognized. Note that this is a continuous gesture; you might want to check for `data.state == Gestures.CHANGED` or `ENDED` to get the desired results.
Additional parameters:
* `number_of_touches_required` - Set if more than one finger is required for the gesture to be recognized.
* `minimum_press_duration` - Set to change the default 0.5 second recognition treshold.
* `allowable_movement` - Set to change the default 10 point maximum distance allowed for the gesture to be recognized.
'''
recog = Recognizer(callback, minimum_press_duration=minimum_press_duration,
maximum_press_duration=None, minimum_number_of_touches=number_of_touches_required, maximum_number_of_touches=number_of_touches_required,
minimum_movement=None,
maximum_movement=None,
direction=None,
gesture=self.LONG_PRESS_N_DRAG,
notify_on=self.CHANGED)
#self.recognizers.append(recog)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def register_pan(self, callback, minimum_number_of_touches = None, maximum_number_of_touches = None):
''' Call `callback` when a pan gesture is recognized. This is a continuous gesture.
Additional parameters:
* `minimum_number_of_touches` - Set to control the gesture recognition.
* `maximum_number_of_touches` - Set to control the gesture recognition.
Handler `action` receives the following gesture-specific attributes in the `data` argument:
* `translation` - Translation from the starting point of the gesture as a `ui.Point` with `x` and `y` attributes.
* `velocity` - Current velocity of the pan gesture as points per second (a `ui.Point` with `x` and `y` attributes).
'''
recog = Recognizer(callback, minimum_press_duration=None,
maximum_press_duration=None, minimum_number_of_touches=minimum_number_of_touches, maximum_number_of_touches=minimum_number_of_touches,
minimum_movement=None,
maximum_movement=None,
direction=None,
gesture=self.PAN,
notify_on=self.CHANGED)
self.recognizers.append(recog)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def register_screen_edge_pan(self, callback, edges):
''' Call `callback` when a pan gesture starting from the edge is recogized. This is a continuous gesture.
`edges` must be set to one of `Gestures.EDGE_NONE/EDGE_TOP/EDGE_LEFT/EDGE_BOTTOM/EDGE_RIGHT/EDGE_ALL`. If you want to recognize pans from different edges, you have to set up separate recognizers with separate calls to this method.
Handler `action` receives the same gesture-specific attributes in the `data` argument as pan gestures, see `add_pan`.
'''
recog = Recognizer(callback, minimum_press_duration=None,
maximum_press_duration=None, minimum_number_of_touches=None, maximum_number_of_touches=None,
minimum_movement=None,
maximum_movement=None,
direction=None,
gesture=None,
notify_on=self.ENDED)
#self.recognizers.append(recog)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def register_pinch(self, callback):
''' Call `callback` when a pinch gesture is recognized. This is a continuous gesture.
Handler `action` receives the following gesture-specific attributes in the `data` argument:
* `scale` - Relative to the distance of the fingers as opposed to when the touch first started.
* `velocity_t` - Current velocity of the pinch gesture as scale per second.
'''
recog = Recognizer(callback, minimum_press_duration=None,
maximum_press_duration=None, minimum_number_of_touches=None, maximum_number_of_touches=None,
minimum_movement=None,
maximum_movement=None,
direction=None,
gesture=self.PINCH,
notify_on=self.CHANGED)
self.recognizers.append(recog)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def register_rotation(self, callback):
''' Call `callback` when a rotation gesture is recognized. This is a continuous gesture.
Handler `action` receives the following gesture-specific attributes in the `data` argument:
* `rotation` - Rotation in radians, relative to the position of the fingers when the touch first started.
* `velocity_r` - Current velocity of the rotation gesture as radians per second.
'''
recog = Recognizer(callback, minimum_press_duration=None,
maximum_press_duration=None, minimum_number_of_touches=None, maximum_number_of_touches=None,
minimum_movement=None,
maximum_movement=None,
direction=None,
gesture=self.ROTATION,
notify_on=self.CHANGED)
self.recognizers.append(recog)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def register_swipe(self, callback, direction = None, number_of_touches_required = None):
''' Call `callback` when a swipe gesture is recognized
Additional parameters:
* `direction` - Direction of the swipe to be recognized. Either one of `Gestures.RIGHT/LEFT/UP/DOWN`, or a list of multiple directions.
* `number_of_touches_required` - Set if you need to change the minimum number of touches required.
If swipes to multiple directions are to be recognized, the handler does not receive any indication of the direction of the swipe. Add multiple recognizers if you need to differentiate between the directions. '''
recog = Recognizer(callback, minimum_press_duration=None,
maximum_press_duration=self.LONG_TOUCH_TIME, minimum_number_of_touches=number_of_touches_required, maximum_number_of_touches=number_of_touches_required,
minimum_movement=None,
maximum_movement=None,
direction=direction,
gesture=self.SWIPE,
notify_on=self.ENDED)
self.recognizers.append(recog)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def setup_messages(self):
#For testing purposes, replaces console HUD with labels on screen. print using the print_messages() function
w, h = scene.get_screen_size()
self.labels = []
for i in range(8):
d = 15 + (15 * i)
self.labels.append(scene.LabelNode('', font=('Helvetica',12) ,position=(w/2, (h-d))))
for lbl in self.labels:
self.parentScene.add_child(lbl)
#<><><><><><><><><><><><><><><><><><><><><><><><>
def print_messages(self, strings):
#Replacement for Console.hud_alert, to allow for more formatting of the print area
if len(self.labels) > 0:
for lbl in self.labels:
lbl.text = ''
i = 0
for txt in strings:
self.labels[i].text = txt
i += 1
#<><><><><><><><><><><><><><><><><><><><><><><><>
def hide(self):
self.visible = False
# <><><><><><><><><><><><><><><><><><><><><><><><>
def highlight(self, start, stop):
#Show a highlighted area of the gesture (intended for long_press_and_drag) to show the selection area of the gesture
new = False
if self.gesture_area is None:
self.gesture_area = scene.SpriteNode(color='#4db9ff', alpha=0.2)
new = True
self.gesture_area.position=((start + stop) / 2)
delta = stop - start
self.gesture_area.size=(abs(delta.x), abs(delta.y))
if self.visible and new:
self.parentScene.add_child(self.gesture_area)
# <><><><><><><><><><><><><><><><><><><><><><><><>
def remove_highlight(self):
if self.gesture_area is not None:
self.gesture_area.remove_from_parent()
del self.gesture_area
self.gesture_area = None
# <><><><><><><><><><><><><><><><><><><><><><><><>
def show(self):
self.visible = True
# <><><><><><><><><><><><><><><><><><><><><><><><>
'''<><><><><><><><><><><><><><><><><><><><><><><><>
gesture_handler.py file END
<><><><><><><><><><><><><><><><><><><><><><><><>'''
'''<><><><><><><><><><><><><><><><><><><><><><><><>
touch_tracker.py file BEGIN
<><><><><><><><><><><><><><><><><><><><><><><><>'''
class TouchTracker():
def __init__(self, parent, **kwargs):
self.parentScene = parent
if not isinstance(parent, scene.Scene):
raise TypeError('parent must be a Scene object!')
self.A = scene.Action
if 'visible' in kwargs:
self.visible = kwargs.get('visible')
else:
self.visible = True #False
self.touches = {}
self.touch_keys = []
def add_touch(self, touch, time=0):
self.touches[touch.touch_id] = TouchPlus(self.parentScene,
position=(touch.location),
color=hsv_to_rgb(random(), 1, 1),
startTime=time,
touch_id = touch.touch_id,
visible = self.visible)
self.touch_keys.append(touch.touch_id)
console.hud_alert('{0} touches'.format(len(self.touches), duration=0.25))
def move_touch(self, touch, time=0):
if touch.touch_id in self.touches:
self.touches[touch.touch_id].update(touch.location, time)
def update_touches(self, touches, time=0):
for touch_key in touches.keys():
if touch_key in self.touches:
self.touches[touch_key].update(touches[touch_key].location, time)
def remove_touch(self, touch, time=0):
if touch.touch_id in self.touches:
self.touches[touch.touch_id].hide()
del self.touches[touch.touch_id]
self.touch_keys.remove(touch.touch_id)
'''<><><><><><><><><><><><><><><><><><><><><><><><>
touch_tracker.py file END
<><><><><><><><><><><><><><><><><><><><><><><><>'''
'''<><><><><><><><><><><><><><><><><><><><><><><><>
test code:
<><><><><><><><><><><><><><><><><><><><><><><><>'''
# Variation of the 'Basic Scene' template that shows every
# touch in a different (random) color that stays the same
# for the duration of the touch.
#import scene
#from gesture_handler import GestureHandler
#from touch_tracker import TouchTracker
#<><><><><><><><><><><><><><><><><><><><><><><><><><>
#This example is the update - object based Scene
class ExampleScene (scene.Scene):
def setup(self):
self.background_color = '#000000'
self.gesture = GestureHandler(self)
#self.gesture = TouchTracker(self)
def update(self):
self.gesture.update_touches(self.touches, self.t)
def touch_began(self, touch):
self.gesture.add_touch(touch, self.t)
def touch_moved(self, touch):
self.gesture.move_touch(touch, self.t)
def touch_ended(self, touch):
self.gesture.remove_touch(touch, self.t)
scene.run(ExampleScene(), show_fps=True)
#<><><><><><><><><><><><><><><><><><><><><><><><><><>