I really needed to make a good one (not a school assignment :) that convers lowlevel mouse events to highlevel ones. When the user left double clicks on a window, the window gets leftdown, leftup, leftdoubleclick, leftup events in this order. It is too low level. In this case all we want is a single double click event.
- A double click event doesn't come with friends.
- Whenever a mouse down event comes, the application can except a following mouse up event (it may be after dragging event stuff or enter/leave window event).
- A series of dragging events are always wrapped by a startdragging and an enddragging event.
- Capture mouse automatically. (See the comment in onButtonDown() method)
- Can set threshold in pixels so that anybody can double click with his trembling hand (e.g. my dad's)
BUG
While dragging, EnterWindow/LeaveWindow occurs when the mouse enter/leaves the parent window on GTK+.
UPDATES
Jul 29: Passing proper event object to mouse down/up handlers.
Jul 31: Get the right mouse position while mouse captured.
Aug 5 : Fixed missing double click detection failure.
Aug 8 : Modified to Register popup menu functions.
"""Low level -> high level mouse event converter.""" import wx NORMAL = 0 FIRSTWAIT = 1 SECONDWAIT = 2 AFTERDCLICK = 3 MOUSEDOWN = 4 DRAGGING = 5 ID_FIRSTTIMER = wx.NewId() ID_SECONDTIMER = wx.NewId() class HighLevMouseEventConv(object): def __init__(self, eventWindow, t1 = 200, t2 = 100, dragThreshPixel = 3): """HighLevelEvent(eventWindow, t1 = 200, t2 = 100, dragThreshPixel = 3) eventWindow: The window that receives mouse events. t1, t2: How long it waits to detect is a mouse down is a double click in mili seconds. dragThreshPixel: Drag moves within this thresh do not produce drag related events. When you want a popup menu associated with the window create a func to show the menu, register it using set(Left, Middle, Right)PopupMenuFunc(). These functions should never return value. """ self.t1 = t1 self.t2 = t2 self.dragThreshPixel = dragThreshPixel self.state = NORMAL self.evwin = eventWindow self.firstTimer = wx.Timer(eventWindow, ID_FIRSTTIMER) self.secondTimer = wx.Timer(eventWindow, ID_SECONDTIMER) dh = self.defaultHandler l, r, m = wx.MOUSE_BTN_LEFT, wx.MOUSE_BTN_RIGHT, wx.MOUSE_BTN_MIDDLE self.onHLVMouseDown = {l:dh, r:dh, m:dh} self.onHLVMouseUp = {l:dh, r:dh, m:dh} self.onHLVMouseDClick = {l:dh, r:dh, m:dh} self.onHLVMouseStartDragging = {l:dh, r:dh, m:dh} self.onHLVMouseDragging = {l:dh, r:dh, m:dh} self.onHLVMouseEndDragging = {l:dh, r:dh, m:dh} self.popupMenuFunc = {l:dh, r:dh, m:dh} self.onHLVMotion = dh self.onHLVEnterWindow = dh self.onHLVLeaveWindow = dh eventWindow.Bind(wx.EVT_MOUSE_EVENTS, self.onMouseEvents) eventWindow.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.onMouseCaptureLost) eventWindow.Bind(wx.EVT_TIMER, self.onFirstTimer, id = ID_FIRSTTIMER) eventWindow.Bind(wx.EVT_TIMER, self.onSecondTimer, id = ID_SECONDTIMER) def setOnHLVLeftDown(self, handler): self.onHLVMouseDown[wx.MOUSE_BTN_LEFT] = handler def setOnHLVLeftUp(self, handler): self.onHLVMouseUp[wx.MOUSE_BTN_LEFT] = handler def setOnHLVLeftDClick(self, handler): self.onHLVMouseDClick[wx.MOUSE_BTN_LEFT] = handler def setOnHLVLeftStartDragging(self, handler): self.onHLVMouseStartDragging[wx.MOUSE_BTN_LEFT] = handler def setOnHLVLeftDragging(self, handler): self.onHLVMouseDragging[wx.MOUSE_BTN_LEFT] = handler def setOnHLVLeftEndDragging(self, handler): self.onHLVMouseEndDragging[wx.MOUSE_BTN_LEFT] = handler def setOnHLVMiddleDown(self, handler): self.onHLVMouseDown[wx.MOUSE_BTN_MIDDLE] = handler def setOnHLVMiddleUp(self, handler): self.onHLVMouseUp[wx.MOUSE_BTN_MIDDLE] = handler def setOnHLVMiddleDClick(self, handler): self.onHLVMouseDClick[wx.MOUSE_BTN_MIDDLE] = handler def setOnHLVMiddleStartDragging(self, handler): self.onHLVMouseStartDragging[wx.MOUSE_BTN_MIDDLE] = handler def setOnHLVMiddleDragging(self, handler): self.onHLVMouseDragging[wx.MOUSE_BTN_MIDDLE] = handler def setOnHLVMiddleEndDragging(self, handler): self.onHLVMouseEndDragging[wx.MOUSE_BTN_MIDDLE] = handler def setOnHLVRightDown(self, handler): self.onHLVMouseDown[wx.MOUSE_BTN_RIGHT] = handler def setOnHLVRightUp(self, handler): self.onHLVMouseUp[wx.MOUSE_BTN_RIGHT] = handler def setOnHLVRightDClick(self, handler): self.onHLVMouseDClick[wx.MOUSE_BTN_RIGHT] = handler def setOnHLVRightStartDragging(self, handler): self.onHLVMouseStartDragging[wx.MOUSE_BTN_RIGHT] = handler def setOnHLVRightDragging(self, handler): self.onHLVMouseDragging[wx.MOUSE_BTN_RIGHT] = handler def setOnHLVRightEndDragging(self, handler): self.onHLVMouseEndDragging[wx.MOUSE_BTN_RIGHT] = handler def setOnHLVMotion(self, handler): self.onHLVMotion = handler def setOnHLVEnterWindow(self, handler): """It can detect the mouse enter/leaves from, to the parent window when the mouse is dragging. There's no clear way but detecting mouse motion event to enter/leave the window itself while the mouse is captured to the parent""" self.onHLVEnterWindow = handler def setOnHLVLeaveWindow(self, handler): """See setOnHLVLeaveWindow doc.""" self.onHLVLeaveWindow = handler #popupMenu def setLeftPopupMenuFunc(self, func): self.popupMenuFunc[wx.MOUSE_BTN_LEFT] = func def setMiddlePopupMenuFunc(self, func): self.popupMenuFunc[wx.MOUSE_BTN_MIDDLE] = func def setRightPopupMenuFunc(self, func): self.popupMenuFunc[wx.MOUSE_BTN_RIGHT] = func def defaultHandler(self, ev): return True def onButtonDown(self, ev): if not self.popupMenuFunc[ev.GetButton()](ev): return if self.state == AFTERDCLICK: self.state = NORMAL self.evwin.CaptureMouse() #On GTK, I knoticed if the window is a control, its parent gets mouse events when the window #captures mouse events with CaptureMouse(). To call ReleaseMouse() properly this object receives #the mouse leftup events sent to the parent window temporarily. parent = self.evwin.GetParent() if parent: parent.Bind(wx.EVT_LEFT_UP, self.onParentButtonUp) parent.Bind(wx.EVT_MIDDLE_UP, self.onParentButtonUp) parent.Bind(wx.EVT_RIGHT_UP, self.onParentButtonUp) parent.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.onMouseCaptureLost) parent.Bind(wx.EVT_MOTION, self.onParentMouseEvents) parent.Bind(wx.EVT_ENTER_WINDOW, self.onParentEntering) parent.Bind(wx.EVT_LEAVE_WINDOW, self.onParentLeaving) self.secondTimer.Stop() state = self.state if state == NORMAL: self.button = ev.GetButton() self.state = FIRSTWAIT self.posx, self.posy = ev.GetPosition() self.downevattrs = self._getEventAttrs(ev) self.firstTimer.Start(self.t1, True) elif state == SECONDWAIT: self.state = AFTERDCLICK self.onHLVMouseDClick[self.button](ev) def onButtonUp(self, ev): evwin = self.evwin if evwin.HasCapture(): evwin.ReleaseMouse() parent = evwin.GetParent() if parent: parent.Unbind(wx.EVT_LEFT_UP) parent.Unbind(wx.EVT_MIDDLE_UP) parent.Unbind(wx.EVT_RIGHT_UP) parent.Unbind(wx.EVT_MOUSE_CAPTURE_LOST) parent.Unbind(wx.EVT_MOTION) parent.Unbind(wx.EVT_ENTER_WINDOW) parent.Unbind(wx.EVT_LEAVE_WINDOW) self.firstTimer.Stop() state = self.state if state == FIRSTWAIT: self.state = SECONDWAIT self.upevattrs = self._getEventAttrs(ev) self.secondTimer.Start(self.t2, True) elif state == AFTERDCLICK: self.state = NORMAL elif state == MOUSEDOWN: self.state = NORMAL self.onHLVMouseUp[self.button](ev) elif state == DRAGGING: self.state = NORMAL self.onHLVMouseEndDragging[self.button](ev) self.onHLVMouseUp[self.button](ev) def onButtonDClick(self, ev): self.secondTimer.Stop() state = self.state if state == NORMAL: self.onButtonDown(ev) elif state == SECONDWAIT: self.state = AFTERDCLICK self.onHLVMouseDClick[self.button](ev) def onMoving(self, ev): if self.state == NORMAL: self.onHLVMotion(ev) def onDragging(self, ev): x, y = ev.GetPosition() t = self.dragThreshPixel state = self.state if state == FIRSTWAIT: if -t < x - self.posx < t and -t < y - self.posy < t: return else: self.state = DRAGGING self.firstTimer.Stop() self.onHLVMouseDown[self.button](ev) self.onHLVMouseStartDragging[self.button](ev) elif state == MOUSEDOWN: if -t < x - self.posx < t and -t < y - self.posy < t: return else: self.state = DRAGGING self.onHLVMouseStartDragging[self.button](ev) elif state == DRAGGING: self.onHLVMouseDragging[self.button](ev) def onEntering(self, ev): self.onHLVEnterWindow(ev) def onLeaving(self, ev): self.onHLVLeaveWindow(ev) def onMouseEvents(self, ev): ev.Skip() if ev.Moving(): return self.onMoving(ev) elif ev.Dragging(): return self.onDragging(ev) elif ev.ButtonDown(): return self.onButtonDown(ev) elif ev.ButtonUp(): return self.onButtonUp(ev) elif ev.ButtonDClick(): return self.onButtonDClick(ev) elif ev.Entering(): return self.onEntering(ev) elif ev.Leaving(): return self.onLeaving(ev) def onMouseCaptureLost(self, ev): self.evwin.ReleaseMouse() ev.Skip() def onFirstTimer(self, ev): self.state = MOUSEDOWN downev = self._createMouseEvent(self.downevattrs) self.onHLVMouseDown[self.button](downev) def onSecondTimer(self, ev): self.state = NORMAL downev = self._createMouseEvent(self.downevattrs) upev = self._createMouseEvent(self.upevattrs) self.onHLVMouseDown[self.button](downev) self.onHLVMouseUp[self.button](upev) def onParentButtonUp(self, ev): self._convetMouseEvent(ev) self.onButtonUp(ev) def onParentMouseEvents(self, ev): self._convetMouseEvent(ev) self.onMouseEvents(ev) def onParentEntering(self, ev): self._convetMouseEvent(ev) self.onEntering(ev) def onParentLeaving(self, ev): self._convetMouseEvent(ev) self.onLeaving(ev) def _convetMouseEvent(self, ev): evwin = self.evwin parent = evwin.GetParent() x, y = ev.GetPosition() offsetx, offsety = evwin.GetPosition() ev.m_x = x - offsetx ev.m_y = y - offsety ev.SetEventObject(evwin) def _getEventAttrs(self, ev): return [ ev.GetEventObject(), ev.GetEventType(), ev.GetId(), ev.GetTimestamp(), ev.m_altDown, ev.m_controlDown, ev.m_leftDown, ev.m_middleDown, ev.m_rightDown, ev.m_metaDown, ev.m_shiftDown, ev.m_x, ev.m_y, ev.m_wheelRotation, ev.m_wheelDelta, ev.m_linesPerAction] def _createMouseEvent(self, eventAttrs): obj, typ, id, time, alt, ctrl, l, m, r, meta, shift, x, y, wr, wd, lpa = eventAttrs ev = wx.MouseEvent(typ) ev.SetEventObject(obj) ev.GetEventObject() ev.SetEventType(typ) ev.SetId(id) ev.SetTimestamp(time) ev.m_altDown = alt ev.m_controlDown = ctrl ev.m_leftDown = l ev.m_middleDown = m ev.m_rightDown = r ev.m_metaDown = meta ev.m_shiftDown = shift ev.m_x = x ev.m_y = y ev.m_wheelRotation = wr ev.m_wheelDelta = wd ev.m_linesPerAction = lpa return ev if __name__ == '__main__': class TestWindow(wx.Button): def __init__(self, parent): super(TestWindow, self).__init__(parent, -1, pos = (50, 50), size = (150, 150)) hlev = HighLevMouseEventConv(self, dragThreshPixel = 15) hlev.setOnHLVLeftDown(self.onHLVLeftDown) hlev.setOnHLVLeftUp(self.onHLVLeftUp) hlev.setOnHLVLeftDClick(self.onHLVLeftDClick) hlev.setOnHLVLeftStartDragging(self.onHLVLeftStartDragging) hlev.setOnHLVLeftDragging(self.onHLVLeftDragging) hlev.setOnHLVLeftEndDragging(self.onHLVLeftEndDragging) hlev.setOnHLVMiddleDown(self.onHLVMiddleDown) hlev.setOnHLVMiddleUp(self.onHLVMiddleUp) hlev.setOnHLVMiddleDClick(self.onHLVMiddleDClick) hlev.setOnHLVMiddleStartDragging(self.onHLVMiddleStartDragging) hlev.setOnHLVMiddleDragging(self.onHLVMiddleDragging) hlev.setOnHLVMiddleEndDragging(self.onHLVMiddleEndDragging) hlev.setOnHLVRightDown(self.onHLVRightDown) hlev.setOnHLVRightUp(self.onHLVRightUp) hlev.setOnHLVRightDClick(self.onHLVRightDClick) hlev.setOnHLVRightStartDragging(self.onHLVRightStartDragging) hlev.setOnHLVRightDragging(self.onHLVRightDragging) hlev.setOnHLVRightEndDragging(self.onHLVRightEndDragging) hlev.setOnHLVMotion(self.onHLVMotion) hlev.setOnHLVEnterWindow(self.onHLVEnterWindow) hlev.setOnHLVLeaveWindow(self.onHLVLeaveWindow) self.lup = True self.dragstate = 'normal' def onHLVLeftDown(self, ev): print 'onHLVLeftDown', ev.GetPosition(), type(ev.GetEventObject()).__name__ if not self.lup: print 'ERROR ' * 5 self.lup = False def onHLVLeftUp(self, ev): print 'onHLVLeftUp', ev.GetPosition(), type(ev.GetEventObject()).__name__ self.lup = True def onHLVLeftDClick(self, ev): print 'onHLVLeftDClick', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVLeftStartDragging(self, ev): print 'onHLVLeftStartDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ if self.dragstate != 'normal': print 'ERROR ' * 5 self.dragstate = 'started' def onHLVLeftDragging(self, ev): print 'onHLVLeftDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ if not self.dragstate in ('started', 'dragging'): print 'ERROR ' * 5 self.dragstate = 'dragging' def onHLVLeftEndDragging(self, ev): print 'onHLVLeftEndDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ if not self.dragstate in ('started', 'dragging'): print 'ERROR ' * 5 self.dragstate = 'normal' def onHLVMiddleDown(self, ev): print 'onHLVMiddleDown', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVMiddleUp(self, ev): print 'onHLVMiddleUp', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVMiddleDClick(self, ev): print 'onHLVMiddleDClick', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVMiddleStartDragging(self, ev): print 'onHLVMiddleStartDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVMiddleDragging(self, ev): print 'onHLVMiddleDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVMiddleEndDragging(self, ev): print 'onHLVMiddleEndDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVRightDown(self, ev): print 'onHLVRightDown', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVRightUp(self, ev): print 'onHLVRightUp', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVRightDClick(self, ev): print 'onHLVRightDClick', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVRightStartDragging(self, ev): print 'onHLVRightStartDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVRightDragging(self, ev): print 'onHLVRightDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVRightEndDragging(self, ev): print 'onHLVRightEndDragging', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVMotion(self, ev): print 'onHLVMotion', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVEnterWindow(self, ev): print 'onHLVEnterWindow', ev.GetPosition(), type(ev.GetEventObject()).__name__ def onHLVLeaveWindow(self, ev): print 'onHLVLeaveWindow', ev.GetPosition(), type(ev.GetEventObject()).__name__ app = wx.App() frame = wx.Frame(None, -1, "Title", size = (300, 400)) panel = wx.Panel(frame, -1) testwin = TestWindow(panel) frame.Show() app.MainLoop()