tkinter: GUI programs in Nil Python

A Nil Python program gets a desktop window with import tkinter, as in CPython. PXX's tkinter module is lib/pcl/tkinter.pas. It binds the system's Tcl/Tk 8.6 directly: each widget call becomes one Tcl command, which is also how CPython's tkinter works underneath. Widgets are objects that take keyword options, command= and bind take Python functions, and the widget variables (StringVar, BooleanVar) work as in CPython.

It is a subset of tkinter, not all of it. Everything below was measured on Linux x86-64. A name the module does not have is a compile error that names it, never a call that silently does nothing (see Limits).

For a Pascal program, use the PCL instead.

What you need

An example

A window with an entry, a check box, a button and a label. The button's command is a Python function. Instead of waiting for a person, after() types a name, presses the button, ticks the box, prints what the widgets now hold, and closes the window, so the program also runs headless.

import tkinter as tk

root = tk.Tk()
root.title("tkinter hello")
name = tk.StringVar(value="")
remember = tk.BooleanVar(value=False)

entry = tk.Entry(root, textvariable=name, width=20)
entry.pack(padx=8, pady=4)
check = tk.Checkbutton(root, text="Remember me", variable=remember)
check.pack()
greeting = tk.Label(root, text="(nobody yet)")
greeting.pack(pady=4)


def greet():
    greeting.configure(text="Hello, " + name.get())


button = tk.Button(root, text="Greet", command=greet)
button.pack(pady=4)


def play():
    name.set("PXX")
    button.invoke()
    remember.set(True)
    print("entry:", entry.get())
    print("label:", greeting.cget("text"))
    print("checked:", remember.get())
    print("children:", len(root.winfo_children()))
    root.destroy()


root.after(300, play)
root.mainloop()
print("closed")
./pxx tk_hello.npy tk_hello
xvfb-run -a ./tk_hello
entry: PXX
label: Hello, PXX
checked: True
children: 4
closed

Built with pin v451 (compiler sha256 d9b7226769cc) through ./pxx and run under xvfb-run on 2026-09-29. CPython 3.14.4 prints the same five lines for the same file. Drop the after() line to keep the window open.

More programs are in examples/tk/, listed on the Examples page.

What works

The module has these names. Each widget takes the options listed in lib/pcl/tkinter.pas as keyword arguments, in any subset.

Area Names
Windows Tk (with className=, the window class), Toplevel (transient, grab_set, grab_release); on both, geometry, minsize, maxsize, resizable, iconphoto and wait_window (a modal dialog)
Widgets Frame, Label, Entry, Button, Checkbutton, Text, Canvas, Scrollbar, Menu, Separator, PanedWindow, Notebook, PhotoImage
Variables StringVar, BooleanVar (get, set, trace_add)
Layout pack (with anchor=), grid, grid_columnconfigure/columnconfigure, grid_rowconfigure/rowconfigure (with uniform=), pack_forget, grid_forget, pack_propagate, grid_propagate
Every widget configure/config (with cursor=), cget, bind, bind_all, unbind_all, event_generate (with x=, y=), after, after_idle, after_cancel, winfo_children, winfo_width, winfo_height, winfo_class, winfo_ismapped, focus_set, destroy, update, mainloop, quit, the clipboard calls
Text insert, delete, get, index, see, mark_set, tag_add, tag_configure, scrolling
Canvas create_text, create_line, create_polygon, create_rectangle, create_oval, create_image, create_window, each with tags=; coordinates as four numbers, a flat list or a list of (x, y) pairs; coords, itemconfigure/itemconfig, itemcget, find_all, find_withtag, tag_raise, tag_lower, bbox, delete (by id or tag), scrolling with yview_moveto
Dialogs from tkinter import messagebox: showinfo, showwarning, showerror, askyesno, askokcancel, askyesnocancel. from tkinter import filedialog: askopenfilename, asksaveasfilename, askdirectory
ttk from tkinter import ttk: ttk.Frame (with padding=), ttk.Label, ttk.Entry, ttk.Button, ttk.Checkbutton, ttk.Notebook, ttk.PanedWindow, ttk.Separator, ttk.Scrollbar. ttk.Style() with theme_use, theme_names, configure(name, background=, foreground=, font=, relief=, padding=, ...) and map(name, background=[(state, value), ...], ...), and style= on ttk.Frame and ttk.Label
Option names the short bg= and fg= wherever background= and foreground= are taken (Frame, Label, Canvas, Toplevel, configure/config)
Constants tk.END, tk.BOTH, tk.LEFT, tk.RIGHT, tk.TOP, tk.BOTTOM, tk.HORIZONTAL, tk.VERTICAL, tk.WORD, tk.CENTER, tk.NW, tk.NE, tk.SW, tk.SE, tk.DISABLED, tk.NORMAL (the same strings as CPython's); the error class tk.TclError

tkinter.font.Font(family=, size=, weight=, slant=) or Font(font=spec) has measure, metrics and actual, and a Font can be passed as any widget's font=. It is a shim, and from tkhtmlview import HTMLLabel, HTMLScrolledText renders HTML into a Tk text widget (see Nil Python: Shims).

A command or binding can be a plain function, a bound method or a lambda. A bound function receives an event object with tkinter's field names, such as event.width for <Configure> and event.widget, the widget the binding was made on (None for bind_all), and a yscrollcommand function receives Tk's two fractions as strings, as in CPython. examples/tk/callbacks.npy shows each of these, and it printed its .expected output with v451.

A class can extend a widget and pass options on with super().__init__(parent, height=h, **kwargs). When Tk cannot start, for example with no display, tk.Tk() raises tk.TclError with Tk's reason, so the usual except tk.TclError: fallback to a text interface works. examples/tk/style_and_short_options.npy and examples/tk/no_display_raises_tclerror.npy match CPython's output; both were checked on 2026-10-20 with the compiler built from master (not yet in a pin). So was examples/tk/canvas_items_dialog_and_events.npy: a live graph moved with coords(), a scrolling side bar of clickable tiles, Style.map, font measuring and a modal dialog closed through wait_window. And so was examples/tk/sidebar_cards_and_hover_rows.npy: card frames with a highlight border, wrapped labels, uniform grid columns, rows taken out with pack_forget, a window icon, and a group of widgets bound with lambdas in a loop to act as one hover button.

coords(item, x0, y0, ...) takes at most four numbers. For a longer polyline pass the list, coords(item, pts), which CPython takes too.

Limits

Measured with pin v451 on 2026-09-29, each with a one-line program: