Skip to main content

What is PUI

PUI is a reactive/declarative UI framework with two-way data binding. PUI doesn't do UI itself, it turns imperative UI libraries into reactive/declarative flavor with virtual DOM and aims to maintain interoperability.

Slides for SciWork 2023

CPPUI: Experimental C++ Version

Features

Installation

PUI requires Python 3.9 or newer. Backend packages have their own Python and platform requirements; the versions available for your interpreter may differ. The shared framework and PySide6 backend retain Python 3.9 support. Shared changes must preserve that baseline even when an optional backend requires newer Python.

Choose a backend extra:

pip install 'QPUIQ[pyside6]'
pip install 'QPUIQ[wx]'
pip install 'QPUIQ[flet]'
pip install 'QPUIQ[textual]'

The flet extra installs the current Flet 1.x desktop/web runtime (minimum tested version: 1.0.4, requiring Python 3.10 or newer). Table support uses its built-in DataTable, including Dropdown choices and dialog editing; no table extension is required.

For tkinter, install QPUIQ and use a Python build with Tk support:

pip install QPUIQ
python -c "import tkinter"

The optional QPUIQ[tkinter] spelling installs the same packages; its extra is empty because Tk is supplied by Python or the operating system. Some Linux distributions provide Tk support separately as python3-tk; an existing virtual environment uses its base Python's native support. wxPython can also require platform-native GTK/build dependencies.

For the framework alone, install QPUIQ. Optional development tools:

pip install jurigged  # Hot reload
pip install pygments # Cookbook syntax highlighting
pip install textual-dev # Textual development tools

When working from this checkout, use the project environment:

python3 -m venv env
env/bin/python -m pip install -e '.[pyside6,flet,textual]'
# Add .[wx] when wxPython is available for the platform.

Backend alignment and checked runtime versions are tracked in wx progress, flet progress, tkinter progress, and Textual plan.

Python 3.9 compatibility

The shared and Qt regression suites pass on Python 3.9.24 with PySide6 6.7.2 on Linux. This covers state/bindings, lifecycle, signatures and legacy decorators, plus Qt windows, controls, tables, trees, scrolling, Tabs, and Views. It does not validate integration with an application's embedded interpreter or other platforms. PySide6 6.7.2 supports Python 3.9–3.12. To use this tested combination on Python 3.9:

python3.9 -m pip install 'QPUIQ[pyside6]' 'PySide6==6.7.2'

To reproduce the compatibility check from a checkout, create a separate runtime under env/ and install only the packages needed for this baseline:

python3.9 -m venv env/python39
env/python39/bin/python -m pip install -e . -r tests/requirements-python39.txt
make check-python39
# Or use an existing environment: make check-python39 PYTHON39=/path/to/python3.9

The test runtime is pinned to PySide6==6.7.2 in tests/requirements-python39.txt, and make check-python39 rejects a different PySide6 version. The check runs the shared and Qt regression suites; the Flet-specific signature test is skipped in this environment. The normal backend extra continues to allow newer versions. Run this check when changing shared code or the Qt backend. Keep Python 3.10-only syntax and APIs, including standard-library ParamSpec, out of code used by the Python 3.9 baseline.

Get Started

Define applications by subclassing the backend's Application, and reusable views by subclassing its PUIView. Put UI declarations in content(), initialize local state in setup(), and use member functions as event handlers. Classes are the recommended API; @PUI and @PUIApp are deprecated and retained for compatibility. See the migration reference.

Hello World

# example/hello_world.py
from PUI.PySide6 import *

class Example(Application):
    def content(self):
        with Window(title="test", size=(320,240)):
            Label("Hello world")

root = Example()
root.run()

Hello World

State & Data Binding

# example/generic_textfield.py
from PUI.PySide6 import *

data = State()
class Example(Application):
    def __init__(self):
        super().__init__()
        data.var = 0

    def content(self):
        with Window(title="blah"):
            with VBox():
                with HBox():
                    Button("-").click(self.on_minus)
                    Label(f"{data.var}")
                    Button("+").click(self.on_plus)

                TextField(data("var")) # binding

    def on_minus(self, event):
        data.var -= 1

    def on_plus(self, event):
        data.var += 1

root = Example()
root.run()

State & Data Binding

View Component

# example/bleak_list.py

....

class DeviceView(PUIView):
    def __init__(self, device, advertising_data):
        super().__init__()
        self.device = device
        self.advertising_data = advertising_data

    def content(self):
        Label(f"{self.device.address} {self.device.name} {self.advertising_data.rssi}")

class GUI(Application):
    def __init__(self, state):
        super().__init__()
        self.state = state

    def content(self):
        with Window(title="BLE List"):
            with VBox():
                Label(f"Found {len(self.state.scanned_devices)} devices")
                for device, advertising_data in self.state.scanned_devices:
                    DeviceView(device, advertising_data)

....

View Component

Layout & Styling

PUI handles normal widget sizing automatically. Each component supplies a preferred size and expansion policy appropriate to the widget and its position in an HBox or VBox; for example, a TextField uses the available horizontal space while retaining its native height, and a Spacer consumes flexible space. Application code normally does not need to assign control dimensions just to make a layout fit.

Use .layout(width=..., height=..., weight=..., padding=..., margin=...) when a design needs an explicit size, a particular share of flexible space, or custom spacing. PUI translates these semantic rules to each backend's native layout system, so exact pixel sizes can still vary with the toolkit, platform, font, and theme.

# example/pyside6_feedparser.py

