Thin Tcl/Tk binding (import tk)
- Type: feature (Track B — library;
lib/pcl/tk.pas). Enables the NilPy IDE demo ([[feature-demo-nilpy-ide]], Track E). - Status: done
- Opened: 2026-07-17.
Approach
The whole GUI is Tcl/Tk command strings through TkEval — the CPython-tkinter model
minus tkinter's weight. lib/pcl/tk.pas binds the system libtcl8.6/libtk8.6 sonames
with hand external clauses (no -dev headers, no C-import-registry change). Thin by
construction: Tk does all widget/layout/event work.
v1 — LANDED (commits f2dd8206, tk.pas StrPas fix)
TkInit/TkEval/TkMainLoopover the Tcl/Tk C API.DT_NEEDEDpulls libtcl/libtk; runs underxvfb.examples/tk/hello.npy:import tkfrom Nil-Python opens a window + runs the event loop. Proven end-to-end.- Event model settled — POLL, no C trampoline. A widget writes a Tcl variable
(
-command {set action run}); NilPy polls it withTkEval("update")+TkEval("set action")in its own loop. Proven working. This keeps the wrapper thin — noTcl_CreateObjCommand/callback plumbing, pureTkEval. - Found + filed along the way: [[bug-pascal-ansistring-cast-of-cdecl-call-result]] (silent, the StrPas fix), [[feature-nilpy-break-continue]] (NilPy loop-control gap).
Remaining
- tkinter-shaped convenience surface — thin helpers so common Python tk snippets run
(e.g.
Text,Button,pack,bind,mainloopmapping to oneTkEvaleach). Stays thin: name→command mapping, not a widget reimplementation. Where NilPy's v1 subset limits compat (kwargs,str+str, callbacks), that is a NilPy limit, not the wrapper. ttkthemed widgets — oneTkEval("ttk::style ...")away; closes most of the dated-look gap for free.- Gate smoke — an
xvfb-wrapped hello smoke wired intotest-nilpy/lib-test(testmgr already models thexvfbresource). Auto-close viaafterso it terminates.
Acceptance
- v1:
import tkopens a window from NilPy and runs the loop (DONE). - Surface: a representative tkinter-style snippet (label + button + poll loop) runs.
- A gated
xvfbsmoke;make test-nilpygreen.
Non-goals
- Not full tkinter parity — the common subset, thin.
- Not a C-callback event system — the poll model is the deliberate thin choice.
Log
-
2026-07-20 (Track B) — Item 3 (gate smoke) landed.
examples/tk/hello.npynow runs underxvfb-runas part ofmake lib-test, asserting its exact output. It SKIPs cleanly whenxvfb-runor the systemlibtk8.6is missing — an absent GUI stack on a build host is not a code defect, and reddening the Track B gate over one would just train people to ignore it.It cannot hang the suite: the
.npycloses itself viaafter 400 {destroy .}, so termination does not depend on anything the harness does.Wired into
lib-testrather thantest-nilpyon purpose —test-nilpybuilds with$(COMPILER)(a fresh compiler), and Track B builds with$(PXX_STABLE)and never rebuilds. Same coverage, correct lane.Items 1 (tkinter-shaped convenience surface) and 2 (
ttkthemed widgets) remain open; both are additive wrapper work on top ofTkEvalwith no blocker. -
2026-07-20 (Track B) — Items 1 and 2 landed; the ticket is complete.
Item 1, tkinter-shaped surface (
lib/pcl/tk.pas):TkTitle,TkLabel,TkButton,TkEntry,TkText,TkFrame,TkPack,TkGrid,TkBind,TkDestroy,TkAfter, plusTkGetText/TkSetText. Each is exactly oneTkEvalwith arguments interpolated — a name-to-command mapping, no widget objects, no state that can drift out of sync with Tk. Where a helper is missing,TkEvalstill does the job in one more line, and that escape hatch is what keeps the wrapper from growing into a widget layer.TkGetText/TkSetTextare the one place with logic: they readwinfo classand pickentry getvstext get 1.0 end-1cvscget -text, so callers do not branch on widget kind. That is convenience, not abstraction — it maps to the same three commands a human would type.Item 2, ttk:
TkThemeUse,TkThemeNames,TtkLabel,TtkButton,TtkEntry,TtkFrame. As predicted, one command prefix.Values are interpolated in Tcl
{braces}, so a caption containing spaces or brackets stays a single argument. Known limit: a caption containing an unbalanced brace still breaks. That is inherent to a thin string-passing wrapper, and it is documented at the declaration rather than papered over — starting to escape Tcl here would be the first step toward the widget layer this deliberately is not.examples/tk/widgets.npydemonstrates the surface and is gated headless alongsidehello.npyinmake lib-test, asserting the round-tripped text of an entry, a text widget and a label (all three read back throughTkGetText). -
2026-07-20 — resolved, commit HEAD.