Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
multitouch
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Taddeüs Kroes
multitouch
Commits
1aff9a2e
Commit
1aff9a2e
authored
May 04, 2012
by
Taddeus Kroes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Code cleanup.
parent
9807d347
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
243 additions
and
148 deletions
+243
-148
src/touch.py
src/touch.py
+243
-148
No files found.
src/touch.py
View file @
1aff9a2e
...
@@ -6,9 +6,9 @@ from threading import Thread
...
@@ -6,9 +6,9 @@ from threading import Thread
from
tuio_server
import
TuioServer2D
from
tuio_server
import
TuioServer2D
from
logger
import
Logger
from
logger
import
Logger
from
events
import
BasicEvent
,
DownEvent
,
UpEvent
,
MoveEvent
,
TapEvent
,
\
from
events
import
Event
,
DownEvent
,
UpEvent
,
MoveEvent
,
Tap
,
\
SingleTap
Event
,
DoubleTapEvent
,
FlickEvent
,
RotateEvent
,
PinchEvent
,
\
SingleTap
,
DoubleTap
,
Flick
,
Rotate
,
Pinch
,
\
Pan
Event
Pan
# get screen resolution
# get screen resolution
pygame
.
display
.
init
()
pygame
.
display
.
init
()
...
@@ -18,7 +18,7 @@ pygame.display.quit()
...
@@ -18,7 +18,7 @@ pygame.display.quit()
# Heuristic constants
# Heuristic constants
# TODO: Encapsulate screen resolution in distance heuristics
# TODO: Encapsulate screen resolution in distance heuristics
SUPPORTED_
GESTURE
S
=
(
'down'
,
'up'
,
'move'
,
'tap'
,
'single_tap'
,
'double_tap'
,
SUPPORTED_
EVENT
S
=
(
'down'
,
'up'
,
'move'
,
'tap'
,
'single_tap'
,
'double_tap'
,
'pan'
,
'flick'
,
'rotate'
,
'pinch'
)
'pan'
,
'flick'
,
'rotate'
,
'pinch'
)
DOUBLE_TAP_DISTANCE
=
.
05
DOUBLE_TAP_DISTANCE
=
.
05
FLICK_VELOCITY_TRESHOLD
=
20
FLICK_VELOCITY_TRESHOLD
=
20
...
@@ -26,6 +26,9 @@ TAP_TIMEOUT = .2
...
@@ -26,6 +26,9 @@ TAP_TIMEOUT = .2
MAX_MULTI_DRAG_DISTANCE
=
.
05
MAX_MULTI_DRAG_DISTANCE
=
.
05
STATIONARY_TIME
=
.
01
STATIONARY_TIME
=
.
01
# Detect gestures 60 times per second
GESTURE_UPDATE_RATE
=
60
# Minimum distance for two coordinates to be considered different
# Minimum distance for two coordinates to be considered different
# Theoretically, this should be one pixel because that is the minimal movement
# Theoretically, this should be one pixel because that is the minimal movement
# of a mouse cursor on the screen
# of a mouse cursor on the screen
...
@@ -34,7 +37,8 @@ DIST_THRESHOLD = 1. / max(screen_size)
...
@@ -34,7 +37,8 @@ DIST_THRESHOLD = 1. / max(screen_size)
def
distance
(
a
,
b
):
def
distance
(
a
,
b
):
"""
"""
Calculate the distance between points a and b.
Calculate the Pythagorian distance between points a and b (which are (x, y)
tuples).
"""
"""
xa
,
ya
=
a
xa
,
ya
=
a
xb
,
yb
=
b
xb
,
yb
=
b
...
@@ -43,6 +47,9 @@ def distance(a, b):
...
@@ -43,6 +47,9 @@ def distance(a, b):
def
add
(
a
,
b
):
def
add
(
a
,
b
):
"""
Add a an b, used for some functional programming calls.
"""
return
a
+
b
return
a
+
b
...
@@ -77,39 +84,30 @@ class TouchPoint(object):
...
@@ -77,39 +84,30 @@ class TouchPoint(object):
def
distance_to_prev
(
self
):
def
distance_to_prev
(
self
):
return
self
.
distance_to
(
self
.
px
,
self
.
py
)
return
self
.
distance_to
(
self
.
px
,
self
.
py
)
def
init_gesture_data
(
self
,
cx
,
cy
):
def
set_centroid
(
self
,
cx
,
cy
):
self
.
pinch
=
self
.
old_pinch
=
self
.
distance_to
(
cx
,
cy
)
self
.
pinch
=
self
.
distance_to
(
cx
,
cy
)
self
.
angle
=
self
.
old_angle
=
atan2
(
self
.
y
-
cy
,
self
.
x
-
cx
)
self
.
angle
=
atan2
(
cy
-
self
.
y
,
self
.
x
-
cx
)
def
rotation_around
(
cx
,
cy
):
angle
=
atan2
(
cy
-
self
.
y
,
self
.
x
-
cx
)
prev_angle
=
atan2
(
cy
-
self
.
py
,
self
.
px
-
cx
)
da
=
angle
-
prev_angle
# Assert that angle is in [-pi, pi]
if
da
>
pi
:
da
-=
2
*
pi
elif
da
<
pi
:
da
+=
2
*
pi
return
da
if
self
.
pinch
!=
None
:
def
set_angle
(
self
,
angle
):
self
.
old_angle
=
self
.
angle
self
.
angle
=
angle
def
set_pinch
(
self
,
pinch
):
self
.
old_pinch
=
self
.
pinch
self
.
old_pinch
=
self
.
pinch
self
.
old_angle
=
self
.
angle
self
.
pinch
=
pinch
self
.
pinch
=
pinch
self
.
angle
=
angle
else
:
self
.
old_pinch
=
self
.
pinch
=
pinch
self
.
old_angle
=
self
.
angle
=
angle
def
angle_diff
(
self
):
def
angle_diff
(
self
):
return
self
.
angle
-
self
.
old_angle
return
self
.
angle
-
self
.
old_angle
def
pinch_diff
(
self
):
return
self
.
pinch
-
self
.
old_pinch
def
dx
(
self
):
def
dx
(
self
):
return
int
(
self
.
x
-
self
.
px
)
return
self
.
x
-
self
.
px
def
dy
(
self
):
def
dy
(
self
):
return
int
(
self
.
y
-
self
.
py
)
return
self
.
y
-
self
.
py
def
down_time
(
self
):
def
down_time
(
self
):
return
time
.
time
()
-
self
.
start_time
return
time
.
time
()
-
self
.
start_time
...
@@ -118,9 +116,6 @@ class TouchPoint(object):
...
@@ -118,9 +116,6 @@ class TouchPoint(object):
return
self
.
distance_to_prev
()
<
TAP_TIME
\
return
self
.
distance_to_prev
()
<
TAP_TIME
\
and
self
.
distance_to
(
self
.
start_x
,
self
.
start_y
)
<
TAP_DISTANCE
and
self
.
distance_to
(
self
.
start_x
,
self
.
start_y
)
<
TAP_DISTANCE
def
movement
(
self
):
return
self
.
x
-
self
.
px
,
self
.
y
-
self
.
py
def
is_stationary
(
self
):
def
is_stationary
(
self
):
return
self
.
distance_to_prev
()
<
DIST_THRESHOLD
return
self
.
distance_to_prev
()
<
DIST_THRESHOLD
...
@@ -129,9 +124,11 @@ class MultiTouchListener(Logger):
...
@@ -129,9 +124,11 @@ class MultiTouchListener(Logger):
def
__init__
(
self
,
verbose
=
0
,
tuio_verbose
=
0
,
**
kwargs
):
def
__init__
(
self
,
verbose
=
0
,
tuio_verbose
=
0
,
**
kwargs
):
super
(
MultiTouchListener
,
self
).
__init__
(
**
kwargs
)
super
(
MultiTouchListener
,
self
).
__init__
(
**
kwargs
)
self
.
verbose
=
verbose
self
.
verbose
=
verbose
self
.
tuio_verbose
=
tuio_verbos
e
self
.
last_tap
=
Non
e
self
.
last_tap_time
=
0
self
.
last_tap_time
=
0
self
.
handlers
=
{}
self
.
handlers
=
{}
self
.
thread
=
None
self
.
points_changed
=
False
# Session id's pointing to point coordinates
# Session id's pointing to point coordinates
self
.
points
=
[]
self
.
points
=
[]
...
@@ -139,22 +136,74 @@ class MultiTouchListener(Logger):
...
@@ -139,22 +136,74 @@ class MultiTouchListener(Logger):
# Put centroid outside screen to prevent misinterpretation
# Put centroid outside screen to prevent misinterpretation
self
.
centroid
=
(
-
1.
,
-
1.
)
self
.
centroid
=
(
-
1.
,
-
1.
)
def
update_centroid
(
self
,
moving
=
None
):
self
.
server
=
TuioServer2D
(
self
,
verbose
=
tuio_verbose
)
self
.
old_centroid
=
self
.
centroid
if
not
len
(
self
.
points
):
def
point_down
(
self
,
sid
,
x
,
y
):
self
.
centroid
=
(
-
1.
,
-
1.
)
"""
return
Called by TUIO listener when a new touch point is created, triggers a
DownEvent.
"""
if
self
.
find_point
(
sid
):
raise
ValueError
(
'Point with session id "%d" already exists.'
%
sid
)
#use = filter(TouchPoint.is_stationary, self.points)
p
=
TouchPoint
(
sid
,
x
,
y
)
use
=
filter
(
lambda
p
:
p
!=
moving
,
self
.
points
)
self
.
points
.
append
(
p
)
self
.
trigger
(
DownEvent
(
p
))
self
.
points_changed
=
True
if
not
use
:
def
point_up
(
self
,
sid
):
use
=
self
.
points
"""
Called by TUIO listener when a touch point is removed, triggers an
UpEvent. Also, simple/double tap detection is located here instead of
in the gesture thread (for responsiveness reasons).
"""
i
,
p
=
self
.
find_point
(
sid
,
index
=
True
)
l
=
len
(
use
)
if
not
p
:
cx
,
cy
=
zip
(
*
[(
p
.
x
,
p
.
y
)
for
p
in
use
])
raise
KeyError
(
'No point with session id "%d".'
%
sid
)
self
.
centroid
=
(
reduce
(
add
,
cx
,
0
)
/
l
,
reduce
(
add
,
cy
,
0
)
/
l
)
del
self
.
points
[
i
]
self
.
trigger
(
UpEvent
(
p
))
if
p
.
is_tap
():
# Always trigger a regular tap event, also in case of double tap
# (use the 'single_tap' event to keep single/double apart from
# eachother)
self
.
trigger
(
Tap
(
p
.
x
,
p
.
y
))
# Detect double tap by comparing time and distance to last tap
# event
t
=
time
.
time
()
if
t
-
self
.
last_tap_time
<
TAP_TIMEOUT
\
and
p
.
distance_to
(
*
self
.
last_tap
.
xy
)
<
DOUBLE_TAP_DISTANCE
:
self
.
trigger
(
DoubleTap
(
p
.
x
,
p
.
y
))
self
.
last_tap
=
None
self
.
last_tap_time
=
0
else
:
self
.
last_tap
=
p
self
.
last_tap_time
=
t
# TODO: Detect flick
#elif p.is_flick():
# self.trigger(Flick(p.x, p.y))
self
.
points_changed
=
True
def
point_move
(
self
,
sid
,
x
,
y
):
"""
Called by TUIO listener when a touch point moves, triggers a MoveEvent.
The move event is only used if the movement distance is greater that a
preset constant, so that negligible movement is ignored. This prevents
unnecessary gesture detection.
"""
p
=
self
.
find_point
(
sid
)
if
p
.
distance_to
(
x
,
y
)
>
DIST_THRESHOLD
:
p
.
update
(
x
,
y
)
self
.
trigger
(
MoveEvent
(
p
))
self
.
points_changed
=
True
def
detect_rotation_and_pinch
(
self
):
def
detect_rotation_and_pinch
(
self
):
"""
"""
...
@@ -164,32 +213,35 @@ class MultiTouchListener(Logger):
...
@@ -164,32 +213,35 @@ class MultiTouchListener(Logger):
"""
"""
l
=
len
(
self
.
points
)
l
=
len
(
self
.
points
)
if
'pinch'
not
in
self
.
handlers
or
l
<
2
:
if
l
<
2
or
(
'pinch'
not
in
self
.
handlers
\
and
'rotate'
not
in
self
.
handlers
):
return
return
rotation
=
pinch
=
0
cx
,
cy
=
self
.
centroid
cx
,
cy
=
self
.
centroid
#rotation = pinch = 0
for
p
in
self
.
points
:
#for p in self.points:
p
.
set_angle
(
atan2
(
p
.
y
-
cy
,
p
.
x
-
cx
))
# da = p.angle_diff()
da
=
p
.
angle_diff
()
# Assert that angle is in [-pi, pi]
#
# Assert that angle is in [-pi, pi]
if
da
>
pi
:
#
if da > pi:
da
-=
2
*
pi
#
da -= 2 * pi
elif
da
<
pi
:
#
elif da < pi:
da
+=
2
*
pi
#
da += 2 * pi
rotation
+=
da
# rotation += da
# pinch += p.pinch_diff()
p
.
set_pinch
(
p
.
distance_to
(
cx
,
cy
))
angles
,
pinches
=
zip
(
*
[(
p
.
angle_diff
(),
p
.
pinch_diff
())
pinch
+=
p
.
pinch_diff
()
for
p
in
self
.
points
])
rotation
=
reduce
(
add
,
angles
)
pinch
=
reduce
(
add
,
pinches
)
if
rotation
:
if
rotation
:
self
.
trigger
(
Rotate
Event
(
cx
,
cy
,
rotation
/
l
,
l
))
self
.
trigger
(
Rotate
(
cx
,
cy
,
rotation
/
l
,
l
))
if
pinch
:
if
pinch
:
self
.
trigger
(
Pinch
Event
(
cx
,
cy
,
pinch
/
l
,
l
))
self
.
trigger
(
Pinch
(
cx
,
cy
,
pinch
/
l
*
2
,
l
))
def
detect_pan
(
self
):
def
detect_pan
(
self
):
"""
"""
...
@@ -197,13 +249,19 @@ class MultiTouchListener(Logger):
...
@@ -197,13 +249,19 @@ class MultiTouchListener(Logger):
fingers moving close-ish together in the same direction.
fingers moving close-ish together in the same direction.
"""
"""
l
=
len
(
self
.
points
)
l
=
len
(
self
.
points
)
if
not
l
:
return
False
m
=
MAX_MULTI_DRAG_DISTANCE
m
=
MAX_MULTI_DRAG_DISTANCE
clustered
=
l
==
1
or
all
([
p
.
distance_to
(
*
self
.
centroid
)
<=
m
\
clustered
=
l
==
1
or
all
([
p
.
distance_to
(
*
self
.
centroid
)
<=
m
\
for
p
in
self
.
points
])
for
p
in
self
.
points
])
directions
=
[(
cmp
(
p
.
dx
(),
0
),
cmp
(
p
.
dy
(),
0
))
\
directions
=
[(
cmp
(
p
.
dx
(),
0
),
cmp
(
p
.
dy
(),
0
))
\
for
p
in
self
.
points
]
for
p
in
self
.
points
]
if
any
(
map
(
all
,
zip
(
*
directions
)))
and
clustered
:
if
not
clustered
or
not
any
(
map
(
all
,
zip
(
*
directions
))):
return
False
if
l
==
1
:
if
l
==
1
:
p
=
self
.
points
[
0
]
p
=
self
.
points
[
0
]
cx
,
cy
,
dx
,
dy
=
p
.
x
,
p
.
y
,
p
.
dx
(),
p
.
dy
()
cx
,
cy
,
dx
,
dy
=
p
.
x
,
p
.
y
,
p
.
dx
(),
p
.
dy
()
...
@@ -212,7 +270,9 @@ class MultiTouchListener(Logger):
...
@@ -212,7 +270,9 @@ class MultiTouchListener(Logger):
old_cx
,
old_cy
=
self
.
old_centroid
old_cx
,
old_cy
=
self
.
old_centroid
dx
,
dy
=
cx
-
old_cx
,
cy
-
old_cy
dx
,
dy
=
cx
-
old_cx
,
cy
-
old_cy
self
.
trigger
(
PanEvent
(
cx
,
cy
,
dx
,
dy
,
l
))
self
.
trigger
(
Pan
(
cx
,
cy
,
dx
,
dy
,
l
))
return
True
def
find_point
(
self
,
sid
,
index
=
False
):
def
find_point
(
self
,
sid
,
index
=
False
):
for
i
,
p
in
enumerate
(
self
.
points
):
for
i
,
p
in
enumerate
(
self
.
points
):
...
@@ -222,123 +282,158 @@ class MultiTouchListener(Logger):
...
@@ -222,123 +282,158 @@ class MultiTouchListener(Logger):
if
index
:
if
index
:
return
-
1
,
None
return
-
1
,
None
def
point_down
(
self
,
sid
,
x
,
y
):
def
detect_pinch
(
self
,
moved
):
if
self
.
find_point
(
sid
):
cx
,
cy
=
self
.
centroid
raise
ValueError
(
'Point with session id "%d" already exists.'
%
sid
)
dist
=
moved
.
distance_to
(
cx
,
cy
)
old_dist
=
distance
((
moved
.
px
,
moved
.
py
),
self
.
centroid
)
p
=
TouchPoint
(
sid
,
x
,
y
)
if
abs
(
dist
-
old_dist
)
>
DIST_THRESHOLD
:
self
.
points
.
append
(
p
)
self
.
trigger
(
Pinch
(
cx
,
cy
,
dist
/
old_dist
,
self
.
update_centroid
()
len
(
self
.
points
)))
self
.
trigger
(
DownEvent
(
p
))
def
point_up
(
self
,
sid
):
def
detect_single_tap
(
self
):
i
,
p
=
self
.
find_point
(
sid
,
index
=
True
)
"""
Check if a single tap event should be triggered by checking is the last
tap.
"""
if
self
.
last_tap
and
time
.
time
()
-
self
.
last_tap_time
>=
TAP_TIMEOUT
:
self
.
trigger
(
SingleTap
(
*
self
.
last_tap
.
xy
))
self
.
last_tap
=
None
self
.
last_tap_time
=
0
if
not
p
:
def
update_centroid
(
self
):
raise
KeyError
(
'No point with session id "%d".'
%
sid
)
"""
Calculate the centroid of all current touch points.
"""
self
.
old_centroid
=
self
.
centroid
l
=
len
(
self
.
points
)
del
self
.
points
[
i
]
# If there are no touch points, move the entroid to outside the screen
self
.
update_centroid
()
if
not
l
:
self
.
trigger
(
UpEvent
(
p
))
self
.
centroid
=
(
-
1.
,
-
1.
)
return
if
p
.
is_tap
():
cx
,
cy
=
zip
(
*
[(
p
.
x
,
p
.
y
)
for
p
in
self
.
points
])
# Always trigger a regular tap event, also in case of double tap
self
.
centroid
=
(
reduce
(
add
,
cx
,
0
)
/
l
,
reduce
(
add
,
cy
,
0
)
/
l
)
# (use the 'single_tap' event to keep single/double apart from
# eachother)
self
.
trigger
(
TapEvent
(
p
.
x
,
p
.
y
))
# Detect double tap by comparing time and distance from last tap
# Update angle and pinch of all touch points
# event
for
p
in
self
.
points
:
t
=
time
.
time
(
)
p
.
set_centroid
(
*
self
.
centroid
)
if
t
-
self
.
last_tap_time
<
TAP_TIMEOUT
\
def
centroid_movement
(
self
):
and
p
.
distance_to
(
*
self
.
last_tap
.
xy
)
<
DOUBLE_TAP_DISTANCE
:
cx
,
cy
=
self
.
centroid
self
.
trigger
(
DoubleTapEvent
(
p
.
x
,
p
.
y
))
ocx
,
ocy
=
self
.
old_centroid
else
:
self
.
last_tap
=
p
self
.
last_tap_time
=
t
# TODO: Detect flick
return
cx
-
ocx
,
cy
-
ocy
#elif p.is_flick():
# self.trigger(FlickEvent(p.x, p.y))
def
point_move
(
self
,
sid
,
x
,
y
):
def
detect_gestures
(
self
):
p
=
self
.
find_point
(
sid
)
"""
Detect if any gestures have occured in the past gesture frame. This
method is called in each time interval of the gesture thread, for
gestures that can only be detected using accumulated point down/up/move
events.
"""
# Simple and double taps are detected in the main thread, specific
# single-tap in the gesture thread
self
.
detect_single_tap
()
# Optimization: only update if the point has moved far enough
# If the touch points have not been updated, neither have the gestures
if
p
.
distance_to
(
x
,
y
)
>
DIST_THRESHOLD
:
if
self
.
points_changed
:
p
.
update
(
x
,
y
)
# The new centroid is used for pan/rotate/pinch detection
self
.
update_centroid
(
moving
=
p
)
self
.
update_centroid
()
self
.
trigger
(
MoveEvent
(
p
))
self
.
detect_pinch
(
p
)
# TODO: Detect pan
# If a pan event is detected, ignore any rotate or pinch movement
# (they are considered noise)
if
not
self
.
detect_pan
():
self
.
detect_rotation_and_pinch
()
def
detect_pinch
(
self
,
moved
):
self
.
points_changed
=
False
cx
,
cy
=
self
.
centroid
dist
=
moved
.
distance_to
(
cx
,
cy
)
old_dist
=
distance
((
moved
.
px
,
moved
.
py
),
self
.
centroid
)
if
abs
(
dist
-
old_dist
)
>
DIST_THRESHOLD
:
def
start_gesture_thread
(
self
):
self
.
trigger
(
PinchEvent
(
cx
,
cy
,
dist
/
old_dist
,
"""
len
(
self
.
points
)))
Loop of the gesture thread.
"""
interval
=
1.
/
GESTURE_UPDATE_RATE
while
True
:
self
.
detect_gestures
()
time
.
sleep
(
interval
)
def
stop
(
self
):
def
stop
(
self
):
self
.
log
(
'Stopping event loop'
)
"""
Stop main event loop.
"""
self
.
log
(
'Stopping TUIO server'
)
self
.
server
.
stop
()
if
self
.
thread
:
if
self
.
thread
:
self
.
log
(
'Stopping main loop thread'
)
self
.
thread
.
join
()
self
.
thread
.
join
()
self
.
thread
=
False
self
.
thread
=
None
else
:
self
.
server
.
stop
()
def
start
(
self
,
threaded
=
False
):
def
start
(
self
,
threaded
=
False
,
daemon
=
False
):
"""
"""
Start event loop.
Start main event loop. If threaded is set to True, the main loop is
started in a new thread. If daemon is also set to True, that thread
will be daemonic. The daemon option makes a call to stop() unnecessary.
"""
"""
if
threaded
:
if
threaded
:
self
.
thread
=
Thread
(
target
=
self
.
start
,
kwargs
=
{
'threaded'
:
False
})
self
.
log
(
'Creating %sthread for main loop'
self
.
thread
.
daemon
=
True
%
(
'daemon '
if
daemon
else
''
))
self
.
thread
=
Thread
(
target
=
self
.
start
)
self
.
thread
.
daemon
=
daemon
self
.
thread
.
start
()
self
.
thread
.
start
()
return
return
# Start gesture thread
self
.
log
(
'Starting gesture thread'
)
gesture_thread
=
Thread
(
target
=
self
.
start_gesture_thread
)
gesture_thread
.
daemon
=
True
gesture_thread
.
start
()
# Start TUIO listener
try
:
try
:
self
.
log
(
'Starting event loop'
)
self
.
log
(
'Starting TUIO server'
)
self
.
server
=
TuioServer2D
(
self
,
verbose
=
self
.
tuio_verbose
)
self
.
server
.
start
()
self
.
server
.
start
()
except
KeyboardInterrup
t
:
except
SystemExi
t
:
self
.
stop
()
self
.
stop
()
def
bind
(
self
,
gesture
,
handler
):
def
bind
(
self
,
gesture
,
handler
,
*
args
,
**
kwargs
):
if
gesture
not
in
SUPPORTED_GESTURES
:
"""
Bind a handler to an event or gesture.
"""
if
gesture
not
in
SUPPORTED_EVENTS
:
raise
ValueError
(
'Unsupported gesture "%s".'
%
gesture
)
raise
ValueError
(
'Unsupported gesture "%s".'
%
gesture
)
if
gesture
not
in
self
.
handlers
:
if
gesture
not
in
self
.
handlers
:
self
.
handlers
[
gesture
]
=
[]
self
.
handlers
[
gesture
]
=
[]
self
.
handlers
[
gesture
].
append
(
handler
)
self
.
handlers
[
gesture
].
append
(
(
handler
,
args
,
kwargs
)
)
def
trigger
(
self
,
event
):
def
trigger
(
self
,
event
):
"""
Call all handlers bound to the name of the triggered event.
"""
if
event
.
__class__
.
_name
in
self
.
handlers
:
if
event
.
__class__
.
_name
in
self
.
handlers
:
h
=
self
.
handlers
[
event
.
__class__
.
_name
]
h
=
self
.
handlers
[
event
.
__class__
.
_name
]
self
.
log
(
'Event triggered: "%s" (%d handlers)'
%
(
event
,
len
(
h
)),
self
.
log
(
'Event triggered: "%s" (%d handlers)'
%
(
event
,
len
(
h
)),
1
+
int
(
isinstance
(
event
,
Basic
Event
)))
1
+
int
(
isinstance
(
event
,
Event
)))
for
handler
in
h
:
for
handler
,
args
,
kwargs
in
h
:
handler
(
event
)
handler
(
event
,
*
args
,
**
kwargs
)
def
centroid_movement
(
self
):
cx
,
cy
=
self
.
centroid
ocx
,
ocy
=
self
.
old_centroid
return
cx
-
ocx
,
cy
-
ocy
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
def
tap
(
event
):
def
tap
(
event
,
tap_type
):
print
'tap:'
,
event
print
'tap:'
,
tap_type
listener
=
MultiTouchListener
(
verbose
=
1
,
tuio_verbose
=
0
)
listener
.
bind
(
'tap'
,
tap
,
0
)
listener
.
bind
(
'single_tap'
,
tap
,
1
)
listener
.
bind
(
'double_tap'
,
tap
,
2
)
listener
.
bind
(
'rotate'
,
lambda
e
:
0
)
loop
=
MultiTouchListener
(
verbose
=
1
,
tuio_verbose
=
0
)
try
:
loop
.
bind
(
'tap'
,
tap
)
listener
.
start
(
)
loop
.
bind
(
'double_tap'
,
tap
)
except
KeyboardInterrupt
:
loop
.
start
()
listener
.
stop
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment