Skip to main content

Tkintergalactic

Declarative Tcl/Tk UI library for Python.

  • Somewhat React-like (there is effectively a Tk VDOM).
  • Well typed.
  • Maps very closely to the underlying Tcl/Tk for ease of debugging.
  • Zero dependency.
  • On mac sometimes you have to start by wiggling the window.
  • Small enough to understand how it works.
  • In an incomplete state - much functionality missing.

Hello World

After pip install tkintergalactic, just run:

import tkintergalactic as tk

counter = 0

@tk.command()
def inc_counter() -> None:
    global counter
    counter += 1

tk.Window(
    app=lambda: tk.Frame(
        tk.Button(text="Hello World!", onbuttonrelease=inc_counter),
        tk.Text(content=f"Button clicked {counter} times"),
    ),
).run()

Hello world screencast

TODO list

from dataclasses import dataclass, field
import tkintergalactic as tk

@dataclass
class Task:
    description: str
    complete: bool = False

@dataclass
class State:
    tasks: list[Task] = field(default_factory=list)
    new_task_description: str = ""

state = State()

@tk.command()
def add_task() -> None:
    state.tasks.append(Task(state.new_task_description))
    state.new_task_description = ""

@tk.command()
def delete_task(i: int) -> None:
    state.tasks.pop(i)

@tk.command()
def toggle_class_complete(i: int) -> None:
    state.tasks[i].complete = not state.tasks[i].complete

@tk.command(with_event=True)
def set_new_task_description(e: tk.EventKeyRelease) -> None:
    state.new_task_description = e.value

tk.Window(
    title="TODO List",
    h=600,
    w=500,
    app=lambda: tk.Frame(
        tk.Frame(
            [
                tk.Frame(
                    tk.Entry(
                        value=task.description,
                        side="left",
                        font=tk.Font(styles=["overstrike"]) if task.complete else tk.Font(),
                        expand=True,
                    ),
                    tk.Button(
                        text="✗" if task.complete else "✔",
                        onbuttonrelease=toggle_class_complete.partial(i=i),
                    ),
                    tk.Button(text="Delete", onbuttonrelease=delete_task.partial(i=i), side="right"),
                    fill="x",
                    expand=True,
                )
                for i, task in enumerate(state.tasks)
            ],
            fill="x",
            expand=True,
        ),
        tk.Frame(
            tk.Entry(
                value=state.new_task_description,
                onkeyrelease=set_new_task_description,
                side="left",
                expand=True,
            ),
            tk.Button(
                text="New Task",
                onbuttonrelease=add_task,
            ),
            fill="x",
        ),
        tk.Text(
            content=f"Total number of tasks: {len(state.tasks)}\nComplete: {sum(t.complete for t in state.tasks)}",
        ),
    ),
).run()

TODO list screencast

Packer example

The packer is the main way of arranging Widgets.

import tkintergalactic as tk

tk.Window(
    title="Packer",
    w=200,
    h=300,
    app=lambda: tk.Frame(
        tk.Button(text="t", side="top", fill="x"),
        tk.Button(text="b", side="bottom", fill="x"),
        tk.Button(text="l", side="left"),
        tk.Button(text="r", side="right"),
        tk.Text(content="mid", expand=True, fill="both"),
        fill="both",
        expand=True,
    ),
).run()

Packer screenshot

Further work

Functionality

The majority of the functionality in the Tk Docs is still not implemented, most of it is just a case of padding out existing functionality in widgets.py, however there are some complicated text buffer bits that would take a lot more work.

Diffing

  • The diffing algorithm could be made more efficient - see eg. the referenced alogrithms in the mithril code.
  • Could allow passing optional ids to widgets to make diffing long lists more efficient.
  • More complicated state management a la React could be done. I'd have a preference for a simpler "this widget tree is the same as the other, don't diff" approach that the user can opt in to.
  • Potentially a lot of the diffing code coudld be offloaded to Rust.
  • Before doing any of the above, set up benchmarking.

Other

  • Sort out distinct naming for custom python commands, TCL commands and subcommands.

Development

uv pip install -e '.[dev]'
mypy .
pytest -vv

Metadata

Release files for tkintergalactic 0.0.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 tkintergalactic 0.0.1
File Size Uploaded Provenance
tkintergalactic-0.0.1.tar.gz 13.8 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 26.0 kB

Release files / tkintergalactic-0.0.1.tar.gz

Download URL tkintergalactic-0.0.1.tar.gz
Size 13.8 kB
Tags Source
SHA-256 checksum
How to use checksums
137eaab430a74e62e86c1f3d34251dbae19184bdf6eaf4cf96ed329356286eed
BLAKE2b-256 checksum
How to use checksums
374b47d913fe1266f8b6ce866f418dfd1ccc62b440689ccba7165fea7668debf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.5

Release files / tkintergalactic-0.0.1-py3-none-any.whl

Download URL tkintergalactic-0.0.1-py3-none-any.whl
Size 12.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
393d2bab3b5b31eac56d07838750125162c3a9940c978dd23a7d06e15809cb05
BLAKE2b-256 checksum
How to use checksums
a934bfb2fffa103c88feb082f1ed7db66096e4e5549bc2ec605e606b96a15696
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.5

Security for tkintergalactic 0.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files No provenance

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