Kivy Touch Events
Deep dive · part of Python Kivy
Kivy is multi-touch first. Override on_touch_down, on_touch_move and on_touch_up on a widget to react to gestures. Each touch carries position, id and pressure (where supported).
Kivy targets multi-touch hardware: on_touch_down, on_touch_move, on_touch_up receive MotionEvent objects with pos, id, and pressure. Widgets return True to claim the touch so parents do not also handle it.
Coordinate system origin is bottom-left unlike tkinter top-left—convert mentally when porting algorithms.
Production code combines this topic with logging, tests, and clear module boundaries so refactors stay safe when requirements grow.
touch.uid distinguishes simultaneous fingers on multitouch tables.
collide_point(x, y) hit-tests widgets in complex UIs.
Canvas instructions (Ellipse, Line) draw in widget coordinates updated on touch_move.
Multitouch on desktop emulates mouse; test on real device for gestures.
Clock.schedule_once defers UI updates safely from non-main threads.
App.build returns root widget receiving dispatch if children do not grab.
Practice explaining kivy touch events aloud with a concrete example from your current project so the abstraction sticks beyond copy-paste exercises.
Gestures (pinch, rotate) compose from tracking multiple uids over time. For games, consider pymunk physics updating on Clock.schedule_interval.
KV language can bind touch handlers declaratively; Python overrides offer finer control.
Read touch.pressure when drivers support stylus input; default to 1.0 on desktop so drawing code stays unified.
Normalize coordinates to 0–1 when supporting multiple display densities and aspect ratios.
Read the parent tutorial on pythondeck.com for runnable snippets, then reproduce them locally in a virtual environment with pinned dependency versions matching your deployment target.
When pairing with teammates, agree on one idiomatic pattern per concern—mixed styles in one repo slow reviews and invite subtle integration bugs during merges.
Not returning True from on_touch_down so moves never arrive.
Assuming y increases downward like HTML canvas.
Heavy work in touch handlers blocking 60fps animations.
Creating new Canvas instructions every frame without clearing.
Normalize coordinates to 0-1 if supporting multiple screen sizes.
Use Scatter or RelativeLayout for draggable components.
Test multitouch on cheapest target hardware early.
Separate input handling class from drawing code for clarity.
Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.
The program below demonstrates drag a circle. Read the comments on each line, run the code, then change names or values to see how the output shifts.
# Example: Drag a circle
# Run in the REPL or save as a .py file and execute with python.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Ellipse
class Pad(Widget):
def __init__(self, **kw):
super().__init__(**kw)
with self.canvas:
Color(0.16, 0.29, 0.45, 1)
self.dot = Ellipse(pos=(100, 100), size=(60, 60))
def on_touch_move(self, t):
self.dot.pos = (t.x - 30, t.y - 30)
class Demo(App):
def build(self): return Pad()
Demo().run()
This sample walks through touch move in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.
# Override on_touch_move to follow finger/mouse
from kivy.uix.widget import Widget # base widget
from kivy.graphics import Color, Ellipse # drawing primitives
class Pad(Widget): # drawable pad
def __init__(self, **kw): # init
super().__init__(**kw) # base init
with self.canvas: # draw list
Color(0.2, 0.4, 0.7, 1) # blue
self.dot = Ellipse(pos=(50, 50), size=(40, 40)) # circle
def on_touch_move(self, touch): # drag handler
self.dot.pos = (touch.x - 20, touch.y - 20) # center on touch
return True # claim touch
print(Pad()) # widget constructed
Here is a hands-on illustration of multi touch. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.
# touch.uid distinguishes concurrent touches
from kivy.uix.widget import Widget # widget
class LogTouches(Widget): # logger
def on_touch_down(self, touch): # begin
print("down", touch.uid, round(touch.x, 1), round(touch.y, 1)) # log
return True # accept
def on_touch_up(self, touch): # end
print("up", touch.uid) # release id
return True # accept
print(LogTouches()) # ready
Related deep dives on Python Kivy: