A declarative approach to building Tkinter interfaces.
Project description
declarative-tkinter
A small declarative UI layer on top of Python's Tkinter — inspired by SwiftUI-style composition.
This project is Alpha. APIs may change.
Note: this document is written by AI and may contain inaccuracies, because this library origianlly developed as a part of my homework for the university course.
Features
- Compose UI as a tree (
VStack,HStack,Grid,TabView, ...) - Chainable styling and event handlers (
padding,fill,onClicked, ...) - App/window primitives (
App,Window) and a simple menu API (Menu)
Requirements
- Python
>= 3.14 - Tk
>= 9.0 - Dependency:
Pillow>=12.0.0
Note: importing declarative_tkinter performs runtime checks for Python/Tk versions and raises RuntimeError when the requirements are not met.
Installation
pip install declarative-tkinter
Editable install for development:
pip install -e .
Quick start
Create an App, create a Window, lay out a component tree into window.window, then call app.ready().
from declarative_tkinter import App, Window
from declarative_tkinter.component import VStack, Text, Button
def main() -> None:
app = App()
window = Window(app, width=360, height=180)
window.set_title("declarative-tkinter")
ui = VStack(
Text("Hello, Tkinter!"),
Button(text="Quit").onActive(lambda _btn: app.quit()),
).padding(12)
ui.layout(window.window)
app.ready()
if __name__ == "__main__":
main()
Components (excerpt)
Most components live in declarative_tkinter.component.
- Layout:
VStack,HStack,Grid,GridRow,VSplitView,HSplitView,TabView - Display:
Text,Image,ImagePNG,ImageSVG,Separator,Rectangle - Input:
TextField,SecureField,TextEditor,Stepper,CheckBox - Other:
Canvas
Common chainable helpers include:
padding(...),fill("x"|"y"|"both", expand=...)foregroundColor(...),backgroundColor(...)borderWidth(...),focusBorder(...)frame(width=..., height=...),font(...),alignment(...),justify(...)onClicked(...),onMouseEnter(...),onMouseLeave(...)
TextField example
TextField uses a tkinter.StringVar and supports onChange / onSubmit callbacks.
import tkinter
from declarative_tkinter import App, Window
from declarative_tkinter.component import VStack, Text, TextField, Button
def main() -> None:
app = App()
window = Window(app, width=420, height=220)
window.set_title("TextField demo")
name = tkinter.StringVar(value="")
message = Text("(empty)")
ui = VStack(
TextField(name, placeholder="Your name")
.onSubmit(lambda _tf, var: message.setText(f"Hello, {var.get()}!")),
message,
Button(text="Quit").onActive(lambda _btn: app.quit()),
).padding(12)
ui.layout(window.window)
app.ready()
if __name__ == "__main__":
main()
Real-world layout example (Grapher-like editor shell)
When your UI looks like an “app shell” (toolbar + sidebars + a main canvas + a status bar), plain Tkinter tends to become a long sequence of Frame(...), pack(...), and widget wiring.
With declarative-tkinter, you can describe the layout as a single tree and attach behavior where it belongs:
import tkinter
import tkinter.font
from declarative_tkinter import App, Window
from declarative_tkinter.component import VStack, HStack, Text, TextField, Canvas, Separator
def main() -> None:
app = App()
window = Window(app, width=1000, height=700)
window.set_title("Editor shell demo")
command = tkinter.StringVar(value="")
status = Text("Ready").name("status")
body = VStack(
# Top command bar
HStack(
Text("Command").padding(horizontal=8),
TextField(command, placeholder="Type a command and press Enter")
.padding(horizontal=8, vertical=4)
.focusBorder(backColor="#717171", focusColor="#6889f6", width=2)
.font(tkinter.font.Font(family=("Menlo"), size=10))
.fill("x", expand=True)
.onSubmit(lambda _tf, var: status.setText(f"Executed: {var.get().strip()}")),
).frame(height=30).fill("x").backgroundColor("#2C2C2C"),
# Main area
HStack(
# Left toolbar
VStack(
Text("Move").padding(6).pointer("pointinghand")
.onClicked(lambda _ui, _ev: status.setText("Mode: move")),
Text("Select").padding(6).pointer("pointinghand")
.onClicked(lambda _ui, _ev: status.setText("Mode: select")),
Separator().padding(vertical=10),
).frame(width=80).fill("y").backgroundColor("#3B3B3B"),
# Center canvas (scrollable)
VStack(
Canvas(1, 1, scrollable=True)
.fill("both", expand=True)
.backgroundColor("#ffffff")
.name("main_canvas"),
).fill("both", expand=True).backgroundColor("#202020"),
# Right sidebar (inspector)
VStack(
Text("Inspector").padding(8),
).frame(width=260).fill("y").backgroundColor("#232323"),
).fill("both", expand=True),
# Status bar
HStack(
status.padding(vertical=6, horizontal=12),
).frame(height=30).fill("x").backgroundColor("#0B3881"),
).backgroundColor("#1E1E1E").fill("both", expand=True)
body.layout(window.window)
# Optional: access widgets later via name lookup
canvas_item = body.getUIByName("main_canvas")
if isinstance(canvas_item, Canvas) and isinstance(canvas_item.widget, tkinter.Canvas):
canvas_item.widget.create_text(20, 20, text="Hello Canvas", anchor="nw")
app.ready()
if __name__ == "__main__":
main()
Menu
Attach a menu to a window using Window.set_menu().
from declarative_tkinter import (
App,
Window,
Menu,
MenuSubmenu,
MenuButton,
MenuSeparator,
Notification,
)
def main() -> None:
app = App()
window = Window(app, width=320, height=160)
window.set_title("Menu demo")
window.set_menu(
Menu([
MenuSubmenu(
"File",
items=[
MenuButton(
"Notify",
click=lambda app: app.notify(Notification("declarative-tkinter", "Hello from Menu")),
),
MenuSeparator(),
MenuButton("Quit", click=lambda app: app.quit(), accelerator="Cmd+Q"),
],
),
])
)
app.ready()
if __name__ == "__main__":
main()
Platform notes
App.notify(...)calls Tk'ssysnotify. It may fail depending on platform configuration and will returnFalse.- Some
WindowAPIs (e.g.is_dark_mode,set_bounce) are macOS-only.
License
MIT
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file declarative_tkinter-0.1.3.tar.gz.
File metadata
- Download URL: declarative_tkinter-0.1.3.tar.gz
- Upload date:
- Size: 33.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b4113228ac172ad4ddce71a38b9acd1ba4d0e1a2dca6769231cf9ccc90b18a4
|
|
| MD5 |
b7161e940f1e8e212dc8d166c2f9094a
|
|
| BLAKE2b-256 |
5089cc9df250b823ce040c30f9ae8ed66c06944042cfe648909d4020577bfc2b
|
File details
Details for the file declarative_tkinter-0.1.3-py3-none-any.whl.
File metadata
- Download URL: declarative_tkinter-0.1.3-py3-none-any.whl
- Upload date:
- Size: 30.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac40ef830bab698aec09e5793bd0ee1b8384aea6e91a678e26036a5df9c82108
|
|
| MD5 |
c298dfba114203df892702020924e2e2
|
|
| BLAKE2b-256 |
c73e90ce6147826bdaa8b27260b2810abe4b00c93b7728d9f7f9412c1758f78a
|