]>
Commit | Line | Data |
---|---|---|
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 | ||
76 | import wx | |
77 | ||
78 | class 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() |