From 51c5e1f21eb54f2457269258040b7cd360cb3f69 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Fri, 18 Feb 2005 18:02:19 +0000 Subject: [PATCH] Added wx.lib.gestures module from Daniel Pozmanter which supports using Mouse Gestures in an application. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@32148 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/demo/Main.py | 2 + wxPython/demo/MouseGestures.py | 115 ++++++++++++++ wxPython/docs/CHANGES.txt | 5 + wxPython/wx/lib/gestures.py | 263 +++++++++++++++++++++++++++++++++ 4 files changed, 385 insertions(+) create mode 100644 wxPython/demo/MouseGestures.py create mode 100644 wxPython/wx/lib/gestures.py diff --git a/wxPython/demo/Main.py b/wxPython/demo/Main.py index 94d03cb458..6b978e2291 100644 --- a/wxPython/demo/Main.py +++ b/wxPython/demo/Main.py @@ -50,6 +50,7 @@ _treeList = [ 'StandardPaths', 'MediaCtrl', 'DatePickerCtrl', + 'MouseGestures', ]), # managed windows == things with a (optional) caption you can close @@ -226,6 +227,7 @@ _treeList = [ 'FileHistory', 'FontEnumerator', 'Joystick', + 'MouseGestures', 'OGL', 'PrintFramework', 'ShapedWindow', diff --git a/wxPython/demo/MouseGestures.py b/wxPython/demo/MouseGestures.py new file mode 100644 index 0000000000..3083414f15 --- /dev/null +++ b/wxPython/demo/MouseGestures.py @@ -0,0 +1,115 @@ +#******************* +#By Daniel Pozmanter +#Under the GPL, etc, etc. + +#Thanks to Robin Dunn for taking the time to show me how to do this +#via the mailing list. + +#Version 0.0.0 ALPHA + +#This is a hacked version of DragAndDrop.py from the wxPython demo 2.5.2.8 + +import wx, wx.stc +from wx.lib.gestures import MouseGestures + +#ToDo: + +#Add a dialog to record gestures (Have it showcase the manual mode) +#Allow users to remove gestures + +#---------------------------------------------------------------------- + +class TestPanel(wx.Panel): + def __init__(self, parent, log): + wx.Panel.__init__(self, parent, -1) + + ID_GESTURE = wx.NewId() + + self.log = log + + #Mouse Gestures: + + self.mg = MouseGestures(self, Auto=True) + + self.mg.SetGesturesVisible(True) + + self.mg.AddGesture('L', self.LogSomethingClever, 'You moved left') + self.mg.AddGesture('9', self.LogSomethingClever, 'You moved right and up') + self.mg.AddGesture('U', self.LogSomethingClever, 'You moved up') + self.mg.AddGesture('DR', self.OnDownThenRight) + + #Widgets: + + self.btnAddGesture = wx.Button(self, ID_GESTURE, 'Add New Gesture') + + msg = "Mouse Gestures" + text = wx.StaticText(self, -1, "", style=wx.ALIGN_CENTRE) + text.SetFont(wx.Font(24, wx.SWISS, wx.NORMAL, wx.BOLD, False)) + text.SetLabel(msg) + + w,h = text.GetTextExtent(msg) + text.SetSize(wx.Size(w,h+1)) + text.SetForegroundColour(wx.BLUE) + + #Sizer: + outsideSizer = wx.BoxSizer(wx.VERTICAL) + + outsideSizer.Add(text, 0, wx.EXPAND|wx.ALL, 5) + outsideSizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND) + outsideSizer.Add(wx.StaticText(self, -1, ' '), 0, wx.EXPAND) + outsideSizer.Add(wx.StaticText(self, -1, 'Hold The Middle Mouse Button Down to Gesticulate'), 0, wx.EXPAND) + outsideSizer.Add(wx.StaticText(self, -1, 'Left, The Diagonal Up/Right, Down Then Right, and Up are Preset'), 0, wx.EXPAND) + outsideSizer.Add(wx.StaticText(self, -1, ' '), 0, wx.EXPAND) + outsideSizer.Add(self.btnAddGesture, 0, wx.SHAPED) + + self.SetAutoLayout(True) + self.SetSizer(outsideSizer) + + + #Events: + self.Bind(wx.EVT_BUTTON, self.OnAddGesture, id=ID_GESTURE) + + def LogSomethingClever(self, somethingclever): + self.log.WriteText(somethingclever) + + def OnAddGesture(self, event): + d = wx.TextEntryDialog(self, "Enter Gesture (LRUD1379) (EG Right Then Up Then DownLeft is RU1):", "Add New Gesture", "") + answer1 = d.ShowModal() + gesture = d.GetValue() + d.Destroy() + d = wx.TextEntryDialog(self, 'Print the following text on "%s":' % gesture, "Gesture Action", "") + answer2 = d.ShowModal() + text = d.GetValue() + d.Destroy() + if (answer1 == wx.ID_OK) and (answer2 == wx.ID_OK): + self.mg.AddGesture(gesture.upper(), self.LogSomethingClever, text) + + def OnDownThenRight(self): + self.log.WriteText('You made an "L"!') + +#---------------------------------------------------------------------- + +def runTest(frame, nb, log): + win = TestPanel(nb, log) + return win + +#---------------------------------------------------------------------- + + +overview = """\ + + +This demo shows how to add MouseGestures +to your program, and showcases the MouseGestures +class in all it's mousey glory. +

