Skip to content

Commit 3ad88ce

Browse files
committed
Merge pull request #20 from GCorbel/master
Merge GCorbel's motion command mode
2 parents 40324df + fceec4b commit 3ad88ce

File tree

4 files changed

+221
-48
lines changed

4 files changed

+221
-48
lines changed

MotionControl.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import sys, os, ConfigParser
2+
from leap import Leap, CircleGesture, KeyTapGesture, ScreenTapGesture, SwipeGesture
3+
4+
class Motion_Control_Listener(Leap.Listener): #The Listener that we attach to the controller. This listener is for motion control
5+
def __init__(self, mouse):
6+
super(Motion_Control_Listener, self).__init__() #Initialize like a normal listener
7+
8+
def on_init(self, controller):
9+
self.read_config() #Read the config file
10+
self.init_list_of_commands() #Initialize the list of recognized commands
11+
12+
print "Initialized"
13+
14+
def read_config(self):
15+
self.config = ConfigParser.ConfigParser()
16+
self.config.read("./commands.ini")
17+
18+
def init_list_of_commands(self):
19+
#Initialize all commands an put it in an array
20+
self.commands = [
21+
ScreentapCommand(),
22+
SwiperightCommand(),
23+
SwipeleftCommand(),
24+
CounterclockwiseCommand(),
25+
ClockwiseCommand(),
26+
KeytapCommand()
27+
]
28+
29+
def on_connect(self, controller):
30+
#Enable all gestures
31+
controller.enable_gesture(Leap.Gesture.TYPE_CIRCLE);
32+
controller.enable_gesture(Leap.Gesture.TYPE_KEY_TAP);
33+
controller.enable_gesture(Leap.Gesture.TYPE_SCREEN_TAP);
34+
controller.enable_gesture(Leap.Gesture.TYPE_SWIPE);
35+
36+
print "Connected"
37+
38+
def on_disconnect(self, controller):
39+
print "Disconnected"
40+
41+
def on_exit(self, controller):
42+
print "Exited"
43+
44+
def on_frame(self, controller):
45+
frame = controller.frame() #Grab the latest 3D data
46+
if not frame.hands.empty: #Make sure we have some hands to work with
47+
for command in self.commands: #Loop all enabled commands
48+
if(command.applicable(frame)): #If the motion associated to the command is triggered
49+
self.execute(frame, command.name) #Execute the command
50+
51+
def execute(self, frame, command_name):
52+
number_for_fingers = self.get_fingers_code(frame) #Get a text correspond to the number of fingers
53+
if(self.config.has_option(command_name, number_for_fingers)): #If the command if finded in the config file
54+
syscommand = self.config.get(command_name, number_for_fingers) #Prepare the command
55+
print(syscommand)
56+
os.system(syscommand) #Execute the command
57+
58+
def get_fingers_code(self, frame):
59+
return "%dfinger" % len(frame.fingers)
60+
61+
62+
class ScreentapCommand():
63+
def __init__(self):
64+
self.name = "screentap"
65+
#The name of the command in the config file
66+
67+
#Return true if the command is applicable
68+
def applicable(self, frame):
69+
return(frame.gestures()[0].type == Leap.Gesture.TYPE_SCREEN_TAP)
70+
71+
class KeytapCommand():
72+
def __init__(self):
73+
self.name = "keytap" #The name of the command in the config file
74+
75+
#Return true if the command is applicable
76+
def applicable(self, frame):
77+
return(frame.gestures()[0].type == Leap.Gesture.TYPE_KEY_TAP)
78+
79+
class SwiperightCommand():
80+
def __init__(self):
81+
self.name = "swiperight" #The name of the command in the config file
82+
83+
#Return true if the command is applicable
84+
def applicable(self, frame):
85+
swipe = SwipeGesture(frame.gestures()[0])
86+
return(swipe.state == Leap.Gesture.STATE_STOP
87+
and swipe.type == Leap.Gesture.TYPE_SWIPE
88+
and swipe.direction[0] < 0)
89+
90+
class SwipeleftCommand():
91+
def __init__(self):
92+
self.name = "swipeleft" #The name of the command in the config file
93+
94+
#Return true if the command is applicable
95+
def applicable(self, frame):
96+
swipe = SwipeGesture(frame.gestures()[0])
97+
return(swipe.state == Leap.Gesture.STATE_STOP
98+
and swipe.type == Leap.Gesture.TYPE_SWIPE
99+
and swipe.direction[0] > 0)
100+
101+
class ClockwiseCommand():
102+
def __init__(self):
103+
self.name = "clockwise" #The name of the command in the config file
104+
105+
#Return true if the command is applicable
106+
def applicable(self, frame):
107+
circle = CircleGesture(frame.gestures()[0])
108+
return(circle.state == Leap.Gesture.STATE_STOP and
109+
circle.type == Leap.Gesture.TYPE_CIRCLE and
110+
circle.pointable.direction.angle_to(circle.normal) <= Leap.PI/4)
111+
112+
class CounterclockwiseCommand():
113+
def __init__(self):
114+
self.name = "counterclockwise" #The name of the command in the config file
115+
116+
#Return true if the command is applicable
117+
def applicable(self, frame):
118+
circle = CircleGesture(frame.gestures()[0])
119+
return(circle.state == Leap.Gesture.STATE_STOP and
120+
circle.type == Leap.Gesture.TYPE_CIRCLE and
121+
circle.pointable.direction.angle_to(circle.normal) > Leap.PI/4)

