]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/gestures.py
Added wx.lib.gestures module from Daniel Pozmanter which supports
[wxWidgets.git] / wxPython / wx / lib / gestures.py
CommitLineData
51c5e1f2
RD
1#Mouse Gestures
2
3#Version 0.0.0 ALPHA
4
5#By Daniel Pozmanter
6#drpython@bluebottle.com
7
8#Released under the terms of the wxWindows License.
9
10#This is a class to add Mouse Gestures to a program.
11#It can be used in two ways:
12#
13#1. Automatic:
14# Automatically runs mouse gestures.
15# You need to set the gestures, and their associated actions,
16# as well as the Mouse Button/Modifiers to use.
17#
18# (Mouse Buttons are set in init)
19#
20#2. Manual:
21# Same as above, but you do not need to set the mouse button/modifiers.
22# You can launch this from events as you wish.
23#
24#An example is provided in the demo.
25#The parent window is where the mouse events will be recorded.
26#(So if you want to record them in a pop up window, use manual mode,
27#and set the pop up as the parent).
28#
29#Start() starts recording mouse movement.
30#End() stops the recording, compiles all the gestures into a list,
31#and looks through the registered gestures to find a match.
32#The first matchs associated action is then run.
33
34#The marginoferror is how much to forgive when calculating movement:
35#If the margin is 25, then movement less than 25 pixels will not be detected.
36
37#Recognized: L, R, U, D, 1, 3, 7, 9
38
39#Styles: Manual (Automatic By Default), DisplayNumbersForDiagonals (Off By Default).
40#Not Yet Implemented
41
42#The criteria for a direction is as follows:
43#x in a row. (Where x is the WobbleTolerance).
44#So if the WobbleTolerance is 9
45# 'URUUUUUUUUUUUUUUURUURUUUU1' is Up.
46
47#The higher this number, the less sensitive this class is.
48#So the more likely something like 1L will translate to 1.
49
50#This is good, since the mouse does tend to wobble somewhat,
51#and a higher number allows for this.
52
53#To change this, use SetWobbleTolerance
54
55#Also, to help with recognition of a diagonal versus
56#a vey messy straight line, if the greater absolute value
57#is not greater than twice the lesser, only the grater value
58#is counted.
59
60#ToDo:
61
62#Add in modifier code (Ctrl, Alt, etc).
63
64#SetGestureLine(wx.Colour(), int width)
65
66#Add "Ends With": AddGestureEndsWith(self, gesture, action, args)
67#Add "Starts With": AddGestuteStartsWith(self, gesture, action, args)
68
69#At the moment, the mouse button can only be set at startup.
70#I could use UnBind, but this may limit the wxPython version being used,
71#and, what if the user has other events bound?
72#So I think, if the user wants to change the mouse button at runtime,
73#the best solution is to use manual mode.
74
75
76import wx
77
78class MouseGestures:
79 def __init__(self, parent, Auto=True, MouseButton=wx.MOUSE_BTN_MIDDLE):
80 self.parent = parent
81
82 self.gestures = []
83 self.actions = []
84 self.actionarguments = []
85
86 self.mousebutton = MouseButton
87 self.modifiers = 0
88
89 self.recording = False
90
91 self.lastposition = (-1, -1)
92
93 self.pen = wx.Pen(wx.Colour(0, 144, 255), 5)
94
95 self.dc = wx.ScreenDC()
96 self.dc.SetPen(self.pen)
97
98 self.showgesture = False
99
100 self.wobbletolerance = 7
101
102 self.rawgesture = ''
103
104 self.SetAuto(Auto)
105
106 def Action(self, gesture):
107 if gesture in self.gestures:
108 i = self.gestures.index(gesture)
109 apply(self.actions[i], self.actionarguments[i])
110
111 def AddGesture(self, gesture, action, *args):
112 #Make Sure not a duplicate:
113 self.RemoveGesture(gesture)
114
115 self.gestures.append(gesture)
116 self.actions.append(action)
117 self.actionarguments.append(args)
118
119 def End(self):
120 self.recording = False
121
122 #Figure out the gestures (Look for occurances of 5 in a row or more):
123
124 tempstring = '0'
125 possiblechange = '0'
126
127 directions = ''
128
129 for g in self.rawgesture:
130 l = len(tempstring)
131 if g != tempstring[l - 1]:
132 if g == possiblechange:
133 tempstring = g + g
134 else:
135 possiblechange = g
136 else:
137 tempstring += g
138 if len(tempstring) >= self.wobbletolerance:
139 ld = len(directions)
140 if ld > 0:
141 if directions[ld - 1] != g:
142 directions += g
143 else:
144 directions += g
145 tempstring = '0'
146
147 return directions
148
149 def GetDirection(self, point1, point2):
150 #point1 is the old point
151 #point2 is current
152
153 x1, y1 = point1
154 x2, y2 = point2
155
156 #(Negative = Left, Up)
157 #(Positive = Right, Down)
158
159 horizontal = x2 - x1
160 vertical = y2 - y1
161
162 horizontalchange = abs(horizontal) > 0
163 verticalchange = abs(vertical) > 0
164
165 if horizontalchange and verticalchange:
166 ah = abs(horizontal)
167 av = abs(vertical)
168 if ah > av:
169 if (ah / av) > 2:
170 vertical = 0
171 verticalchange = False
172 elif av > ah:
173 if (av / ah) > 2:
174 horizontal = 0
175 horizontalchange = False
176
177 if horizontalchange and verticalchange:
178 #Diagonal
179 if (horizontal > 0) and (vertical > 0):
180 return '3'
181 elif (horizontal > 0) and (vertical < 0):
182 return '9'
183 elif (horizontal < 0) and (vertical > 0):
184 return '1'
185 else:
186 return '7'
187 else:
188 #Straight Line
189 if horizontalchange:
190 if horizontal > 0:
191 return 'R'
192 else:
193 return 'L'
194 else:
195 if vertical > 0:
196 return 'D'
197 else:
198 return 'U'
199
200
201 def OnEnd(self, event):
202 result = self.End()
203
204 self.Action(result)
205
206 event.Skip()
207
208 def OnMotion(self, event):
209 if self.recording:
210 currentposition = event.GetPosition()
211 if self.lastposition != (-1, -1):
212 self.rawgesture += self.GetDirection(self.lastposition, currentposition)
213 if self.showgesture:
214 #Draw it!
215 px1, py1 = self.parent.ClientToScreen(self.lastposition)
216 px2, py2 = self.parent.ClientToScreen(currentposition)
217
218 self.dc.DrawLine(px1, py1, px2, py2)
219
220 self.lastposition = currentposition
221
222 event.Skip()
223
224 def OnStart(self, event):
225 self.Start()
226 event.Skip()
227
228 def RemoveGesture(self, gesture):
229 if gesture in self.gestures:
230 i = self.gestures.index(gesture)
231 del self.gestures[i]
232 del self.actions[i]
233 del self.actionarguments[i]
234
235 def SetAuto(self, auto):
236 #I was not sure about making this part of init, so I left it as its own method for now.
237 if auto:
238 if self.mousebutton == wx.MOUSE_BTN_LEFT:
239 self.parent.Bind(wx.EVT_LEFT_DOWN, self.OnStart)
240 self.parent.Bind(wx.EVT_LEFT_UP, self.OnEnd)
241 elif self.mousebutton == wx.MOUSE_BTN_MIDDLE:
242 self.parent.Bind(wx.EVT_MIDDLE_DOWN, self.OnStart)
243 self.parent.Bind(wx.EVT_MIDDLE_UP, self.OnEnd)
244 elif self.mousebutton == wx.MOUSE_BTN_RIGHT:
245 self.parent.Bind(wx.EVT_RIGHT_DOWN, self.OnStart)
246 self.parent.Bind(wx.EVT_RIGHT_UP, self.OnEnd)
247 self.parent.Bind(wx.EVT_MOTION, self.OnMotion)
248
249 def SetGesturesVisible(self, vis):
250 self.showgesture = vis
251
252 def SetModifiers(self, modifers):
253 self.modifiers = modifiers
254
255 def SetWobbleTolerance(self, wobbletolerance):
256 self.WobbleTolerance = wobbletolerance
257
258 def Start(self):
259 self.recording = True
260 self.rawgesture = ''
261 self.lastposition = (-1, -1)
262 if self.showgesture:
263 self.parent.Refresh()