+ + +""" + + +if __name__ == '__main__': + import sys,os + import run + run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) + diff --git a/wxPython/docs/CHANGES.txt b/wxPython/docs/CHANGES.txt index 1ca38daee8..2f2e649d72 100644 --- a/wxPython/docs/CHANGES.txt +++ b/wxPython/docs/CHANGES.txt @@ -221,6 +221,11 @@ buttons. Added wx.DatePickerCtrl. +wx.html.HtmlWindow now supports background images. + +Added wx.lib.gestures module from Daniel Pozmanter which supports +using Mouse Gestures in an application. + diff --git a/wxPython/wx/lib/gestures.py b/wxPython/wx/lib/gestures.py new file mode 100644 index 0000000000..b4e8beea03 --- /dev/null +++ b/wxPython/wx/lib/gestures.py @@ -0,0 +1,263 @@ +#Mouse Gestures + +#Version 0.0.0 ALPHA + +#By Daniel Pozmanter +#drpython@bluebottle.com + +#Released under the terms of the wxWindows License. + +#This is a class to add Mouse Gestures to a program. +#It can be used in two ways: +# +#1. Automatic: +# Automatically runs mouse gestures. +# You need to set the gestures, and their associated actions, +# as well as the Mouse Button/Modifiers to use. +# +# (Mouse Buttons are set in init) +# +#2. Manual: +# Same as above, but you do not need to set the mouse button/modifiers. +# You can launch this from events as you wish. +# +#An example is provided in the demo. +#The parent window is where the mouse events will be recorded. +#(So if you want to record them in a pop up window, use manual mode, +#and set the pop up as the parent). +# +#Start() starts recording mouse movement. +#End() stops the recording, compiles all the gestures into a list, +#and looks through the registered gestures to find a match. +#The first matchs associated action is then run. + +#The marginoferror is how much to forgive when calculating movement: +#If the margin is 25, then movement less than 25 pixels will not be detected. + +#Recognized: L, R, U, D, 1, 3, 7, 9 + +#Styles: Manual (Automatic By Default), DisplayNumbersForDiagonals (Off By Default). +#Not Yet Implemented + +#The criteria for a direction is as follows: +#x in a row. (Where x is the WobbleTolerance). +#So if the WobbleTolerance is 9 +# 'URUUUUUUUUUUUUUUURUURUUUU1' is Up. + +#The higher this number, the less sensitive this class is. +#So the more likely something like 1L will translate to 1. + +#This is good, since the mouse does tend to wobble somewhat, +#and a higher number allows for this. + +#To change this, use SetWobbleTolerance + +#Also, to help with recognition of a diagonal versus +#a vey messy straight line, if the greater absolute value +#is not greater than twice the lesser, only the grater value +#is counted. + +#ToDo: + +#Add in modifier code (Ctrl, Alt, etc). + +#SetGestureLine(wx.Colour(), int width) + +#Add "Ends With": AddGestureEndsWith(self, gesture, action, args) +#Add "Starts With": AddGestuteStartsWith(self, gesture, action, args) + +#At the moment, the mouse button can only be set at startup. +#I could use UnBind, but this may limit the wxPython version being used, +#and, what if the user has other events bound? +#So I think, if the user wants to change the mouse button at runtime, +#the best solution is to use manual mode. + + +import wx + +class MouseGestures: + def __init__(self, parent, Auto=True, MouseButton=wx.MOUSE_BTN_MIDDLE): + self.parent = parent + + self.gestures = [] + self.actions = [] + self.actionarguments = [] + + self.mousebutton = MouseButton + self.modifiers = 0 + + self.recording = False + + self.lastposition = (-1, -1) + + self.pen = wx.Pen(wx.Colour(0, 144, 255), 5) + + self.dc = wx.ScreenDC() + self.dc.SetPen(self.pen) + + self.showgesture = False + + self.wobbletolerance = 7 + + self.rawgesture = '' + + self.SetAuto(Auto) + + def Action(self, gesture): + if gesture in self.gestures: + i = self.gestures.index(gesture) + apply(self.actions[i], self.actionarguments[i]) + + def AddGesture(self, gesture, action, *args): + #Make Sure not a duplicate: + self.RemoveGesture(gesture) + + self.gestures.append(gesture) + self.actions.append(action) + self.actionarguments.append(args) + + def End(self): + self.recording = False + + #Figure out the gestures (Look for occurances of 5 in a row or more): + + tempstring = '0' + possiblechange = '0' + + directions = '' + + for g in self.rawgesture: + l = len(tempstring) + if g != tempstring[l - 1]: + if g == possiblechange: + tempstring = g + g + else: + possiblechange = g + else: + tempstring += g + if len(tempstring) >= self.wobbletolerance: + ld = len(directions) + if ld > 0: + if directions[ld - 1] != g: + directions += g + else: + directions += g + tempstring = '0' + + return directions + + def GetDirection(self, point1, point2): + #point1 is the old point + #point2 is current + + x1, y1 = point1 + x2, y2 = point2 + + #(Negative = Left, Up) + #(Positive = Right, Down) + + horizontal = x2 - x1 + vertical = y2 - y1 + + horizontalchange = abs(horizontal) > 0 + verticalchange = abs(vertical) > 0 + + if horizontalchange and verticalchange: + ah = abs(horizontal) + av = abs(vertical) + if ah > av: + if (ah / av) > 2: + vertical = 0 + verticalchange = False + elif av > ah: + if (av / ah) > 2: + horizontal = 0 + horizontalchange = False + + if horizontalchange and verticalchange: + #Diagonal + if (horizontal > 0) and (vertical > 0): + return '3' + elif (horizontal > 0) and (vertical < 0): + return '9' + elif (horizontal < 0) and (vertical > 0): + return '1' + else: + return '7' + else: + #Straight Line + if horizontalchange: + if horizontal > 0: + return 'R' + else: + return 'L' + else: + if vertical > 0: + return 'D' + else: + return 'U' + + + def OnEnd(self, event): + result = self.End() + + self.Action(result) + + event.Skip() + + def OnMotion(self, event): + if self.recording: + currentposition = event.GetPosition() + if self.lastposition != (-1, -1): + self.rawgesture += self.GetDirection(self.lastposition, currentposition) + if self.showgesture: + #Draw it! + px1, py1 = self.parent.ClientToScreen(self.lastposition) + px2, py2 = self.parent.ClientToScreen(currentposition) + + self.dc.DrawLine(px1, py1, px2, py2) + + self.lastposition = currentposition + + event.Skip() + + def OnStart(self, event): + self.Start() + event.Skip() + + def RemoveGesture(self, gesture): + if gesture in self.gestures: + i = self.gestures.index(gesture) + del self.gestures[i] + del self.actions[i] + del self.actionarguments[i] + + def SetAuto(self, auto): + #I was not sure about making this part of init, so I left it as its own method for now. + if auto: + if self.mousebutton == wx.MOUSE_BTN_LEFT: + self.parent.Bind(wx.EVT_LEFT_DOWN, self.OnStart) + self.parent.Bind(wx.EVT_LEFT_UP, self.OnEnd) + elif self.mousebutton == wx.MOUSE_BTN_MIDDLE: + self.parent.Bind(wx.EVT_MIDDLE_DOWN, self.OnStart) + self.parent.Bind(wx.EVT_MIDDLE_UP, self.OnEnd) + elif self.mousebutton == wx.MOUSE_BTN_RIGHT: + self.parent.Bind(wx.EVT_RIGHT_DOWN, self.OnStart) + self.parent.Bind(wx.EVT_RIGHT_UP, self.OnEnd) + self.parent.Bind(wx.EVT_MOTION, self.OnMotion) + + def SetGesturesVisible(self, vis): + self.showgesture = vis + + def SetModifiers(self, modifers): + self.modifiers = modifiers + + def SetWobbleTolerance(self, wobbletolerance): + self.WobbleTolerance = wobbletolerance + + def Start(self): + self.recording = True + self.rawgesture = '' + self.lastposition = (-1, -1) + if self.showgesture: + self.parent.Refresh() \ No newline at end of file -- 2.45.2