"PUI" Python Declarative UI Framework
Project description
What is PUI
PUI is a declarative UI framework with two-way data binding
Installation
pip install QPUIQ
Get Started
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()
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()
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)
....
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
...
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()
Cookbook
python -m cookbook PySide6
(requires pygments for syntax highlight)
python -m cookbook textual
python -m cookbook flet
python -m cookbook tkinter
Hot-Reload with Reloadium
Backends
Tier-1
- PySide6
Lower Priority
- tkinter
- flet
- textual (Text Mode)
- no canvas
Declarative Components
Generic | PySide6 | flet | tkinter | textual |
---|---|---|---|---|
Application | QApplication | Page | Tk | App |
Window | QMainWindow | ✓(Single) | Toplevel | ✓(Single) |
HBox | QHBoxLayout | Row | Frame(grid) | Horizontal |
VBox | QVBoxLayout | Column | Frame(grid) | Vertical |
Spacer | QSpacerItem | ✓ | ✓ | ✓ |
Label | QLabel | Text | Label | Label/Button |
Button | QPushButton | ElevatedButton | Button | Button |
Checkbox | QCheckBox | Checkbox | Checkbutton | Checkbox |
RadioButton | QRadioButton | Radio | Radiobutton | RadioButton |
Canvas | ✓(QWidget) | Canvas | Canvas | - |
TextField | QLineEdit | TextField | Entry | Input |
ProgressBar | QProgressBar | ProgressBar | Progressbar | ProgressBar |
Scroll | QScrollArea | ✓ | ✓ | ScrollableContainer |
Text | QLabel | Text | Label | Text |
Html | QLabel | ⚠ Text | ⚠ Label | ⚠ Text |
MarkDown | QLabel | Markdown | ⚠ Label | Markdown |
Combobox | QComboBox | - | - | - |
ComboboxItem | ✓ | - | - | - |
Tabs | QTabWidget | Tabs | Notebook | Tabs |
Tab | ✓ | Tab | ✓ | ✓ |
MenuBar | QMenuBar | - | - | - |
Menu | QMenu | - | - | - |
MenuAction | QAction | - | - | - |
MdiArea | QMdiArea | - | - | - |
MdiSubWindow | QMdiSubWindow | - | - | - |
Splitter | QSplitter | - | - | - |
Modal | ✓(QWidget) | - | - | - |
(Wrapper) | QtWrapper |
- | - | - |
Imperative Dialogs
Generic | PySide6 | flet | tkinter | textual |
---|---|---|---|---|
OpenDirectory | QFileDialog.getExistingDirectory | - | - | - |
OpenFile | QFileDialog.getOpenFileName | - | - | - |
OpenFiles | QFileDialog.getOpenFileNames | - | - | - |
SaveFile | QFileDialog.getSaveFileName | - | - | - |
Information | QMessageBox | - | - | - |
Warning | QMessageBox | - | - | - |
Critical | QMessageBox | - | - | - |
Confirm | QMessageBox | - | - | - |
Prompt | QInputDialog | - | - | - |
Interfaces
- Button(text)
- .click(callback, *cb_args, **cb_kwargs)
- Label(text)
- .click(callback, *cb_args, **cb_kwargs)
- TextField(binding)
- ProgressBar(progress
0-1
) - Checkbox(label, model)
- RadioButton(label, value, model)
- Canvas
- .drawText(x, y, text)
- .drawLine(x1, y1, x2, y2, color=0xFF0000, width=2)
- .drawPolyline([x1, y2, ..., xn, yn], color=0xFF0000, width=2)
Modifiers
- .layout(width=320, height=240, weight=1, padding=, margin=)
- .style(color=0xFF0000, bgColor=0x0, fontSize=16, fontWeight="bold", fontFamily="Arial")
- .qt(HorizontalPolicy=, VerticalPolicy=, SizeConstraint=, StyleSheet={})
- .flet(k=v)
Hot Reload
Add these lines to your view file and run with reloadium
import reloadium
# reloadium: after_reload
def after_reload(actions):
PUIView.reload()
TODO
- [ISSUE] empty virtual node
- [ISSUE] textual layout sizing (cookbook scroll example)
- [ISSUE] flet layout sizing (cookbook scroll example)
- nested state trigger
- set state in PUIView init
- set state in setup() ?
- Tabs(
tabposition
) - Lazy List
- StateObject decorator
- UI Flow
- Navigation Stack
- View Router
- Model Window/Dialog
- Layout
- ZBox
- Grid
- Row
- Column
- SwiftUI style overlay ??
- Canvas
- Rect
- Arc
- Image
- ...
- Table
- Tree
- Dialog
- State with Pydantic support?
Project details
Release history Release notifications | RSS feed
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.2.5.tar.gz
(30.6 kB
view details)
Built Distribution
QPUIQ-0.2.5-py3-none-any.whl
(48.8 kB
view details)
File details
Details for the file QPUIQ-0.2.5.tar.gz
.
File metadata
- Download URL: QPUIQ-0.2.5.tar.gz
- Upload date:
- Size: 30.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1fa6aeadec99e9317c4984494d9ecd932941c7ce03aea18772922ef0f2bbd5cc |
|
MD5 | 730da36729fcf7e04943c343cd5f4b47 |
|
BLAKE2b-256 | 6070658e383cf58e23d19fd50f63fbfcfda06e170d71d83d71eaea3dbc42d49d |
File details
Details for the file QPUIQ-0.2.5-py3-none-any.whl
.
File metadata
- Download URL: QPUIQ-0.2.5-py3-none-any.whl
- Upload date:
- Size: 48.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 36aea2a5e03fc03c1482734b54de6473412543742608c9e0ce33c86689246b13 |
|
MD5 | c7187a352637c37d672a2df9a68e896c |
|
BLAKE2b-256 | cf9736b6ab88d7a1b77271e8595b96cb1bd973634cd544a95bf256a94ad07d0d |