Table Of Contents
Hover Behavior¶
The HoverBehavior
mixin class provides hover detection
and event handling for Kivy widgets. You can combine this class with other
widgets to add specialized hover events (on_hover_enter, on_hover_update,
on_hover_leave) and reactive hover state tracking.
For an overview of behaviors, please refer to the behaviors
documentation.
Examples¶
Basic hover with visual feedback:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.behaviors.hover import HoverBehavior
class HoverButton(HoverBehavior, ButtonBehavior, Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.text = 'Hover me!'
def on_hovered(self, instance, is_hovered):
# Visual feedback: red when hovered, white otherwise
self.color = [1, 0, 0, 1] if is_hovered else [1, 1, 1, 1]
def on_hover_enter(self, motion_event):
print(f"Mouse entered at {motion_event.pos}")
def on_hover_leave(self, motion_event):
print(f"Mouse left at {motion_event.pos}")
class SampleApp(App):
def build(self):
return HoverButton()
SampleApp().run()
Using with ScrollView and collision filtering:
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.behaviors.hover import HoverBehavior
from kivy.uix.behaviors.motion import MotionCollideBehavior
class HoverScrollView(MotionCollideBehavior, ScrollView):
# Filters hover events outside stencil bounds
pass
class HoverItem(HoverBehavior, MotionCollideBehavior, Label):
def on_hovered(self, instance, is_hovered):
self.color = [1, 0, 0, 1] if is_hovered else [1, 1, 1, 1]
# Now only visible items receive hover events
# Items scrolled out of view are automatically ignored
Blocking hover leak-through to background widgets:
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors.motion import MotionBlockBehavior
class OpaqueButton(MotionBlockBehavior, Button):
# Blocks all unregistered motion events from passing through
pass
layout = FloatLayout()
layout.add_widget(HoverScrollView()) # Background
layout.add_widget(OpaqueButton()) # Foreground
# Hovering over button won't trigger ScrollView hover
Configuring hover update behavior:
from kivy.uix.behaviors.hover import HoverBehavior
# Disable hover re-dispatching for stationary mouse
HoverBehavior.set_hover_update_interval(-1)
# Re-dispatch once after 0.1s (max 10 updates per second)
HoverBehavior.set_hover_update_interval(0.1)
# Default: re-dispatch once after 1/30s (max 30 updates per second)
HoverBehavior.set_hover_update_interval(1/30)
See HoverBehavior for details.
- class kivy.uix.behaviors.hover.HoverBehavior(**kwargs)[source]¶
Bases:
builtins.objectMixin to add hover detection and event handling to any Kivy widget.
This mixin enables widgets to respond to mouse hover events with three specialized events:
on_hover_enter(),on_hover_update(), andon_hover_leave(). It also provides a reactivehoveredproperty.Hover State Management¶
Tracks active hover events internally. Each hover event gets a unique ID (supports multi-touch hover). When a hover enters widget bounds, it’s tracked. When it leaves, it’s removed. The
hoveredproperty is True when any hover events are active.Event Propagation Modes¶
“default”: Standard behavior. Events go to children first. If no child accepts, the event is offered to this widget.
“all”: Forces dispatch to both children AND this widget, regardless of whether children accept. Use when parent needs to track hovers even while children handle them (e.g., container with hover effects).
“self”: Skips children entirely. This widget captures all hover events within its bounds. Use for widgets that should be “opaque” to hover (e.g., popup overlays).
Static Hover Re-dispatching¶
By default, hover events are re-dispatched once when the mouse is stationary AND the time since the last dispatch exceeds 1/30s. This enables proper hover state tracking when content moves under a stationary cursor (e.g., scrolling with mousewheel).
All three hover events (enter/update/leave) are affected:
on_hover_enter(): Fires when widget scrolls under static cursoron_hover_update(): Fires once after intervalon_hover_leave(): Fires when widget scrolls away from cursor
Configure globally before creating widgets:
# Disable re-dispatching entirely HoverBehavior.set_hover_update_interval(-1) # Re-dispatch once after 0.1s (max 10 updates per second) HoverBehavior.set_hover_update_interval(0.1) # Default: re-dispatch once after 1/30s (max 30 updates per second) HoverBehavior.set_hover_update_interval(1/30)
Combining with Motion Filters¶
For most use cases, combine
HoverBehaviorwith motion event filters frommotion:Example - ScrollView with Collision Filter:
from kivy.uix.behaviors.motion import MotionCollideBehavior class HoverScrollView(MotionCollideBehavior, ScrollView): # Filters hover outside stencil bounds pass class HoverItem(HoverBehavior, MotionCollideBehavior, Label): # Only receives hover when visible pass
Example - Blocking Hover Leak-Through:
from kivy.uix.behaviors.motion import MotionBlockBehavior class OpaqueButton(MotionBlockBehavior, Button): # Blocks all unregistered motion events pass layout = FloatLayout() layout.add_widget(HoverScrollView()) # Background layout.add_widget(OpaqueButton()) # Foreground # Hovering button won't trigger ScrollView hover
Events¶
on_hover_enter()First collision with widget bounds
on_hover_update()Hover moved within bounds
on_hover_leave()Hover left widget bounds
Internal State¶
For subclassing:
_hover_ids(dict) maps active hover UIDs to positions {uid: (x, y)}. Updated by hover events. Empty when not hovered.- classmethod get_hover_update_interval()[source]¶
Get the current hover re-dispatch interval.
- Returns:
Current interval in seconds, or negative if disabled
Example:
interval = HoverBehavior.get_hover_update_interval() if interval < 0: print("Hover re-dispatching disabled") else: print(f"Re-dispatches every {interval:.3f} seconds")
- hover_mode¶
Controls how hover events propagate through the widget tree.
Modes:
“default”: Standard propagation. Children first, then self if not accepted. Use this for most widgets.
“all”: Force dispatch to both children AND self. Use when parent needs to track hovers even when children handle them (e.g., container with background hover effects).
“self”: Skip children entirely. Capture all hovers within bounds. Use for modal overlays or widgets that should be “opaque” to hover events.
hover_modeis anOptionPropertyand defaults to “default”.
- hovered¶
Read-only boolean indicating if widget is currently hovered.
hoveredis anAliasPropertyand defaults to False.
- on_hover_enter(motion_event)[source]¶
Called when hover first enters widget bounds.
Override this method to respond to hover enter events.
- Parameters:
motion_event –
MotionEvent
- on_hover_leave(motion_event)[source]¶
Called when hover exits widget bounds.
Override this method to respond to hover leave events.
- Parameters:
motion_event –
MotionEvent
- on_hover_update(motion_event)[source]¶
Called when hover moves within widget bounds.
If the mouse doesn’t move AND enough time has passed since the last dispatch, this is called once.
Disable re-dispatching via
set_hover_update_interval(-1)if this behavior is not needed.- Parameters:
motion_event –
MotionEvent
- on_motion(event_type: str, motion_event) bool[source]¶
Handle incoming motion events, filtering for hover events.
Main entry point for motion events. Filters for hover events and handles them according to
hover_mode. Non-hover events pass to parent.- Parameters:
event_type – Type of motion event (“begin”, “update”, “end”)
motion_event –
MotionEventinstance
- Returns:
True if event was handled
- classmethod set_hover_update_interval(interval)[source]¶
Configure hover re-dispatching interval globally.
Controls the minimum time between hover re-dispatches when the mouse is stationary. Re-dispatches once when:
Mouse hasn’t moved, AND
Time since last dispatch exceeds interval
This affects all three hover events (enter/update/leave) and is essential for scenarios where content moves under a static cursor (e.g., scrolling with mousewheel).
When enabled, the system re-checks which widgets should receive hover events, allowing proper enter/leave events as widgets scroll in/out of view.
This configuration applies to all
HoverBehaviorwidgets and can be called before any widgets are instantiated.- Parameters:
interval – Seconds between re-dispatches. Use negative value to disable re-dispatching entirely
Example:
# Disable re-dispatching (no scroll hover updates) HoverBehavior.set_hover_update_interval(-1) # Re-dispatch once after 0.1s (max 10 updates per second) HoverBehavior.set_hover_update_interval(0.1) # Default: re-dispatch once after 1/30s (max 30 updates per second) HoverBehavior.set_hover_update_interval(1/30)
Note
When disabled (negative interval), hover events only fire when the mouse actually moves. Content scrolling under a static cursor will not trigger enter/update/leave events.