Skip to content

Devices and screens

The same app can run on a laptop, a touch panel on a wall, a car’s dashboard, a phone or a TV. They differ in how people use them (a mouse, a finger, a remote), in their screens and in how fast they draw. A device profile tells Pyzed which one it runs on, and Pyzed fills in the window options and rendering settings your app leaves at their defaults.

import pyzed as pz
class Dashboard(pz.App):
title = "Dashboard"
device = "automotive" # fullscreen kiosk, no pointer, arrows move the focus
design_size = (1920, 720) # laid out for this screen, scaled to the real one
def view(self):
...
Profile Used for What it sets
desktop Laptops and desktop computers (the default) Nothing: windows as you declare them.
touch Tablets, touch monitors, convertibles Touch first (pointer: coarse), scale 1.15, 44 px targets.
automotive Car displays, machine panels Fullscreen kiosk, hidden pointer, arrow focus, scale 1.25, 64 px targets, medium quality.
mobile Phones (the default on Android) Touch, fullscreen, no pointer, 48 px targets, medium quality.
kiosk Points of sale, museum and info panels Fullscreen kiosk, touch, scale 1.15, paused background animations.
tv TVs and set-top boxes Fullscreen, no pointer, arrow focus, scale 2, medium quality.

Your own options always win: a profile only fills in what the app’s class leaves alone. A kiosk = False in your App keeps the window closable on a kiosk device.

Pyzed looks, in this order, at:

  1. App.device: a profile name or a pz.Device.
  2. The PYZED_DEVICE environment variable: handy to try a profile on your computer (PYZED_DEVICE=tv python app.py).
  3. device = "..." in [tool.pyzed.platform.<system>] of pyproject.toml (linux, windows, macos, android), then in [tool.pyzed].
  4. The system: mobile on Android, desktop elsewhere.

pz.device() returns the profile in use and pz.set_device("touch") changes it while the app runs.

Define profiles in pyproject.toml. extends names the profile to start from; a table named like a built-in profile changes it:

[tool.pyzed]
device = "dashboard"
[tool.pyzed.platform.windows]
device = "desktop" # the same project on a Windows laptop
[tool.pyzed.device.dashboard]
extends = "automotive"
design_size = [1920, 720]
stretch = "fit"
quality = "low"
[tool.pyzed.device.tv]
scale = 1.5

Or in code: pz.Device("panel", touch=True, kiosk=True, quality="low"), or pz.devices.PROFILES["tv"].replace(scale=1.5).

Field Meaning
touch The screen is touched more than clicked: pz.media("pointer: coarse") is true.
pointer False hides the pointer over the windows.
kiosk, fullscreen The main window opens as a kiosk or fullscreen (see Screens and kiosks).
arrow_focus The arrow keys move the focus (see below).
scale Interface scale of the windows that leave scale at 1.
min_target The smallest comfortable control in logical pixels; read it with pz.device().min_target to size your own components.
design_size, stretch The design resolution (see below).
quality "high", "medium" or "low": rendering defaults for slower hardware.
reduced_motion True turns animations off, None follows the system.
rendering pz.Rendering fields on top of the quality level.

quality fills in the rendering settings the project leaves at "auto":

Quality Antialiasing Frame rate Background windows Other
high 4x the screen’s as focused
medium 2x 60 10 fps
low off 30 paused low GPU memory, low power GPU

Panels and car displays have one known screen, and designers draw for it. design_size lays the view out for that size and scales it to the real window, like Godot’s stretch modes:

  • stretch = "expand" (the default) scales until the design fits and lets the view grow into the space left, so a wider screen shows more.
  • stretch = "fit" scales it the same way and keeps the design’s size, centered, with bars around it.
  • stretch = "none" ignores design_size.

pz.viewport() and media queries see the design’s units, so breakpoints keep working.

Media queries answer questions about the device, in code and in stylesheets:

padding = 20 if pz.media("pointer: coarse") else 8 # bigger on touch screens
pz.stylesheet({
"@(pointer: coarse)": {"button": {"padding": [14, 20]}},
"@(device: tv)": {"text": {"size": 22}},
})

pointer is coarse on touch screens, none where nothing points (TVs) and fine with a mouse.

TV remotes, rotary knobs and steering wheel pads send arrow keys. With App.arrow_focus = True (on by default in the tv and automotive profiles), an arrow no control used moves the focus to the nearest control that way. on_key sees the key first; returning something from it keeps the focus where it is. Command.focus_direction("left") does the same from code.

Controls take the focus with pz.focusable:

pz.focusable(card, self.open, keys=["enter"])

On a car display or a kiosk, run the app as the only program of a kiosk compositor such as cage, started by the system at boot:

Terminal window
PYZED_DEVICE=automotive cage -- python app.py

There is no desktop, no panel and nothing to close; with background_fps = 0 the app uses no CPU while nothing moves.