1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
"""
Tool to detect when the user hovers over a specific part of an underlying
components.
"""
# Enthought library imports
from enable.base_tool import BaseTool
from traits.etsconfig.api import ETSConfig
from pyface.toolkit import toolkit_object
from traits.api import Any, Callable, Enum, Float, Int
# Define a toolkit-specific function for determining the global mouse position
if ETSConfig.toolkit == 'wx':
import wx
def GetGlobalMousePosition():
pos = wx.GetMousePosition()
if isinstance(pos, tuple):
return pos
elif hasattr(pos, "x") and hasattr(pos, "y"):
return (pos.x, pos.y)
else:
raise RuntimeError("Unable to determine mouse position")
elif ETSConfig.toolkit == 'qt4':
from pyface.qt import QtGui
def GetGlobalMousePosition():
pos = QtGui.QCursor.pos()
return (pos.x(), pos.y())
else:
def GetGlobalMousePosition():
raise NotImplementedError, "GetGlobalMousePosition is not defined for" \
"toolkit '%s'." % ETSConfig.toolkit
class HoverTool(BaseTool):
"""
Tool to detect when the user hovers over a certain area on a component.
The type of area to detect can be configured by the 'area_type' and 'bounds'
traits.
Users of the class should either set the 'callback' attribute, or
subclass and override on_hover().
"""
# Defines the part of the component that the hover tool will listen
area_type = Enum("top", "bottom", "left", "right", "borders", # borders
"UL", "UR", "LL", "LR", "corners") # corners
# The width/height of the border or corner area. (Corners are assumed to
# be square.)
area = Float(35.0)
# The number of milliseconds that the user has to keep the mouse within
# the hover threshold before a hover is triggered.
hover_delay = Int(500)
# Controls the mouse sensitivity; if the mouse moves less than the
# threshold amount in X or Y, then the hover timer is not reset.
hover_threshold = Int(5)
# The action to perform when the hover activates. This can be used
# instead of subclassing and overriding on_hover().
# If cb_param is not None, then the callback gets passed it as
# its single parameter.
callback = Callable
# An optional parameter that gets passed to the callback.
cb_param = Any
#-------------------------------------------------------------------------
# Private traits
#-------------------------------------------------------------------------
# A tuple (x,y) of the mouse position (in global coordinate) when we set the timer
_start_xy = Any
# The timer
_timer = Any
#-------------------------------------------------------------------------
# Public methods
#-------------------------------------------------------------------------
def on_hover(self):
""" This gets called when all the conditions of the hover action have
been met, and the tool determines that the mouse is, in fact, hovering
over a target region on the component.
By default, this method call self.callback (if one is configured).
"""
if self.callback is not None:
if self.cb_param is not None:
self.callback(self.cb_param)
else:
self.callback()
def normal_mouse_move(self, event):
if self._is_in(event.x, event.y):
# update xy and restart the timer
self._start_xy = GetGlobalMousePosition()
self.restart_hover_timer(event)
else:
if self._timer:
self._timer.Stop()
def restart_hover_timer(self, event):
if self._timer is None:
self._create_timer(event)
else:
self._timer.Start()
def on_timer(self):
position = GetGlobalMousePosition()
diffx = abs(position[0] - self._start_xy[0])
diffy = abs(position[1] - self._start_xy[1])
if (diffx < self.hover_threshold) and (diffy < self.hover_threshold):
self.on_hover()
self._timer.Stop()
#-------------------------------------------------------------------------
# Private methods
#-------------------------------------------------------------------------
def _is_in(self, x, y):
""" Returns True if local coordinates (x,y) is inside our hover region """
area_type = self.area_type.lower()
c = self.component
t = (c.y2 - y <= self.area)
b = (y - c.y <= self.area)
r = (c.x2 - x <= self.area)
l = (x - c.x <= self.area)
if area_type in ("top", "bottom", "left", "right"):
return locals()[area_type[0]]
elif area_type.lower() in ("ul", "ur", "ll", "lr"):
u = t
return locals()[area_type[0]] and locals()[area_type[1]]
elif area_type == "corners":
return (t | b) & (l | r)
elif area_type == "borders":
return any((t, b, r, l))
def _create_timer(self, event):
klass = toolkit_object("timer.timer:Timer")
self._timer = klass(self.hover_delay, self.on_timer)
|