PyLeapMouse.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from leap import Leap, Mouse
55
from PalmControl import Palm_Control_Listener #For palm-tilt based control
66
from FingerControl import Finger_Control_Listener #For finger-pointing control
7+
from MotionControl import Motion_Control_Listener #For motion control
78

89
def show_help():
910
print "----------------------------------PyLeapMouse----------------------------------"
@@ -23,13 +24,21 @@ def main():
2324

2425
#Default
2526
finger_mode = True
27+
palm_mode = False
28+
motion_mode = False
2629
smooth_aggressiveness = 8
2730
smooth_falloff = 1.3
2831

2932
for i in range(0,len(sys.argv)):
3033
arg = sys.argv[i].lower()
3134
if "--palm" in arg:
3235
finger_mode = False
36+
palm_mode = True
37+
motion_mode = False
38+
if "--motion" in arg:
39+
finger_mode = False
40+
palm_mode = False
41+
motion_mode = True
3342
if "--smooth-falloff" in arg:
3443
smooth_falloff = float(sys.argv[i+1])
3544
if "--smooth-aggressiveness" in arg:
@@ -41,9 +50,12 @@ def main():
4150
if finger_mode: #Finger pointer mode
4251
listener = Finger_Control_Listener(Mouse, smooth_aggressiveness=smooth_aggressiveness, smooth_falloff=smooth_falloff)
4352
print "Using finger mode..."
44-
else: #Palm control mode
53+
elif palm_mode: #Palm control mode
4554
listener = Palm_Control_Listener(Mouse)
4655
print "Using palm mode..."
56+
elif motion_mode: #Motion control mode
57+
listener = Motion_Control_Listener(Mouse)
58+
print "Using motion mode..."
4759

4860

4961
controller = Leap.Controller() #Get a Leap controller

README.md

Lines changed: 84 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,91 @@
11
PyLeapMouse
22
===========
33

4-
wyager's Proof-of-concept code for a Leap Motion-based mouse controller. It now works with Linux, OS X and Windows.
4+
wyager's Proof-of-concept code for a Leap Motion-based mouse controller. It now works with Linux, OS X and Windows.
55

6-
The most recent version is on Github at github.com/openleap/pyleapmouse.
6+
The most recent version is on Github at github.com/openleap/pyleapmouse.
77

