Metadata-Version: 2.4
Name: tuikinter
Version: 0.1.0
Summary: Write terminal UIs with a tkinter/PyQt-style imperative API, powered by Textual.
Project-URL: Homepage, https://github.com/joshuamaojh/tuikinter
Project-URL: Repository, https://github.com/joshuamaojh/tuikinter
Project-URL: Issues, https://github.com/joshuamaojh/tuikinter/issues
Project-URL: Changelog, https://github.com/joshuamaojh/tuikinter/blob/main/CHANGELOG.md
Author: Joshua Mao
License-Expression: MIT
License-File: LICENSE
Keywords: cli,console,terminal,textual,tkinter,tui,ui,widgets
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.9
Requires-Dist: textual>=0.40
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/markdown

# tuikinter

**Write terminal UIs (TUIs) with a `tkinter` / `PyQt`-style imperative API — powered by [Textual](https://github.com/Textualize/textual).**

Textual is a fantastic, modern TUI framework, but its programming model is *declarative and async*: you `yield` widgets inside `App.compose()`, style them with TCSS, and manage state with reactives. If your mental model is `tkinter` — *create widgets one by one, lay them out with `pack` / `grid`, then call `mainloop()`* — `tuikinter` gives you exactly that, while Textual does the heavy lifting underneath.

```python
import tuikinter as tk

root = tk.Tk()
root.title("Hello")

name = tk.StringVar(root, value="World")
tk.Label(root, text="Name:").pack(side="left")
tk.Entry(root, textvariable=name).pack(side="left", fill="x", expand=True)

tk.Button(root, text="Greet", command=lambda: print(f"Hi {name.get()}")).pack()
tk.Button(root, text="Quit", command=root.destroy).pack()

root.mainloop()
```

## Why

If you already know `tkinter`, you already know `tuikinter`. The API mirrors it closely — same widget names, same `pack`/`grid`, same `StringVar`/`IntVar`/`BooleanVar` with `trace_add`, same `command=` callbacks, same `widget.config(...)` and `widget["text"] = ...`. You get a real, keyboard-navigable terminal app (and, because it's Textual underneath, it can even run in a browser via `textual serve`).

## Install

```bash
pip install tuikinter
```

Requires Python 3.9+. Textual is pulled in automatically.

## How it works

A façade in two phases:

1. **Build phase** (before `mainloop()`): each widget you create is a lightweight *spec proxy* that records which Textual widget to use, its options, callbacks, and layout intent, attaching itself to its parent to form a tree.
2. **Run phase** (`mainloop()`): a Textual `App` subclass is generated on the fly. Its `compose()` walks the tree and instantiates the real Textual widgets; `pack`/`grid` are translated into containers (`Vertical` / `Horizontal` / `Grid`) plus styles; callbacks are routed back to your plain functions through Textual's message system.

This is the same trick `tkinter` itself uses behind the scenes with Tcl commands.

## Widgets

| tuikinter | maps to (Textual) | notes |
|-----------|-------------------|-------|
| `Tk` | `App` | root window; call `.mainloop()` |
| `Frame` | `Vertical` / `Horizontal` / `Grid` | container; nest for complex layouts |
| `Label` | `Static` | `config(text=...)` to update |
| `Button` | `Button` | `command=fn`; `variant="success"/"error"/...` |
| `Entry` | `Input` | `textvariable=`, `get()`, `insert()`, `delete()`, `show="*"` |
| `Text` | `TextArea` | multi-line |
| `Checkbutton` | `Checkbox` | `variable=BooleanVar`, `command=fn` |
| `Listbox` | `OptionList` | `values=[...]`, `insert()`, `get_selected()` |
| `Progressbar` | `ProgressBar` | `maximum=`, `set()`, `step()` |

## Variables

```python
name = tk.StringVar(root, value="Joshua")
name.trace_add("write", lambda: print("changed to", name.get()))
name.set("World")          # pushes to any bound Entry + fires traces
```

`StringVar`, `IntVar`, and `BooleanVar` bind two-way to `Entry` / `Checkbutton`.

## Layout

`pack()` is the primary, robust layout manager:

```python
tk.Label(root, text="Top").pack(side="top", fill="x")
tk.Button(root, text="Left").pack(side="left", padx=1)
tk.Entry(root).pack(side="left", fill="x", expand=True)
```

`grid()` is supported on a best-effort basis (terminal grid semantics differ from pixel grids):

```python
grid = tk.Frame(root); grid.pack()
for i, t in enumerate("789456123"):
    tk.Button(grid, text=t).grid(row=i // 3, column=i % 3)
tk.Button(grid, text="0").grid(row=3, column=0, columnspan=3)
```

## Timers

```python
root.after(1000, lambda: print("once, after 1s"))
root.every(1000, lambda: clock.config(text=time.strftime("%H:%M:%S")))
```

## Limitations / honest notes

- **`pack` is the robust path; `grid` is best-effort.** For non-trivial layouts, nest `Frame`s (just like idiomatic `tkinter`) rather than mixing `side`s in one frame.
- Colors and sizes use **Textual semantics** (color names, `"100%"`, `"1fr"`), not pixels.
- An `Entry` bound to a `textvariable` fires its trace once on mount (Textual emits a `Changed` event when the `Input` first appears). Usually harmless — worth knowing if your trace has side effects.
- Dynamically creating + packing widgets *after* `mainloop()` has started is best-effort.

## Development

```bash
git clone https://github.com/joshuamaojh/tuikinter
cd tuikinter
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
```

Tests drive the app headlessly with Textual's `Pilot` harness.

## License

MIT © Joshua Mao

---

## 中文简述

`tuikinter` 让你用 **`tkinter` / `PyQt` 那样的命令式写法**来写终端 TUI，底层由 Textual 驱动。会 `tkinter` 就会它：控件名、`pack`/`grid`、`StringVar`/`IntVar`/`BooleanVar`（带 `trace_add`）、`command=` 回调、`config()` / `widget["text"]=...` 都一致。

```python
import tuikinter as tk
root = tk.Tk()
tk.Label(root, text="你好").pack()
tk.Button(root, text="退出", command=root.destroy).pack()
root.mainloop()
```

几个要点：`pack` 最稳，复杂布局请像 tkinter 那样嵌套 `Frame`；颜色/尺寸走 Textual 语义（色名、`"100%"`、`"1fr"`），不是像素。
