Skip to main content

Native desktop GUI framework for Python, powered by iced

Project description

plushie

Build native desktop apps in Python. Pre-1.0

Plushie is a desktop GUI framework that allows you to write your entire application in Python -- state, events, UI -- and get native windows on Linux, macOS, and Windows. Rendering is powered by iced, a cross-platform GUI library for Rust, which plushie drives as a precompiled binary behind the scenes.

from dataclasses import dataclass, replace

import plushie
from plushie import ui
from plushie.events import Click


@dataclass(frozen=True, slots=True)
class Model:
    count: int = 0


class Counter(plushie.App[Model]):
    def init(self):
        return Model()

    def update(self, model, event):
        match event:
            case Click(id="inc"):
                return replace(model, count=model.count + 1)
            case Click(id="dec"):
                return replace(model, count=model.count - 1)
            case _:
                return model

    def view(self, model):
        return ui.window(
            "main",
            ui.column(
                ui.text("count", f"Count: {model.count}"),
                ui.row(
                    ui.button("inc", "+"),
                    ui.button("dec", "-"),
                    spacing=8,
                ),
                padding=16,
                spacing=8,
            ),
            title="Counter",
        )


if __name__ == "__main__":
    plushie.run(Counter)
python -m plushie run counter:Counter

This is one of 9 examples included in the repo, from a minimal counter to a full widget catalog. Edit them while the GUI is running and see changes instantly. For complete project demos, including native Rust extensions, see the plushie-demos repository.

Getting started

Install plushie:

pip install plushie

Then:

python -m plushie download              # download precompiled binary
python -m plushie run counter:Counter   # run the counter example

Pin to an exact version and read the CHANGELOG carefully when upgrading.

The precompiled binary requires no Rust toolchain. To build from source instead, install rustup and run python -m plushie build. See the getting started guide for the full walkthrough.

Features

  • 38 built-in widget types -- buttons, text inputs, sliders, tables, markdown, canvas, and more. Easy to build your own. Layout guide
  • 22 built-in themes -- light, dark, dracula, nord, solarized, gruvbox, catppuccin, tokyo night, kanagawa, and more. Custom palettes and per-widget style overrides. Theming guide
  • Multi-window -- declare window nodes in your widget tree; the framework opens, closes, and manages them automatically. App guide
  • Platform effects -- native file dialogs, clipboard, OS notifications. Effects guide
  • Accessibility -- screen reader support via accesskit on all platforms. Accessibility guide
  • Live reload -- edit code, see changes instantly. Use python -m plushie run myapp:App --watch.
  • Extensions -- multiple paths to custom widgets:
    • Compose existing widgets into higher-level components with pure Python. No Rust, no binary rebuild.
    • Draw on the canvas with shape primitives for charts, gauges, diagrams, and other custom 2D rendering.
    • Native -- implement WidgetExtension in Rust for full control over rendering, state, and event handling.
    • Extensions guide
  • Remote rendering -- native desktop UI for apps running on servers or embedded devices. Dashboards, admin tools, IoT diagnostics -- over SSH with configurable event throttling. Running guide

Testing

Plushie ships a test framework with three interchangeable backends. Write your tests once, run them at whatever fidelity you need:

  • Mock -- millisecond tests, no display server. Uses a shared mock process for fast logic and interaction testing.
  • Headless -- real rendering via tiny-skia, no display server needed. Supports screenshots for pixel regression in CI.
  • Windowed -- real windows with GPU rendering. Platform effects, real input, the works.
import pytest
from plushie.testing import AppFixture
from todo import TodoApp, Model


@pytest.fixture
def app(plushie_pool):
    with AppFixture(TodoApp, plushie_pool) as f:
        yield f


def test_add_and_complete_todo(app):
    app.type_text("#new-todo", "Buy milk")
    app.submit("#new-todo")

    app.assert_exists("#todo-1")
    assert app.model.items[0].text == "Buy milk"

    app.toggle("#todo-1/done")
    assert app.model.items[0].done is True

    app.select("#filter", "done")
    app.assert_not_exists("#todo-1")
pytest                                    # mock (fast, no display)
PLUSHIE_TEST_BACKEND=headless pytest      # real rendering, no display
PLUSHIE_TEST_BACKEND=windowed pytest      # real windows (needs display)

See the testing guide for the full API, backend details, and CI configuration.

How it works

Under the hood, a renderer built on iced handles window drawing and platform integration. Your Python code sends widget trees to the renderer over stdin; the renderer draws native windows and sends user events back over stdout.

You don't need Rust to use plushie. The renderer is a precompiled binary, similar to how your app talks to a database without you writing C. If you ever need custom native rendering, the extension system lets you write Rust for just those parts.

The same protocol works over a local pipe, an SSH connection, or any bidirectional byte stream -- your code doesn't need to change. See the running guide for deployment options.

Status

Pre-1.0. The core works -- 38 widget types, event system, 22 themes, multi-window, testing framework, accessibility -- but the API is still evolving:

  • Pin to an exact version and read the CHANGELOG when upgrading.
  • The extension system is the least stable part of the API.

Documentation

Guides are in docs/:

Development

python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
python -m plushie download              # renderer binary for test suite
./preflight                              # run all CI checks locally

Use a local .venv; pyright is configured to resolve types from it. ./preflight mirrors CI and stops on first failure: format, lint, type check, test, doc verification, and docs build.

System requirements

The precompiled binary (python -m plushie download) has no additional dependencies beyond Python 3.12+. To build from source, install a Rust toolchain via rustup and the platform-specific libraries:

  • Linux (Debian/Ubuntu): sudo apt-get install libxkbcommon-dev libwayland-dev libx11-dev cmake fontconfig pkg-config
  • Linux (Arch): sudo pacman -S libxkbcommon wayland libx11 cmake fontconfig pkgconf
  • macOS: xcode-select --install
  • Windows: Visual Studio Build Tools with "Desktop development with C++"

Links

Python SDK github.com/plushie-ui/plushie-python
Elixir SDK github.com/plushie-ui/plushie-elixir
Renderer github.com/plushie-ui/plushie-renderer
Demo projects github.com/plushie-ui/plushie-demos

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

plushie-0.5.0.tar.gz (326.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

plushie-0.5.0-py3-none-any.whl (133.5 kB view details)

Uploaded Python 3

File details

Details for the file plushie-0.5.0.tar.gz.

File metadata

  • Download URL: plushie-0.5.0.tar.gz
  • Upload date:
  • Size: 326.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for plushie-0.5.0.tar.gz
Algorithm Hash digest
SHA256 fb9879da9d985df5e288524491a773297b07dd7f6c70eb67e7a24b2fd71adbc1
MD5 a34d043bc2e51710c5881471d3242f8c
BLAKE2b-256 20c39a56bcdf29075c5b54a8cbe71973cc3bf29bb059dc1ef11e8734c7c543e1

See more details on using hashes here.

File details

Details for the file plushie-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: plushie-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 133.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for plushie-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f4d600120fc1e3bbbf8229aaa9c3582c679eeed082a92e52b90b7e26b8306e42
MD5 ce661561acbdef95df1a5a69dc9e0c23
BLAKE2b-256 68796bc56264e506c3e04a75ab8feb8924432b0017147926c1eca0e1dd82b64a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page