Skip to main content

"PUI" Python Declarative UI Framework

Project description

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

Installation

pip install QPUIQ

# Optional, for hot-reload
pip install jurigged

# Optional, for PySide6 backend
pip install PySide6

# Optional, for textual backend
pip install textual

# Optional, for wxPython backend
pip install wxPython

# Optional, for flet backend
pip install flet

Get Started

Hello World

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

@PUIApp
def Example():
    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):
        data.var -= 1

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

root = Example()
root.run()

State & Data Binding

View Component

# example/bleak_list.py

....

@PUI # View Component
def DeviceView(device, advertising_data):
    Label(f"{device.address} {device.name} {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

# 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.drawText(var, var/2, f"blah {var}")
        canvas.drawLine(var, var, var*2, var*3, color=0xFFFF00)

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

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

root = Example()
root.run()

Canvas

Cookbook

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

Cookbook 1 Cookbook 2

python -m cookbook textual 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?

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

qpuiq-0.36.tar.gz (49.6 kB view details)

Uploaded Source

Built Distribution

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

qpuiq-0.36-py3-none-any.whl (76.1 kB view details)

Uploaded Python 3

File details

Details for the file qpuiq-0.36.tar.gz.

File metadata

  • Download URL: qpuiq-0.36.tar.gz
  • Upload date:
  • Size: 49.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for qpuiq-0.36.tar.gz
Algorithm Hash digest
SHA256 15726b0b16de453dcb51b20193bda8562d6a195b3a0fe3fe097d9dd68b02be93
MD5 4f55913ef44d21a307dcb4f4c2e94718
BLAKE2b-256 2b1f82e20047b1f82e12ac4979abef74c219793773a44d642cb76baa69760b5e

See more details on using hashes here.

File details

Details for the file qpuiq-0.36-py3-none-any.whl.

File metadata

  • Download URL: qpuiq-0.36-py3-none-any.whl
  • Upload date:
  • Size: 76.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for qpuiq-0.36-py3-none-any.whl
Algorithm Hash digest
SHA256 88fe69412350f6be4f833fe377a5ade2462b66f539ac8ea2f1de5a986f79def0
MD5 1fa81625ac8ea958eba605c191fe0887
BLAKE2b-256 e0c3d4e08ce567e34a53ac0f5ef9f87ba9fb45c05e83d4eb43f0d6e678b78339

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