8-
###Configuration:
9-
1. Launch the Leap app (if not launched already) and plug in your Leap
10-
2. If you have not done so already, Configure your Leap screen from the Leap menu.
11-
3. WINDOWS USERS: You must copy the Leap.py file and all required library files (.libs and .dlls) from your Leap SDK folder to the "Windows" folder. These files are already included for OS X users, because OS X is 64-bit only.
8+
###Configuration:
9+
1. Launch the Leap app (if not launched already) and plug in your Leap
10+
2. If you have not done so already, Configure your Leap screen from the Leap menu.
11+
3. WINDOWS USERS: You must copy the Leap.py file and all required library files (.libs and .dlls) from your Leap SDK folder to the "Windows" folder. These files are already included for OS X users, because OS X is 64-bit only.
1212
4. LINUX USERS: You must copy the Leap.py file and all required library files (.sos) from your Leap SDK folder to the "Linux" folder (same reason as for Windows); alternatively, add the directory (or directories) containing them to your PYTHONPATH. Additionally, you must have the PyUserInput and Xlib Python modules installed.
13-
5. `cd` to the directory all this stuff is in and run `python PyLeapMouse.py` (minus quotes) or just double-click PyLeapMouse.py if you have your computer configured to launch .py files.
14-
6. Launch with the --palm argument to run in palm mode (with much more accurate two-handed control).
15-
16-
###Usage with Finger Mode (python PyLeapMouse.py --finger) (default):
17-
1. Insert your hand into frame.
18-
2. The forwardmost finger that the program detects is the mouse finger. Where it points, the cursor goes.
19-
3. Stick your thumb out (see note) to click down, and fold your thumb in to click up.
20-
4. Using two pointer fingers (e.g. index and middle) goes into scroll mode, which is not very intuitive but shows how it might work. The fingertips must be within a short distance of each other to activate scroll mode.
21-
22-
###Usage with Palm Mode (python PyLeapMouse.py --palm):
23-
Operation is as follows:
24-
One hand in frame: The tilt of this hand moves the mouse.
25-
Two hands in frame: Left hand controls action.
26-
All fingers closed: Mouse movement with right hand tilt.
27-
One finger open: Clicking. Left mouse button is down. Mouse movement with right hand tilt.
28-
Two fingers open: Scrolling. Scrolling with right hand movement.
29-
This is a somewhat unintuitive method of operation, but I find that it gives exceptionally better control than the most obvious "point-at-screen" method of mouse control. With this two-handed tilt based mode, it is easy to hit and properly engage small buttons, scroll through webpages, etc.
30-
31-
###Notes:
32-
This is a spare-time project, so it's not perfect quality. However, I tried to keep the code clean and readable. Let me know if you find any bugs (which there are certainly at least a few of). You can reach me at will (dot) yager (at) gmail (dot) (what the gmail domain ends in).
33-
The contents of the files are as follows:
34-
PyLeapMouse.py: The actual program
35-
FingerControl.py: Pointer-finger-control specific code
36-
PalmControl.py: Palm-tilt-control specific code
37-
Linux/OSX/Windows:
38-
Various OS-specific Leap library files
39-
Mouse.py: A set of generic commands and classes to abstract away from OS-Specific mouse commands
40-
Geometry.py: Geometric functions
41-
MiscFunctions.py: Things that aren't strictly geometry and aren't specific to any interface style
42-
README.md: You are here
43-
44-
###Advanced Options:
45-
`--smooth-aggressiveness [value]` sets the number of samples to use for pointer finger mouse smoothing.
46-
`--smooth-falloff [value]` sets the rate at which previous samples lose importance.
47-
For every sample back in time, the previous location of the mouse is weighted with weight smooth_falloff^(-#sample).
48-
So if smooth_falloff = 1.2, the current frame has weight 1/(1.2^0)=1, but the frame from 5 frames ago has weight 1/(1.2^5) = .4
49-
By default, the smooth aggressiveness is 8 frames with a falloff of 1.3.
50-
51-
###TODO:
52-
Add proper relative mouse movement. Should be pretty easy on Windows, not sure how to do so on OS X.
53-
Add multiple monitor support for absolute mouse mode (and OS X's pseudo-relative mode).
13+
5. `cd` to the directory all this stuff is in and run `python PyLeapMouse.py` (minus quotes) or just double-click PyLeapMouse.py if you have your computer configured to launch .py files.
14+
6. Launch with the --palm argument to run in palm mode (with much more accurate two-handed control).
15+
16+
###Usage with Finger Mode (python PyLeapMouse.py --finger) (default):
17+
1. Insert your hand into frame.
18+
2. The forwardmost finger that the program detects is the mouse finger. Where it points, the cursor goes.
19+
3. Stick your thumb out (see note) to click down, and fold your thumb in to click up.
20+
4. Using two pointer fingers (e.g. index and middle) goes into scroll mode, which is not very intuitive but shows how it might work. The fingertips must be within a short distance of each other to activate scroll mode.
21+
22+
###Usage with Palm Mode (python PyLeapMouse.py --palm):
23+
Operation is as follows:
24+
One hand in frame: The tilt of this hand moves the mouse.
25+
Two hands in frame: Left hand controls action.
26+
All fingers closed: Mouse movement with right hand tilt.
27+
One finger open: Clicking. Left mouse button is down. Mouse movement with right hand tilt.
28+
Two fingers open: Scrolling. Scrolling with right hand movement.
29+
This is a somewhat unintuitive method of operation, but I find that it gives exceptionally better control than the most obvious "point-at-screen" method of mouse control. With this two-handed tilt based mode, it is easy to hit and properly engage small buttons, scroll through webpages, etc.
30+
31+
###Usage with Motion Mode (python PyLeapMouse.py --motion):
32+
Movements are associated with commands listed in a file `commands.ini` placed at the root folder. Here is an example of what the file should look like :
33+
34+
[screentap]
35+
36+
[keytap]
37+
38+
[swiperight]
39+
1finger: rhythmbox-client --next
40+
2finger: rhythmbox-client --next
41+
3finger: rhythmbox-client --next
42+
4finger: rhythmbox-client --next
43+
5finger: rhythmbox-client --next
44+
45+
[swipeleft]
46+
1finger: rhythmbox-client --previous
47+
2finger: rhythmbox-client --previous
48+
3finger: rhythmbox-client --previous
49+
4finger: rhythmbox-client --previous
50+
5finger: rhythmbox-client --previous
51+
52+
[clockwise]
53+
1finger: rhythmbox-client --play
54+
2finger: rhythmbox-client --play
55+
3finger: rhythmbox-client --play
56+
4finger: rhythmbox-client --play
57+
5finger: rhythmbox-client --play
58+
59+
[counterclockwise]
60+
1finger: rhythmbox-client --pause
61+
2finger: rhythmbox-client --pause
62+
3finger: rhythmbox-client --pause
63+
4finger: rhythmbox-client --pause
64+
5finger: rhythmbox-client --pause
65+
66+
Every commands could have a different behaviour if 1, 2, 3 ... 10 fingers are recognized but It's recommanded to use the same command for each number of fingers due to a lack of precision with Leap Motion.
67+
68+
###Notes:
69+
This is a spare-time project, so it's not perfect quality. However, I tried to keep the code clean and readable. Let me know if you find any bugs (which there are certainly at least a few of). You can reach me at will (dot) yager (at) gmail (dot) (what the gmail domain ends in).
70+
The contents of the files are as follows:
71+
PyLeapMouse.py: The actual program
72+
FingerControl.py: Pointer-finger-control specific code
73+
PalmControl.py: Palm-tilt-control specific code
74+
Linux/OSX/Windows:
75+
Various OS-specific Leap library files
76+
Mouse.py: A set of generic commands and classes to abstract away from OS-Specific mouse commands
77+
Geometry.py: Geometric functions
78+
MiscFunctions.py: Things that aren't strictly geometry and aren't specific to any interface style
79+
README.md: You are here
80+
81+
###Advanced Options:
82+
`--smooth-aggressiveness [value]` sets the number of samples to use for pointer finger mouse smoothing.
83+
`--smooth-falloff [value]` sets the rate at which previous samples lose importance.
84+
For every sample back in time, the previous location of the mouse is weighted with weight smooth_falloff^(-#sample).
85+
So if smooth_falloff = 1.2, the current frame has weight 1/(1.2^0)=1, but the frame from 5 frames ago has weight 1/(1.2^5) = .4
86+
By default, the smooth aggressiveness is 8 frames with a falloff of 1.3.
87+
88+
###TODO:
89+
Add proper relative mouse movement. Should be pretty easy on Windows, not sure how to do so on OS X.
90+
Add multiple monitor support for absolute mouse mode (and OS X's pseudo-relative mode).
5491
Use PyUserInput for all mouse input? Or use Xlib directly for Linux?

leap.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
if sys.platform == "darwin":
33
import OSX.Leap as Leap
44
import OSX.Mouse as Mouse
5+
from OSX.Leap import CircleGesture, KeyTapGesture, ScreenTapGesture, SwipeGesture
56
elif 'linux' in sys.platform:
67
import Linux.Leap as Leap
78
import Linux.Mouse as Mouse
9+
from Linux.Leap import CircleGesture, KeyTapGesture, ScreenTapGesture, SwipeGesture
810
else:
911
import Windows.Leap as Leap
1012
import Windows.Mouse as Mouse
13+
from Windows.Leap import CircleGesture, KeyTapGesture, ScreenTapGesture, SwipeGesture

0 commit comments

Comments
 (0)