What is Kivy?
Cross-Platform GUI & Touch Framework
Kivy is a free, open-source Python library for developing multi-touch applications. It runs on macOS, Windows, Linux, Android, and iOS — write once, deploy everywhere.
Kivy is not a "native" UI toolkit (its widgets look the same on every platform, rendered via OpenGL ES 2). This makes it ideal for apps that need a custom look, games, or mobile targets. It's less suited to apps that must blend into the OS's native aesthetic.
| Feature | Kivy |
|---|---|
| Targets | macOS, Windows, Linux, Android, iOS |
| Rendering | OpenGL ES 2 (GPU accelerated) |
| Touch/Gesture | First-class support |
| License | MIT |
| Layout language | KV Language (declarative) |
Custom-styled apps, games, cross-platform mobile/desktop apps, touch-based UIs.
Installing Kivy on macOS
Kivy on macOS works best inside a virtual environment. Follow these steps:
- Install Python 3.11+ from python.org (or via Homebrew:
brew install python). - Create and activate a virtual environment:
python3 -m venv kivyenv && source kivyenv/bin/activate - Upgrade pip:
pip install --upgrade pip - Install Kivy with all dependencies:
pip install "kivy[base]" - Verify:
python -c "import kivy; print(kivy.__version__)"
On Apple Silicon (M1/M2/M3) Macs, make sure your Python is the ARM-native version from python.org — not the Rosetta one — to avoid OpenGL issues.
Optional: Install SDL2 via Homebrew
If you encounter display errors, install the system SDL2 library first:
brew install sdl2 sdl2_image sdl2_ttf sdl2_mixer
Hello, Kivy World!
Every Kivy app inherits from App and implements a build() method that returns the root widget.
from kivy.app import App from kivy.uix.label import Label class HelloApp(App): def build(self): # Return the root widget — a simple Label return Label(text='Hello, Kivy on macOS!') if __name__ == '__main__': HelloApp().run()
Run it from your terminal (with the venv active):
python hello.py
A black window with white centred text will appear — you've written your first Kivy app!
Kivy strips "App" from your class name to produce the window title. HelloApp → title becomes "Hello".
Widgets — The Building Blocks
Everything visible in a Kivy app is a Widget. Common ones include:
| Widget | Import path | Purpose |
|---|---|---|
Label | kivy.uix.label | Display text |
Button | kivy.uix.button | Clickable button |
TextInput | kivy.uix.textinput | User text entry |
Image | kivy.uix.image | Display images |
CheckBox | kivy.uix.checkbox | Toggle checkbox |
Slider | kivy.uix.slider | Value slider |
ToggleButton | kivy.uix.togglebutton | On/off toggle |
from kivy.app import App from kivy.uix.button import Button class BtnApp(App): def build(self): btn = Button( text='Click me!', font_size=24, background_color=(0.2, 0.6, 1, 1) # R,G,B,A floats ) btn.bind(on_press=self.on_btn_press) return btn def on_btn_press(self, instance): instance.text = 'Pressed!' BtnApp().run()
Layouts — Arranging Widgets
Widgets need a Layout container to position them on screen. Kivy ships with several:
| Layout | Behaviour |
|---|---|
BoxLayout | Stack widgets horizontally or vertically |
GridLayout | Fixed rows × columns grid |
FloatLayout | Absolute or relative position/size |
AnchorLayout | Anchor child to a corner or edge |
StackLayout | Like CSS flexbox wrap |
from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.label import Label class LayoutApp(App): def build(self): layout = BoxLayout(orientation='vertical', padding=20, spacing=10) layout.add_widget(Label(text='Name:', size_hint_y=None, height=40)) layout.add_widget(Button(text='Submit')) return layout LayoutApp().run()
size_hint is a 0–1 fraction of the parent's size. Set size_hint_y=None and height=40 for a fixed-pixel height widget.
KV Language — Declarative UI
Defining widgets in Python quickly becomes verbose. KV Language is Kivy's built-in declarative language — like YAML meets CSS for your UI structure.
Create myapp.kv alongside your Python file (Kivy loads it automatically based on your App class name):
BoxLayout: orientation: 'vertical' padding: 20 spacing: 12 Label: text: 'Enter your name:' font_size: 18 TextInput: id: name_input hint_text: 'Type here...' size_hint_y: None height: 44 Button: text: 'Greet' size_hint_y: None height: 50 on_press: app.greet(name_input.text)
from kivy.app import App class MyApp(App): def greet(self, name): print(f'Hello, {name}!') MyApp().run()
You can also embed KV strings directly with Builder.load_string() — great for single-file apps.
Events & Property Binding
Kivy uses a reactive property system. When a property changes, bound callbacks fire automatically — no manual event listeners needed in most cases.
from kivy.app import App from kivy.uix.slider import Slider from kivy.uix.label import Label from kivy.uix.boxlayout import BoxLayout class SliderApp(App): def build(self): box = BoxLayout(orientation='vertical', padding=30) self.label = Label(text='Value: 50', font_size=22) slider = Slider(min=0, max=100, value=50) # Bind the 'value' property — fires on any change slider.bind(value=self.on_slider_change) box.add_widget(self.label) box.add_widget(slider) return box def on_slider_change(self, instance, value): self.label.text = f'Value: {value:.0f}' SliderApp().run()
Common Event Names
| Event | Fired when… |
|---|---|
on_press | Button pressed down |
on_release | Button released |
on_text | TextInput text changes |
on_touch_down | Touch/click begins on widget |
on_value | Slider value changes |
Screen Manager — Multi-Screen Apps
Use ScreenManager to build apps with multiple pages (like a mobile app's navigation stack).
from kivy.app import App from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition from kivy.uix.button import Button class HomeScreen(Screen): def __init__(self, **kwargs): super().__init__(**kwargs) btn = Button(text='Go to Settings →') btn.bind(on_press=lambda _: self.manager_goto('settings')) self.add_widget(btn) def manager_goto(self, screen): self.manager.current = screen class SettingsScreen(Screen): def __init__(self, **kwargs): super().__init__(**kwargs) btn = Button(text='← Back Home') btn.bind(on_press=lambda _: setattr(self.manager, 'current', 'home')) self.add_widget(btn) class NavApp(App): def build(self): sm = ScreenManager(transition=SlideTransition()) sm.add_widget(HomeScreen(name='home')) sm.add_widget(SettingsScreen(name='settings')) return sm NavApp().run()
Try FadeTransition, WipeTransition, or NoTransition instead of SlideTransition for different navigation feels.
Canvas & Custom Drawing
Every Kivy widget has a canvas you can draw on using OpenGL-based drawing instructions like Rectangle, Ellipse, Line, and Color.
from kivy.app import App from kivy.uix.widget import Widget from kivy.graphics import Color, Ellipse, Rectangle class PaintWidget(Widget): def on_touch_down(self, touch): with self.canvas: Color(0, 0.9, 0.6, 1) # Kivy green d = 30 Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d)) class PaintApp(App): def build(self): return PaintWidget() PaintApp().run()
Click/tap anywhere in the window and green dots will appear — a mini paint app in 15 lines!
Capstone — A Temperature Converter
Let's put it all together: layouts, KV language, TextInput, Labels, and event binding into a real, usable app.
from kivy.app import App from kivy.lang import Builder KV = """ BoxLayout: orientation: 'vertical' padding: 40 spacing: 16 Label: text: 'Temperature Converter' font_size: 26 bold: True size_hint_y: None height: 50 TextInput: id: celsius_input hint_text: 'Enter Celsius' input_filter: 'float' size_hint_y: None height: 48 Button: text: 'Convert to Fahrenheit' size_hint_y: None height: 52 on_press: app.convert(celsius_input.text) Label: id: result_label text: 'Result will appear here' font_size: 20 color: 0, 0.9, 0.6, 1 """ class ConverterApp(App): def build(self): self.root = Builder.load_string(KV) return self.root def convert(self, value): try: c = float(value) f = c * 9/5 + 32 self.root.ids.result_label.text = f'{c:.1f}°C = {f:.1f}°F' except ValueError: self.root.ids.result_label.text = 'Please enter a valid number' ConverterApp().run()
You've installed Kivy, used Widgets, Layouts, KV Language, events, ScreenManager, Canvas drawing, and built a complete app. Next: explore kivy.org docs, Buildozer (for Android packaging), and Kivy Garden for community widgets.