...
with VBox():
    Label(title).qt(StyleSheet={"font-weight":"bold"}) # QT-specific

    with HBox():
        with Scroll():
            with VBox():
                for i,e in enumerate(entries):
                    Label(e.title).click(self.entry_selected, i)
                Spacer()

        with Scroll().layout(weight=1): # Generic Layout Parameter
            if 0 <= selected and selected < len(entries):
                (Text(entries[selected].description)
                    .layout(padding=10) # Generic Layout Parameter
                    .qt(StyleSheet={"background-color":"white", "color":"black"})) # QT-specific
...

Layout & Styling

Canvas

# example/generic_canvas.py

from PUI.PySide6 import *

data = State()
class Example(Application):
    def __init__(self):
        super().__init__()
        data.var = 50

    def content(self):
        with Window(title="blah", size=(640,480)):
            with VBox():
                Canvas(self.painter, data.var)
                with HBox():
                    Button("-").click(self.on_minus)
                    Label(f"{data.var}").layout(weight=1)
                    Button("+").click(self.on_plus)

    @staticmethod
    def painter(canvas, var):
        # Canvas coordinates are logical pixels. Use pixel_density when
        # preparing raster images for HiDPI displays.
        raster_scale = canvas.pixel_density
        canvas.drawText(var, var/2, f"blah {var}")
        canvas.drawLine(var, var, var*2, var*3, color=0xFFFF00)

    def on_minus(self, event):
        data.var -= 1

    def on_plus(self, event):
        data.var += 1

root = Example()
root.run()

canvas.pixel_density is the number of physical display pixels per logical canvas pixel. It is updated before each paint where the backend exposes the display scale, so moving a window between displays is handled automatically. Canvas coordinates remain in logical pixels; multiply raster image resolution by pixel_density when generating HiDPI content. Backends that cannot report a display scale use 1.0.

Canvas

Cookbook

python -m cookbook PySide6 (requires pygments for syntax highlight)

The code viewer uses a fixed dark Monokai palette. Backends without HTML support show plain text with the same dark background and light foreground.

Cookbook 1 Cookbook 2

python -m cookbook textual

Use make textual to run the Textual cookbook. For development mode, install textual-dev and use make textual_dev.

Cookbook textual

python -m cookbook flet Cookbook flet

python -m cookbook tkinter Cookbook tkinter

Hot Reload

pip install jurigged

Then PUI will take care of view update (code)

Backends

Tier-1

  • PySide6

Lower Priority

Documents

Used by

TODO

  • Dump with layout parameters and add test cases
  • Toga
  • [ISSUE] flet layout sizing (cookbook scroll example)
  • nested state trigger
    • set state in PUIView init
    • set state in setup() ?
  • Tabs(tabposition)
  • Lazy List
  • UI Flow
    • Navigation Stack
    • View Router
    • Model Window/Dialog
  • Layout
    • SwiftUI style overlay ??
  • Canvas
    • Arc
    • ...
  • State with Pydantic support?

Metadata

Release files for qpuiq 1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qpuiq 1.1
File Size Uploaded
qpuiq-1.1.tar.gz 87.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qpuiq 1.1
File Interpreter ABI Platform
qpuiq-1.1-py3-none-any.whl Python 3 none any Details

Total release size: 207.4 kB

Release files / qpuiq-1.1.tar.gz

Download URL qpuiq-1.1.tar.gz
Size 87.0 kB
Tags Source
SHA-256 checksum
How to use checksums
027a29ff25efee0386009ccff4b5ff2755e0284715531978d0d7e4d45b28ace2
BLAKE2b-256 checksum
How to use checksums
8ed3724f53210579fa888b99050b0f051168ab64a160a6cf17abbb3e3537f57a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.8

Release files / qpuiq-1.1-py3-none-any.whl

Download URL qpuiq-1.1-py3-none-any.whl
Size 120.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6990595f48f537be926c977a1ca7eaa062aecb6220b4b353b292ec0158c70e36
BLAKE2b-256 checksum
How to use checksums
44a6f53dc3083d0a2fb83485ec30a45636a0a42eeb44d8e8d2f47efd4bf3e1e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.8

Release history Release notifications | RSS feed

This release

1.1 This release

2 release files

1.0

2 release files

0.51

2 release files

0.50

2 release files

0.49

1 release file

0.48

2 release files

0.47

2 release files

0.46

2 release files

0.45

2 release files

0.44

2 release files

0.43

2 release files

0.42

2 release files

0.41

2 release files

0.40

2 release files

0.39

2 release files

0.38

2 release files

0.37

2 release files

0.36

2 release files

0.35

2 release files

0.34

2 release files

0.33

2 release files

0.32

2 release files

0.31

2 release files

0.30

2 release files

0.29

2 release files

0.28

2 release files

0.27

2 release files

0.26

2 release files

0.25

2 release files

0.24

2 release files

0.23

2 release files

0.22

2 release files

0.21

2 release files

0.20

2 release files

0.19

2 release files

0.18

2 release files

0.17

2 release files

0.16

2 release files

0.15

2 release files

0.14

2 release files

0.13

2 release files

0.12

2 release files

0.11

2 release files

0.10

2 release files

0.9.1

2 release files

0.9

2 release files

0.8

2 release files

0.7.1

2 release files

0.7

2 release files

0.6.1

2 release files

0.6

2 release files

0.5

2 release files

0.4

2 release files

0.3.8

2 release files

0.3.7

1 release file

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.15

2 release files

0.1.14

2 release files

0.1.13

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

1 release file

0.1.2

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page